我写了个Dll,里面导出Frame,Frame上有ListView,编译运行都没有问题,但鼠标一点ListView,就报错Control 'ListView' has no parent window这种问题怎么解决啊,难道,Dll中的Frame里不能用ListView?把ListView换成Memo就不会有这种错
Windows.SetParent(ChildHandle,ParentHandle); 将ChildHandle对应的控件的父窗口设置为ParentHandle对应的控件P.S.楼主如果用DLL仅仅是为了模块化程序的话,推荐使用package(包)分割,用Dll的话,会遇到很多郁闷的问题一下是我收集的一些问题 及其解决办法,供楼主参考Cannot assign a TFont to a TFont 主要是由于在创建Dll的Form时,将外部调用程序的Application、Screen和MainForm都传给了Dll并且将Dll中的这几个对象都替换掉了的缘故,解决的办法是只需将外部调用程序的Application.Handle及MainForm传给Dll并替换掉Dll中的这两项值就可以了,我已测试通过,可以试一试。如: var p: PLongInt;
{DllApp: TApplication Dll中的Application OutApp: TApplication 外部调用程序的Application } p:=@(DllApp.MainForm); DllApp.Handle := OutApp.Handle; p^:=LongInt(OutApp.MainForm); The DLL will not use the same classes as the main program even when compiled from the same source. Objects will look the same, but their classes will not compare equal. That's why assigning a TFont value from the DLL to a TFont property in the main program (or the other way around) doesn't work: the Assign procedure is looking for the main program's TFont class, and never recognises the DLL's TFont object. 最近在公司写一个外壳程序,调用DLL插件把FORM嵌入到EXE中的一个PANEL中,其中遇到了不少的问题,大部分已经解决,还有几个至今没有找到解决方法,有待研究,也希望知道解决方法的富翁共享一下研究成果. 以下列出的问题及解决方法仅针对我写的程序(DLL插件把FORM嵌入到EXE中的一个PANEL中),和自己的解决方法. 从遇到的问题看出,DELPHI封装了太多的东西,有时候直接使用API会有意想不到的效果. 经验:DLL与EXE之间的通讯应该全部使用消息. 第一个问题:Tab键和Enter键在DLL的FORM中无效 原始程序: //frmDll为DLL中的FORM,frmEXE为EXE主窗体,下同 //下面的代码为什么直接引用Exe中的Form又引用Dll中的Form?只是为了方便阅读,实际只是传递一个句柄,下同 //panWorkSpace为Exe中的一个TPanel,DLL中的窗体要嵌入其中 frmDll.WindowState := wsMaximized; frmDll.BorderStyle := bsNone; windows.SetParent(frmDll.Handle,frmExe.panWorkSpace.Handle);发现Tab及Enter键在嵌入的FORM中无效,去掉 frmDll.BorderStyle := bsNone; 后正常,但我不需要标题,就用API解决 frmDll.WindowState := wsMaximized; SetWindowLong(frmDll.Handle,GWL_STYLE,GetWindowLong(frmDll.Handle,GWL_STYLE) and not (WS_CAPTION or WS_THICKFRAME)); windows.SetParent(frmDll.Handle,frmExe.panWorkSpace.Handle); 其中WS_CAPTION和WS_THICKFRAME分别表示标题栏和边框,问题解决. 第二个问题:DLL窗体的ResizeEXE主窗体改变尺寸时,窗体中的Panel也会跟着变(Panel.Align设为了alClient),但其中嵌入的DLL窗体不会跟着变,解决方法: //exe窗口接收消息并改变子窗体大小 //FChildWindowList为TList,子窗体的结构信息列表 Type //子窗体一些信息的结构体 PFormInfo = ^TFormInfo; TFormInfo = record Handle : HWND; Parent : HWND; Style : HWND; end; TfrmExe = class(TForm) private procedure WMSize(var Message:TWMSize);message WM_Size; end;procedure TfrmExe.WMSize(var Message: TWMSize); //ReSize消息 var i : Integer; rc : TRect; begin inherited; if GetWindowRect(panWorkSpace.Handle,rc) then if Assigned(FChildWindowList) then for i := 0 to FChildWindowList.Count - 1 do SetWindowPos(PFormInfo(FChildWindowList[i]).Handle, 0, 0, 0, rc.Right - rc.Left, rc.Bottom - rc.Top, SWP_NOACTIVATE); end; 第三个问题:焦点在DLL中的窗体时,切换到其它应用程序,再点击任务栏上Application对象的按钮,不能切换过来焦点在DLL中的窗体时,切换到其它应用程序,再点击任务栏上Application对象的按钮,不能切换过来,EXE主窗体不最小化,切换到其它程序,直接点嵌入的DLL窗体,DLL窗体获得焦点,发现Application对象在任务栏上的按钮是被按下去了,但是EXE窗体并没有被提到最前,还有,DLL窗体得到焦点时,EXE窗体的标题栏变为灰色,这些都是不符合使用习惯的,虽然不影响使用,但我觉得还是要解决.1.DLL窗体得到焦点时,EXE窗体的标题栏变为灰色的解决方法. DLL窗体 TfrmDll=class(TForm) private procedure WMActivate(var Message : TMessage);message WM_ACTIVATE; end;procedure TfrmDll.WMActivate(var Message: TMessage); begin inherited; SendMessage(frmEXE.Handle, WM_NCACTIVATE, Integer(True), 0); end;2.焦点的问题解决方法 把下面这个单元加入工程 //============================================================================== // Unit Name: AppHandler // Author : ysai // Date : 2003-06-05 // Purpose : 处理焦点问题 // History : //==============================================================================unit AppHandler;interfaceuses Windows, Messages, SysUtils,Forms;implementationvar OldWProc : TFNWndProc;function NewWndProc( Handle : HWND; Msg : Integer; wParam : Longint; lParam : Longint ):Longint; stdcall; begin Result := 0; case Msg of WM_ACTIVATEAPP : //嵌入到主窗口的DLL中的窗口得到焦点不会把程序提前 begin case wParam of 0 : //应用程序失去焦点 begin if Assigned(Application.MainForm) and (GetWindowLong(Application.Handle, GWL_EXSTYLE) and WS_EX_TOOLWINDOW = 0) then SendMessage( Application.MainForm.Handle, WM_NCACTIVATE, Integer(False), 0);//失去焦点把标题栏变灰 end; 1 : //应用程序得到焦点 begin if Assigned(Application.MainForm) and (GetWindowLong(Application.Handle, GWL_EXSTYLE) and WS_EX_TOOLWINDOW = 0) then SendMessage( Application.MainForm.Handle, WM_ACTIVATE, WA_ACTIVE, 1);//注意,这里设为1,后面会用到 end; //case wParam end; Result := CallWindowProc(OldWProc, Handle, Msg, wParam, lParam); end; //msg : WM_ACTIVATEAPP else Result := CallWindowProc(OldWProc, Handle, Msg, wParam, lParam); end; //case msg end;initialization //取代应用程序的消息处理 OldWProc := TFNWndProc(SetWindowLong(Application.Handle, GWL_WNDPROC, Longint(@NewWndProc)));finalization //还原消息处理过程 if OldWProc <> nil then SetWindowLong(Application.Handle, GWL_WNDPROC, LongInt(OldWProc));end. //单元结束//EXE程序主窗口 TfrmEXE = class(TForm) private procedure WMActivate(var Message : TMessage);message WM_ACTIVATE; end;procedure TfrmExe.WMActivate(var Message: TMessage); //激话消息,Message.lParam=1时是OAAppHandler单元发来的,激活子窗口 var hWindow : HWND; begin inherited; if Message.lParam = 1 then //如果是1就是AppHander发出的消息,将焦点设到活动子窗体 begin hWindow := GetActiveChildWindowHandle;//这个函数得到活动子窗体 //如果有子窗口而且不存在模态显示的窗体则把焦点移到子窗体上 if (hWindow > 0) and IsWindowEnabled(Application.Handle) then windows.SetForegroundWindow(hWindow); end; end; 第四个问题:SpeedButton在DLL中鼠标离开不会恢复平面(ShowModal时不会出现)(未解决)SpeedButton.Flat设为真时,在DLL中鼠标离开不会恢复平面状态,而ShowModal时不会出现,不知道原因,应该是消息处理得不好,不知道有没有人解决过 又一个焦点问题:焦点在DLL窗体时,按Alt+Tab,对话框里出来的程序中竟然没有EXE程序!焦点在EXE窗体上时没问题,焦点在DLL窗体上时,用Alt+Tab不会出现EXE应用程序的图标,切换到其它任务后,也不能用Alt+Tab切换回来!这是个比较大的BUG,还未找到原因用spy++看了一下,按下Alt+Tab键,窗体收到了一个WM_CANCELMODE消息,我想,既然焦点在exe窗体上时可以看到图标,而在dll上看不到,那么我在收到这个消息时把焦点给设到exe上不就可以了? 事实证明这点是可行的,代码如下: TDllForm = class(TForm) private procedure WMCancelMode(var Message : TMessage);message WM_CANCELMODE; end;procedure TDllForm.WMCancelMode(var Message: TMessage); //处理Alt+Tab键弹出的对话框中没有应用程序图标问题 begin SetForegroundWindow(exeForm.Handle); //把exe窗体设为当前有焦点的窗体 end; 现在不论焦点在exe的窗体上还是dll的窗体上,按Alt+Tab出现的对话框中都有应用程序的图标,但不同的是,焦点在exe的窗体上时按Alt+Tab,默认激活的是下一个应用程序,而焦点在dll窗体上时按Alt+Tab,默认激活的是第一个,也就是应用程序本身,实际激活的是exe窗体. 虽然还是不怎么习惯,但总算把它给弄出来了,以后有好的解决方法再贴上来. Hint的问题(未解决)焦点在Dll中的窗体时,鼠标移动到控件上不会显示控件的Hint,而且Application.OnHint事件也不会发生,但是焦点在Exe窗体上时,把鼠标放在Dll窗体中的控件上却能显示Hint.原因还未找到:( ALT+TAB解决了,但是那是键盘,鼠标操作还是有问题焦点在DLL中时,用鼠标点其它应用程序,失去焦点了,再按ALT+TAB,那个该死的应用程序图标又没了,焦虑中....
将ChildHandle对应的控件的父窗口设置为ParentHandle对应的控件P.S.楼主如果用DLL仅仅是为了模块化程序的话,推荐使用package(包)分割,用Dll的话,会遇到很多郁闷的问题一下是我收集的一些问题 及其解决办法,供楼主参考Cannot assign a TFont to a TFont
主要是由于在创建Dll的Form时,将外部调用程序的Application、Screen和MainForm都传给了Dll并且将Dll中的这几个对象都替换掉了的缘故,解决的办法是只需将外部调用程序的Application.Handle及MainForm传给Dll并替换掉Dll中的这两项值就可以了,我已测试通过,可以试一试。如:
var
p: PLongInt;
{DllApp: TApplication Dll中的Application
OutApp: TApplication 外部调用程序的Application
}
p:=@(DllApp.MainForm);
DllApp.Handle := OutApp.Handle;
p^:=LongInt(OutApp.MainForm); The DLL will not use the same classes as the main program even when compiled from the same source. Objects will look the same, but their classes will not compare equal. That's why assigning a TFont value from the DLL to a TFont property in the main program (or the other way around) doesn't work: the Assign procedure is looking for the main program's TFont class, and never recognises the DLL's TFont object. 最近在公司写一个外壳程序,调用DLL插件把FORM嵌入到EXE中的一个PANEL中,其中遇到了不少的问题,大部分已经解决,还有几个至今没有找到解决方法,有待研究,也希望知道解决方法的富翁共享一下研究成果. 以下列出的问题及解决方法仅针对我写的程序(DLL插件把FORM嵌入到EXE中的一个PANEL中),和自己的解决方法. 从遇到的问题看出,DELPHI封装了太多的东西,有时候直接使用API会有意想不到的效果.
经验:DLL与EXE之间的通讯应该全部使用消息.
第一个问题:Tab键和Enter键在DLL的FORM中无效 原始程序:
//frmDll为DLL中的FORM,frmEXE为EXE主窗体,下同
//下面的代码为什么直接引用Exe中的Form又引用Dll中的Form?只是为了方便阅读,实际只是传递一个句柄,下同
//panWorkSpace为Exe中的一个TPanel,DLL中的窗体要嵌入其中
frmDll.WindowState := wsMaximized;
frmDll.BorderStyle := bsNone;
windows.SetParent(frmDll.Handle,frmExe.panWorkSpace.Handle);发现Tab及Enter键在嵌入的FORM中无效,去掉
frmDll.BorderStyle := bsNone;
后正常,但我不需要标题,就用API解决
frmDll.WindowState := wsMaximized;
SetWindowLong(frmDll.Handle,GWL_STYLE,GetWindowLong(frmDll.Handle,GWL_STYLE) and not (WS_CAPTION or WS_THICKFRAME));
windows.SetParent(frmDll.Handle,frmExe.panWorkSpace.Handle);
其中WS_CAPTION和WS_THICKFRAME分别表示标题栏和边框,问题解决. 第二个问题:DLL窗体的ResizeEXE主窗体改变尺寸时,窗体中的Panel也会跟着变(Panel.Align设为了alClient),但其中嵌入的DLL窗体不会跟着变,解决方法:
//exe窗口接收消息并改变子窗体大小
//FChildWindowList为TList,子窗体的结构信息列表
Type
//子窗体一些信息的结构体
PFormInfo = ^TFormInfo;
TFormInfo = record
Handle : HWND;
Parent : HWND;
Style : HWND;
end; TfrmExe = class(TForm)
private
procedure WMSize(var Message:TWMSize);message WM_Size;
end;procedure TfrmExe.WMSize(var Message: TWMSize);
//ReSize消息
var
i : Integer;
rc : TRect;
begin
inherited;
if GetWindowRect(panWorkSpace.Handle,rc) then
if Assigned(FChildWindowList) then
for i := 0 to FChildWindowList.Count - 1 do
SetWindowPos(PFormInfo(FChildWindowList[i]).Handle, 0,
0, 0, rc.Right - rc.Left, rc.Bottom - rc.Top,
SWP_NOACTIVATE);
end; 第三个问题:焦点在DLL中的窗体时,切换到其它应用程序,再点击任务栏上Application对象的按钮,不能切换过来焦点在DLL中的窗体时,切换到其它应用程序,再点击任务栏上Application对象的按钮,不能切换过来,EXE主窗体不最小化,切换到其它程序,直接点嵌入的DLL窗体,DLL窗体获得焦点,发现Application对象在任务栏上的按钮是被按下去了,但是EXE窗体并没有被提到最前,还有,DLL窗体得到焦点时,EXE窗体的标题栏变为灰色,这些都是不符合使用习惯的,虽然不影响使用,但我觉得还是要解决.1.DLL窗体得到焦点时,EXE窗体的标题栏变为灰色的解决方法.
DLL窗体
TfrmDll=class(TForm)
private
procedure WMActivate(var Message : TMessage);message WM_ACTIVATE;
end;procedure TfrmDll.WMActivate(var Message: TMessage);
begin
inherited;
SendMessage(frmEXE.Handle, WM_NCACTIVATE, Integer(True), 0);
end;2.焦点的问题解决方法
把下面这个单元加入工程
//==============================================================================
// Unit Name: AppHandler
// Author : ysai
// Date : 2003-06-05
// Purpose : 处理焦点问题
// History :
//==============================================================================unit AppHandler;interfaceuses
Windows, Messages, SysUtils,Forms;implementationvar
OldWProc : TFNWndProc;function NewWndProc(
Handle : HWND;
Msg : Integer;
wParam : Longint;
lParam : Longint
):Longint; stdcall;
begin
Result := 0;
case Msg of
WM_ACTIVATEAPP : //嵌入到主窗口的DLL中的窗口得到焦点不会把程序提前
begin
case wParam of
0 : //应用程序失去焦点
begin
if Assigned(Application.MainForm)
and (GetWindowLong(Application.Handle, GWL_EXSTYLE)
and WS_EX_TOOLWINDOW = 0) then
SendMessage(
Application.MainForm.Handle,
WM_NCACTIVATE,
Integer(False),
0);//失去焦点把标题栏变灰
end;
1 : //应用程序得到焦点
begin
if Assigned(Application.MainForm)
and (GetWindowLong(Application.Handle, GWL_EXSTYLE)
and WS_EX_TOOLWINDOW = 0) then
SendMessage(
Application.MainForm.Handle,
WM_ACTIVATE,
WA_ACTIVE,
1);//注意,这里设为1,后面会用到
end; //case wParam
end;
Result := CallWindowProc(OldWProc, Handle, Msg, wParam, lParam);
end; //msg : WM_ACTIVATEAPP
else
Result := CallWindowProc(OldWProc, Handle, Msg, wParam, lParam);
end; //case msg
end;initialization
//取代应用程序的消息处理
OldWProc := TFNWndProc(SetWindowLong(Application.Handle, GWL_WNDPROC,
Longint(@NewWndProc)));finalization
//还原消息处理过程
if OldWProc <> nil then
SetWindowLong(Application.Handle, GWL_WNDPROC, LongInt(OldWProc));end.
//单元结束//EXE程序主窗口
TfrmEXE = class(TForm)
private
procedure WMActivate(var Message : TMessage);message WM_ACTIVATE;
end;procedure TfrmExe.WMActivate(var Message: TMessage);
//激话消息,Message.lParam=1时是OAAppHandler单元发来的,激活子窗口
var
hWindow : HWND;
begin
inherited;
if Message.lParam = 1 then //如果是1就是AppHander发出的消息,将焦点设到活动子窗体
begin
hWindow := GetActiveChildWindowHandle;//这个函数得到活动子窗体
//如果有子窗口而且不存在模态显示的窗体则把焦点移到子窗体上
if (hWindow > 0) and IsWindowEnabled(Application.Handle) then
windows.SetForegroundWindow(hWindow);
end;
end; 第四个问题:SpeedButton在DLL中鼠标离开不会恢复平面(ShowModal时不会出现)(未解决)SpeedButton.Flat设为真时,在DLL中鼠标离开不会恢复平面状态,而ShowModal时不会出现,不知道原因,应该是消息处理得不好,不知道有没有人解决过 又一个焦点问题:焦点在DLL窗体时,按Alt+Tab,对话框里出来的程序中竟然没有EXE程序!焦点在EXE窗体上时没问题,焦点在DLL窗体上时,用Alt+Tab不会出现EXE应用程序的图标,切换到其它任务后,也不能用Alt+Tab切换回来!这是个比较大的BUG,还未找到原因用spy++看了一下,按下Alt+Tab键,窗体收到了一个WM_CANCELMODE消息,我想,既然焦点在exe窗体上时可以看到图标,而在dll上看不到,那么我在收到这个消息时把焦点给设到exe上不就可以了?
事实证明这点是可行的,代码如下:
TDllForm = class(TForm)
private
procedure WMCancelMode(var Message : TMessage);message WM_CANCELMODE;
end;procedure TDllForm.WMCancelMode(var Message: TMessage);
//处理Alt+Tab键弹出的对话框中没有应用程序图标问题
begin
SetForegroundWindow(exeForm.Handle); //把exe窗体设为当前有焦点的窗体
end; 现在不论焦点在exe的窗体上还是dll的窗体上,按Alt+Tab出现的对话框中都有应用程序的图标,但不同的是,焦点在exe的窗体上时按Alt+Tab,默认激活的是下一个应用程序,而焦点在dll窗体上时按Alt+Tab,默认激活的是第一个,也就是应用程序本身,实际激活的是exe窗体.
虽然还是不怎么习惯,但总算把它给弄出来了,以后有好的解决方法再贴上来. Hint的问题(未解决)焦点在Dll中的窗体时,鼠标移动到控件上不会显示控件的Hint,而且Application.OnHint事件也不会发生,但是焦点在Exe窗体上时,把鼠标放在Dll窗体中的控件上却能显示Hint.原因还未找到:( ALT+TAB解决了,但是那是键盘,鼠标操作还是有问题焦点在DLL中时,用鼠标点其它应用程序,失去焦点了,再按ALT+TAB,那个该死的应用程序图标又没了,焦虑中....
不用它看看吧
如果dll仅仅是供Delphi写的程序使用的话,建议用package代替dll