Шаг 311.
Создание внутрипроцессных серверов автоматизации. Создание и использование DLL. Модальные формы в DLL (динамическое подключение)

    На этом шаге мы рассмотрим особенности использования модальных форм при динамическом подключении DLL.

    Если загружать DLL динамически, возникает ряд проблем. Первая из них заключается в том, что, как правило, загрузка и выгрузка библиотеки, из которой вызывается диалоговое окно, осуществляется в пределах одной функции:

HLib := LoadLibrary('FirstLib.dll'): 
if HLib <> 0 then begin
  ExecDialog:=GetProcAddress(HLib, 'ExecDialog'); 
  if Assigned(ExecDialog) then begin
    if ExecDialog(Application.Handle, P) then {...}; 
  end 
  else 
    ShowMessage('Метод "ExecDialog" не найден'); 
    FreeLibrary(HLib); 
  end 
  else 
    ShowMessage('Не найдена библиотека "FirstLib.dll"');

    При этом библиотека выгружается немедленно после закрытия диалогового окна, а метод Release, который рекомендуется использовать для вызова деструктора формы, посылает сообщения CM_RELEASE форме вызовом функции PostMessage. Эта функция ставит сообщение в конец очереди, приложение продолжает выполнять код - и выгружает DLL! Только после выполнения кода начинает обрабатываться очередь сообщений, в конце концов из очереди извлекается сообщение CM_RELEASE и делается попытка выполнить деструктор формы - а функции-то уже выгружены! Если система имеет значительный резерв ресурсов, то велика вероятность, что место в памяти, где хранился код, сохранит свое содержимое и форма будет разрушена успешно. Но при малых ресурсах в освободившемся месте в оперативной памяти немедленно окажутся какие-либо данные из виртуальной дисковой памяти, и попытка выполнить деструктор закончится исключением. Поэтому при динамической загрузке обязательно следует использовать метод Free вместо Release. Кроме того, перед выгрузкой DLL рекомендуется вызвать метод Application.ProcessMessages.

    Другая проблема заключается в использовании одного и того же объекта TApplication и в приложении, и в DLL при выполнении приведенного выше оператора:

  Application.Handle := AppHandle;

    Если выполнить этот оператор перед вызовом метода ShowModal в DLL, то после выгрузки DLL главная форма приложений сворачивается и ее необходимо восстанавливать вновь. Один из способов решения этой проблемы - вызвать функцию ShowWindow(Handle, SW_RESTORE) сразу же после выполнения команды FreeLibrary в приложении. Однако при этом главная форма приложения будет мерцать. Другой способ - оставить разные объекты TApplication в приложении и DLL - для этого указанный выше оператор в DLL выполнять не надо. Однако при этом на панели задач появляются две кнопки. Корректное решение проблемы заключается в присвоении нулевого значения свойству Application.Handle в DLL перед выходом из функции. Ниже приведен рекомендуемый код для библиотеки, выводящей диалоговое окно при динамической загрузке:

function ExecDialog(AppHandle:THandle;
  var PictName:PChar):boolean; stdcall; export;
var
  FDialog:TForm1;
begin
  FDialog := nil;
  PictName := nil;
  Result := False;
  Application.Handle := AppHandle;
  try
    FDialog := TForm1.Create(Application);
    if FDialog.ShowModal = mrOK then begin
      FillMemory(@C[0], SizeOf(C), 0);
      if length(FDialog.OpenPictureDialog1.FileName) > 0 then
          StrPCopy(C,FDialog.OpenPictureDialog1.FileName);
      PictName := @C[0];
      Result := True;
    end;
    FDialog.Free;
    FDialog := nil;
  except
    On E:Exception do begin
      ShowMessage(E.Message);
      if Assigned(FDialog) then FDialog.Free;
    end;
  end;
  Application.ProcessMessages;
  Application.Handle := 0;
end;

    Ниже приведен код приложения, динамически вызывающего данную библиотеку:

type
  TExecDialog = function(AppHandle: THandle; 
      var PictName: PChar): Boolean; stdcall;

procedure TForm1.Button1Click(Sender: TObject);
var
  P: PChar;
  S: String;
  ExecDialog: TExecDialog;
  HLib: THandle;
begin
  HLib := 0;
  try
    HLib := LoadLibrary('FirstLib.dll');
    if HLib <> 0 then
    begin
      ExecDialog := GetProcAddress(HLib, 'ExecDialog');
      if Assigned(ExecDialog) then
      begin
        if ExecDialog(Application.Handle, P) then
        begin
          S := P;
          Caption := S;
        end;
      end
      else
        ShowMessage('Метод "ExecDialog" не найден');
      end
    else
      ShowMessage('Не найдена библиотека "FirstLib.dll"');
  finally
     Application.ProcessMessages;
     if HLib <> 0 then FreeLibrary(HLib);
  end;
end;
Текст этого приложения вместе с DLL можно взять здесь (444,2 Кб).

    Отображение модальных форм редко осуществляется в COM DLL - обычно в COM DLL помещают расчетные методы. Но можно показывать и модальные формы - при этом не возникают проблемы со вторым экземпляром объекта TApplication. В COM DLL также следует динамически вызывать конструктор модальной формы.

    На следующем шаге мы рассмотрим немодальные формы в DLL.




Предыдущий шаг Содержание Следующий шаг