控件'xxx'没有父窗口

时间:2010-09-23 06:54:53

标签: delphi dll tframe

我正在尝试在Delphi中编写一个dll库,该函数创建一个TFrame后代的实例并返回它。但是当我在一个应用程序中导入这个函数时,每次调用它时我都会得到一个例外,例如“'xxx'控件没有父窗口”。我不是百分百肯定,但是当访问任何GUI控件时,该类的构造函数中出现异常。

你能告诉我这种行为的原因是什么吗?我应该只使用TForm后代还是有更好的解决方案?

谢谢!

6 个答案:

答案 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)

要记住一些重要的事情:

  1. 使用DLL时,DLL和EXE都有一个难以控制的Application实例。 DLL中的控件将看到属于DLL的Application实例; EXE中的控件将看到属于EXE的Application实例。使用包时不存在这种困难,因为那时只有一个Application实例。
  2. 框架是控件,但它们不是表单。
  3. 在应用程序中使用控件时,如果没有父控件(通常是表单或具有面向表单的父层次结构的容器),它们在视觉上不可存在。
  4. 某些控件无法公开其全部功能,除非它们在视觉上存在且具有有效的父级。
  5. 尝试在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