我正在尝试在Delphi中编写一个dll库,该函数创建一个TFrame后代的实例并返回它。但是当我在一个应用程序中导入这个函数时,每次调用它时我都会得到一个例外,例如“'xxx'控件没有父窗口”。我不是百分百肯定,但是当访问任何GUI控件时,该类的构造函数中出现异常。
你能告诉我这种行为的原因是什么吗?我应该只使用TForm后代还是有更好的解决方案?
谢谢!
答案 0 :(得分:8)
关于错误
从TWinControl.CreateWnd
方法从Controls.pas单元引发该错误消息。本质上,该代码用于为您的TWinControl后代创建Window句柄(TFrame,TButton,TEdit ......如果它可以有键盘焦点它是一个TWinControl后代),它实际上是一个非常明智的错误消息:你不能有一个没有WindowParent的窗口,因为我们在这里谈论VCL,尝试从TWinControl.Parent获取父窗口句柄是很有意义的;那并没有分配。
这不是为什么弹出错误消息。您将看到该错误消息,因为您用于设置框架的某些代码需要一个Window句柄来执行某些操作。它可以是任何东西,比如设置某个组件的Caption(内部需要一个窗口句柄来进行某些计算)。当发生这种情况时,我个人真的很讨厌它。当我从代码创建GUI时,我尝试尽可能地延迟Parent的赋值,试图延迟窗口的创建,所以我被这次多次咬了。
特定于您的DLL使用,可能的修复
我打算戴上心理读者的帽子。因为你需要从你的DLL中返回一个FRAME,并且你不能返回实际的Frame,因为那是一个特定于Delphi的对象而且你不允许在DLL边界上返回特定于Delphi的对象,我猜你正在返回一个窗口句柄,就像所有漂亮的API一样,使用这样的函数定义:
function GiveMeTheNiceFrame:HWND;
问题是,该例程需要通过调用TWinControl.CreateWnd
来创建实际的窗口句柄,然后该调用需要父窗口句柄来设置对Windows.CreateWindowEx
的调用,并且例程无法获取父窗口句柄,因此它会出错。
尝试使用以下内容替换您的函数:
function GiveMeTheNiceFrame(OwnerWindow:HWND):HWND;
begin
Result := TMyNiceFrame.CreateParanted(OwnerWindow).Handle;
end;
... ie:使用CreateParented(AParentWindow:HWND)
构造函数,而不是通常的Create(AOwner:TComponent)
并将所有者HWND传递给您的DLL。
答案 1 :(得分:1)
要记住一些重要的事情:
尝试在EXE中重现您的问题;如果你不能复制,可能是上面列表中的第一件事。
- 的Jeroen
答案 2 :(得分:0)
听起来你只需要分配将框架保存到theframe.parent的组件(表单的一个表单或部分,如面板)。
在分配GUI之前,您无法进行GUI工作。框架是重复使用的表单的一部分,通常需要为它们分配一些父级。
将GUI代码移动到onshow或明确调用的过程,以便调用代码可以指定父代。
或者让父项成为函数中的参数。
答案 3 :(得分:0)
我发现了这个(CreateParams作为CreateWnd的一部分被调用):
procedure TCustomFrame.CreateParams(var Params: TCreateParams);
begin
inherited;
if Parent = nil then
Params.WndParent := Application.Handle;
end;
并且Application.Handle = 0因此它总是在稍后的CreateWnd中抛出错误。
看完之后
Delphi: How to call inherited inherited ancestor on a virtual method?
我通过覆盖我的框架中的CreateParams来解决它,错过了tCustomFrame版本:
type
tCreateParamsMethod = procedure(var Params: TCreateParams) of object;
type
tMyScrollingWinControl = class(TScrollingWinControl);
procedure TDelphiFrame.CreateParams(var Params: TCreateParams);
var
Proc: tCreateParamsMethod;
begin
TMethod(Proc).Code := @TMyScrollingWinControl.CreateParams;
TMethod(Proc).Data := Self;
Proc(Params);
end;
现在它只是在尝试将焦点放在子控制上时抛出错误,我想我会通过拦截WM_FOCUS来修复它,但我们将从这里开始如何。
function CreateFrame(hwndParent: HWnd): HWnd; stdcall;
var
frame: tFrame;
begin
Result := 0;
try
frame := TDelphiFrame.CreateParented(hwndParent);
Result := frame.Handle;
except on e: Exception do
ShowMessage(e.Message);
end;
end;
答案 4 :(得分:0)
您可以通过将nil分配给父OnClose事件来避免此消息,有时它可以正常工作:
SomeControl.Parent := nil;//Before free your TControl
SomeControl.Free;
答案 5 :(得分:0)
我认为这是非常酷的解决方案。我认为之前没试过:) 我使用了Dummy Parent(这是一个表格)。
function MyFrame_Create(hApplication, hwndParent:THandle; X, Y, W, H:Integer):Pointer; stdcall;
var Fr: TMyFrame;
F: TForm;
CurAppHandle: THandle;
begin
CurAppHandle:=Application.Handle;
Application.Handle:=hApplication;
//---
F:=TForm. Create(Application);//Create a dummy form
F.Position:=poDesigned;
F.Width:=0; F.Top:=0; F.Left:=-400; F.Top:=-400;//Hide Form
F.Visible:=True;
//---
Fr:=TMyFrame.Create(Application);
Fr.Parent:=F;//Set Frame's parent
//Fr.ParentWindow:=hwndParent;
Windows.SetParent(Fr.Handle, hwndParent);//Set Frame's parent window
if CurAppHandle>0 then Application.Handle:=CurAppHandle;
//---
Fr.Left:=X;
Fr.Top:=Y;
Fr.Width:=W;
Fr.Height:=H;
Result:=Fr;
end;//MyFrame_Create
procedure MyFrame_Destroy(_Fr:Pointer); stdcall;
var Fr: TMyFrame;
F: TObject;
begin
Fr:=_Fr;
F:=Fr.Parent;
Fr.Parent:=Nil;
if (F is TForm) then F.Free;
//SetParent(Fr.Handle, 0);
//Fr.ParentWindow:=0;
Fr.Free;
end;//MyFrame_Destroy