Показать на форме вызывающей программы панельку с кнопочками из DLL
Автор: Bank
У меня возникла ситуация, когда требуется показать на форме вызывающей
программы (Host Application) из DLL какой-либо контент. Например
панельку с кнопочками.
Т.е. у меня есть 2 проекта - в каждом по одной форме.
Показать форму из DLL - очень просто. Это много обсуждалось и есть
довольно большое колличество примеров (в том числе и в DelphiWorld).
Но мне требуется показать что-либо из DLL в самой программе (для
примера - Norton Internet Security - в окошке статуса можно видеть как
он подгружает интерфейс NAV).
В файлах помощи Delphi написано, что для переноса компоненты
достаточно присвоить значение Parent (родитель).
Если в роекте 2 формы, то перебросить панель можно так:
procedure TForm1.Button1Click(Sender: TObject);
begin
Form2.Panel2.Parent := Form1;
end;
и панелька как и надо переходит на первую форму.
для возврата:
procedure TForm2.Button1Click(Sender: TObject);
begin
Form2.Panel2.Parent := Form2;
end;
И все путем.
Теперь возвращаясь к DLL
Просто присвоением .Parent не обойтись, как выяснилось.
И так. В хост программе описываем процедуры:
const
DLLName = 'paneldll.dll';
function InitLib(App: Integer): Boolean; external DLLName;
function CreateFM(): Boolean; external DLLName;
function ShowPN(pn: HWND): Pointer; external DLLName;
function HidePN(): Boolean; external DLLName;
function ReleaseFM(): Boolean; external DLLName;
function UnloadLib(): Boolean; external DLLName;
Инициализация нам нужна чтобы наша DLL чувствовала себя не как
отдельная программа, а как часть основой программы. Дальше - создаем
форму. Теперь нам требуется перенести панельку из DLL на форму:
в хост программе описываем событие:
procedure TfmHostApp.btShowClick(Sender: TObject);
begin
TPanel(ShowPN(fmHostApp.Handle)).Parent := fmHostApp;
end;
А в самой DLL:
function ShowPN(pn: HWND): Pointer; register;
exports ShowPN;
begin
//ShowMessage(fmDLL.pnDLL.Parent.Name);
fmDLL.pnDLL.Parent := nil;
fmDLL.pnDLL.ParentWindow := pn;
//ShowMessage(fmDLL.pnDLL.Parent.Name);
Result := fmDLL.pnDLL;
end;
Вот оно самое!! Если выставлять просто .Parent - то панель как и надо
- исчезает с формы в нашей библиотеке, но в программе не показывается.
Закоментированные строки - это для теста. В случае без строк
fmDLL.pnDLL.Parent:=nil;
fmDLL.pnDLL.ParentWindow:=pn;
Эти ShowMessage показывают правильные .Parent (1: fmDLL, 2:
fmHostApp), но самой панельки не видно. В данном примере - все
работет.
Теперь разберемся что к чему. В хелпе Delphi сказано:
[Parent Property (TControl) (VCL Reference)]
"Some controls (such as ActiveX controls) are contained in native
windows rather than in a parent VCL control. For these controls, the
value of Parent is nil (Delphi) or NULL (C++) and the ParentWindow
property specifies the window."
Что можно перевести как:
"Некоторые controls (типа ActiveX) содержатся в родных окнах,
а не в родительском VCL control. Для этих компонент, значение Parent
является nil, и для них работает свойство ParentWindow, которое и
определяет окно."
Можно предположить, что это к нашей ситуации никак не отностится.
Неизвестно почему (не охота изучать сотни строк свойств
TControl/TWinControl) что бы разобраться почему в случае с DLL
требуется сначала обнулить свойство Parent и выставить ParentWindow до
того как будет установлен новый Parent.
Теперь - для возврата панельки на родную форму в программе описываем
вызов процедуры:
procedure TfmHostApp.btHideClick(Sender: TObject);
begin
HidePN();
end;
а в DLL делаем то же самое, что и раньше:
function HidePN(): Boolean;
begin
fmDLL.pnDLL.Parent := nil;
fmDLL.pnDLL.ParentWindow := fmDLL.Handle;
fmDLL.pnDLL.Parent := fmDLL;
Result := True;
end;
Вот и все. Надеюсь, кому нибудь это поможет и съэкономит время, так
как у меня ушел целый день, пока я разобрался как заставить это все
работать по "моим" правилам.
Скачать весь проект
|