控制“xxx”没有父窗口
我正在尝试在 Delphi 中编写一个 dll 库,其中包含一个创建 TFrame 后代实例并返回它的函数。但是当我在应用程序中导入这个函数时,每次调用它时我都会得到一个异常,例如“'xxx'控件没有父窗口”。我不是 100% 确定,但是当访问任何 GUI 控件时,异常会出现在该类的构造函数中。
您能告诉我这种行为的原因是什么吗?我应该只使用 TForm 后代还是有更好的解决方案?
谢谢你!
I'm was trying to write a dll library in Delphi wih a function that creates an instance of a TFrame descendant and returns it. But when I imported this function in an application, every time I called it I would get an exception like "the 'xxx' control has no parent window". I'm not 100% sure, but the exception appeared in the constructor of that class when any of GUI controls was accessed.
Could you please tell me what the reason of that behaviour is? Should I just use TForm descendants instead or is there a better solution?
Thank you!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
关于错误
该错误消息是从 Controls.pas 单元的
TWinControl.CreateWnd
方法引发的。本质上,该代码用于为您的 TWinControl 后代(TFrame、TButton、TEdit...如果它可以有键盘焦点,那么它就是 TWinControl 后代)创建窗口句柄,这实际上是一个非常明智的错误消息:您不能有没有 WindowParent 的窗口,并且由于我们在这里讨论的是 VCL,因此尝试从 TWinControl.Parent 获取父窗口句柄很有意义;这还没有分配。这不是弹出错误消息的原因。您会看到该错误消息,因为您用于设置框架的某些代码需要窗口句柄来执行某些操作。它可以是任何东西,比如设置某些组件的标题(内部需要窗口句柄来进行某些计算)。我个人真的很讨厌这种情况发生。当我从代码创建GUI时,我尝试尽可能延迟Parent的分配,以试图延迟窗口的创建,所以我被这个问题困扰了很多次。
特定于您的 DLL 使用情况,可能的修复
我将戴上我的心理读心术帽子。由于您需要从 DLL 返回一个 FRAME,并且您无法返回实际的 Frame,因为这是一个特定于 Delphi 的对象,并且不允许您通过 DLL 边界返回特定于 Delphi 的对象,所以我猜测您正在返回一个窗口句柄,就像所有好的 API 所做的那样,使用如下的函数定义:
问题是,该例程需要通过调用
TWinControl.CreateWnd
创建实际的窗口句柄,并在反过来,该调用需要父窗口句柄来设置对 Windows.CreateWindowEx 的调用,并且例程无法获取父窗口句柄,因此会出错。尝试将您的函数替换为以下内容:
...即:使用
CreateParented(AParentWindow:HWND)
构造函数,而不是通常的Create(AOwner:TComponent)
和将所有者 HWND 传递给您的 DLL。About the error
That error message is raised from the Controls.pas unit, from the
TWinControl.CreateWnd
method. Essentially that code is used to create the Window handle for your TWinControl descendant (TFrame, TButton, TEdit... if it can have keyboard focus it's an TWinControl descendant), and it's actually an very sensible error message: You can't have a Window without an WindowParent, and since we're talking about the VCL here, it makes a lot of sense to try and get the parent window handle from TWinControl.Parent; And that's not assigned.That's not WHY the error message is popping up. You get to see that error message because some of the code you're using to set up the frame requires an Window handle for some operation. It could be anything, like setting the Caption of some component (that internally requires an window handle do to some calculation). I personally really hate it when that happens. When I create GUI's from code I try to delay the assignment of Parent as much as possible, in an attempt to delay the creation of the window, so I got bitten by this many times.
Specific to your DLL usage, possible fix
I'm going to put my psycho mind reader hat on. Since you need to return a FRAME from your DLL, and you can't return the actual Frame because that's an Delphi-specific object and you're not allowed to return Delphi-specific objects over DLL boundaries, my guess is you're returning an Window Handle, as all the nice API's do, using a function definition like this:
The trouble is, that routine requires the creation of the actual Window Handle, by a call to
TWinControl.CreateWnd
, and in turn that call requires an parent window handle to set up the call toWindows.CreateWindowEx
, and the routine can't get an parent window handle, so it errors out.Try replacing your function with something allong the lines of:
... ie: use the
CreateParented(AParentWindow:HWND)
constructor, not the usualCreate(AOwner:TComponent)
and pass an owner HWND to your DLL.有几件重要的事情需要记住:
尝试在 EXE 中重现您的问题;如果您无法重现,它可能是上面列表中的第一件事。
——杰罗恩
There are a few important things to remember:
Try to reproduce your problem inside the EXE; if you cannot reproduce, it is probably the first thing in the above list.
--jeroen
听起来您只需将保存框架的组件(表单或表单的一部分,如面板)分配给 theframe.parent 。
在分配 GUI 工作之前,您不能执行该工作。框架是表单的一部分,可以重用,通常需要为其分配一些父级。
将 GUI 代码移至 onshow 或您显式调用的过程,以便调用代码可以分配父级。
或者将父级作为函数中的参数。
Sounds like you simply need to assign the component (a form or part of a form, like a panel) that holds the frame to theframe.parent.
You cannot do GUI work before it is assigned. Frames are parts of forms for reuse, and normally need to assign some parent to them.
Move the GUI code to onshow or a procedure you call explicitely, so that the calling code can assign parent.
Or make the parent a parameter in the function.
我发现这个(CreateParams 作为 CreateWnd 的一部分被调用):
并且 Application.Handle = 0 因此它总是在 CreateWnd 中稍后抛出错误。
读完这篇文章后
Delphi:如何在虚拟方法上调用继承的继承祖先?
我已经通过在我的框架中重写 CreateParams 来错过 tCustomFrame 版本来解决这个问题:
现在,它只是在尝试将焦点设置在子控件上时抛出错误,我想我会通过拦截 WM_FOCUS 来解决这个问题,但我们将如何从这里开始。
I found this (CreateParams is called as part of CreateWnd):
And Application.Handle = 0 so it always throws the error later in CreateWnd.
After reading this
Delphi: How to call inherited inherited ancestor on a virtual method?
I have solved it by overriding CreateParams in my frame to miss out the tCustomFrame version:
Now it's just throwing errors when trying to set the focus on subcontrols, which I think I will fix by intercepting WM_FOCUS but we'll how it goes from here.
您可以通过将 nil 分配给父 OnClose 事件来避免此消息,有时它会起作用:
You can avoid this message by assigning nil to the parent OnClose event, sometimes it works:
我认为这是非常酷的解决方案。我认为以前没有尝试过:)
我正在使用虚拟父级(这是一个表单)。
I think this is very cool solution. I think it is not tried before :)
I'm using a Dummy Parent (which is a Form).