The control 'xxx' has no parent window
Asked Answered
S

6

11

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!

Strongbox answered 23/9, 2010 at 6:54 Comment(2)
Why are you using DLLs and not Packages? DLLs are a nightmare to get right.Unbelt
possibly related: #23316613Hershel
B
8

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:

function GiveMeTheNiceFrame:HWND;

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 to Windows.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:

function GiveMeTheNiceFrame(OwnerWindow:HWND):HWND;
begin
  Result := TMyNiceFrame.CreateParanted(OwnerWindow).Handle;
end;

... ie: use the CreateParented(AParentWindow:HWND) constructor, not the usual Create(AOwner:TComponent) and pass an owner HWND to your DLL.

Bema answered 23/9, 2010 at 8:8 Comment(9)
Thanks for the answer. I think it should help; I'll try that later. BTW, let me ask you a question. Why is it not allowed to return a Delphi specific object over DLLs? I'm going to use this library with Delphi applications only. The function returns the TFrame type but the actual object created is an instance of a descendant class. Isn't it OK? Thanks!Strongbox
It's not OK for versioning reasons: Using the returned object requires knowledge of it's memory layout, and that's subject to change (it can change with Delphi's version). It will work when you test it, and it will work if the Exe and Dll are built with the same compiler and same compiler settings, but if you're going to impose such restrictions you might as well use packages.Bema
Thanks, it's goot to know that. I did what you suggested and I'm still getting the exception :-(. The exported function does this: Result := TFrame1.CreateParented(ParentWindowHandle); And ParentWindowHandle is a handle to the main window of an app that calls the method (Self.Handle, when called from the main window class).Strongbox
Debug further. With your DLL in the editor, go to Run -> Load Process and load the EXE that uses your DLL. Do whatever you need with the EXE to make it load your DLL and call your function. The IDE will stop on the error, look at the call stack, find the offending code and make sure that Parent is assigned.Bema
The correct way to debug the DLL is to specify the Host Application (the exe) in Project Options and then do RUN. The Host Application will be started and when the error happens you'd be able to debug as you normally do.Bema
This is the line that raises an exception (a call stack item): ":00511884 TWinControl.CreateWnd + $AC". The parent of the frame is nil, even though the ParentWindow is non-zero... I think I'm giving up. I'll just use TForm :-). Thanks for your commitment anyway!Strongbox
I started my answer with the ` About the error ` section, telling you the error pops up in exactly the place you're saying it pops up: TWinControl.CreateWnd; You need to look into your call stack, call by call, until you find your own code: that's what needs fixing.Bema
Thank you for devoting your time!Strongbox
"psycho hat" or "psychic hat"? Huge difference :-)Formulaic
U
1

There are a few important things to remember:

  1. When using DLLs, both your DLL and your EXE each have an Application instance that are struggling for control. The Controls in your DLL will see the Application instance that belongs to the DLL; the Controls in your EXE will see the Application instance that belongs to the EXE. That struggle is not there when using packages, as then there will only be one Application instance.
  2. Frames are Controls, but they are not Forms.
  3. When using Controls in an application, they cannot visually exist without a parent Control (usually a Form or a container that has a parent hierarchy towards a Form).
  4. Some Controls cannot expose their full functionality unless they exist visually and have a valid parent.

Try to reproduce your problem inside the EXE; if you cannot reproduce, it is probably the first thing in the above list.

--jeroen

Unbelt answered 23/9, 2010 at 7:46 Comment(0)
S
0

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.

Sunset answered 23/9, 2010 at 7:37 Comment(2)
Upon second-reading your answer (I'm a bit word blind), it seems your answer is missing a bit after the "normally need to"...Unbelt
fixed. Writing in a hurry is always a problem for me. Working on all sentences at once :)Sunset
H
0

I found this (CreateParams is called as part of CreateWnd):

procedure TCustomFrame.CreateParams(var Params: TCreateParams);
begin
  inherited;
  if Parent = nil then
    Params.WndParent := Application.Handle;
end;

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:

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;

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.

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;
Harrisharrisburg answered 18/3, 2011 at 18:44 Comment(0)
R
0

You can avoid this message by assigning nil to the parent OnClose event, sometimes it works:

SomeControl.Parent := nil;//Before free your TControl
SomeControl.Free;
Rheo answered 17/11, 2011 at 22:9 Comment(3)
This is accidental programming.Hershel
Kachwahed: You should not program by guesswork. It will just litter your code with unnecessary garbage that usually acts as a catalyst for subtle and unexpected bugs. Yes, it can be tricky; but you need to put the effort into root-cause analysis so you can properly understand the problem. In this particular case, your answer is totally irrelevant to OP's problem.Diffractive
And as a second point, you shouldn't be trying to "avoid error messages". Error messages aren't a problem ... They're simply a message about a problem. So don't just shoot the messenger. Figure out what the actual problem is, and fix the real problem!Diffractive
C
0

I think this is very cool solution. I think it is not tried before :) I'm using a Dummy Parent (which is a Form).

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
Cyanic answered 30/12, 2014 at 5:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.