Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding non-VCL window into VCL align queue

Some background (kind of a continuation of TLabel and TGroupbox Captions Flicker on Resize):

  • So, I have an application that loads different plugins and creates a new tab on a TPageControl for each one.
  • Each DLL has a TForm associated with it.
  • The forms are created with their parent hWnd as the new TTabSheet. Since the TTabSheets aren't a parent of the form as far as VCL is concerned (didn't want to use dynamic RTL, and plugins made in other languages) I have to handle resizes manually.

I just seem to be running into a lot of new issues (but great learning experiences) for this "plugin" type of application.

So, my current struggle is trying to have a plugin that doesn't get inserted into a TTabSheet but will be resized and aligned directly on the form.

Since this would be easier to explain with a picture: Visual representation of question Now I could manually do the alignment and the resize, but I'd much rather have the VCL alignment procedures (alClient, alTop, etc) do it for me. That way I would just have to set the plugins alignment on its form without thinking.

After looking through the VCL source I began to step through the align code and how it's called. Basically when a TControl gets a WM_RESIZE it will:

  1. Call Realign() which calls AlignControl()
  2. AlignControl() will get the client rect and call AlignControls()
  3. AlignControls() will call DoAlign() for each TAlignment type in this order: alTop, alBottom, alLeft, alRight, alClient, alCustom, alNone
  4. DoAlign() will loop through FControls and FWinControls (which are TLists) and will align them appropriately

So my thought process is that if I create a new TWinControl, set it's handle to the plugins form (window) handle, and insert it into the FControls list with the proper align it should do my work for me.

Of course I'm here, so it failed miserably. I even get an AV when exiting the application about an invalid window handle. My guess is that the TWinControl I created is trying to free the handle of the plugins form (window) which doesn't exist any more.

What I've tried:

procedure AddHandleToControlList(AHandle: DWORD; Align: TAlign);
var
  NewWinControl : TWinControl;
begin
  NewWinControl := TWinControl.Create(frmMain);
  NewWinControl.WindowHandle := AHandle;
  NewWinControl.Align := Align;
  NewWinControl.Width := frmMain.ClientWidth;
  NewWinControl.Height := 30;
  NewWinControl.Parent := frmMain;
end;

procedure AddHandleToControlList(AHandle: DWORD; Align: TAlign);
var
  NewWinControl : TWinControl;
begin
  NewWinControl := TWinControl.Create(frmMain);
  NewWinControl.WindowHandle := AHandle;
  NewWinControl.Align := Align;
  NewWinControl.Width := frmMain.ClientWidth;
  NewWinControl.Height := 30;
  TWinControl(frmMain).Insert(NewWinControl);
end;

Soooo, thoughts?

EDIT 1:

Ok, so this correctly adds the control to the list and conforms the the TAlign set (why is it that I spend 8 hours trying to figure something out, I post here, and then the answer just appears...oh well someone might find this question and my ramblings useful):

procedure AddHandleToControlList(AHandle: DWORD; AName: PChar; ATop, ALeft, AWidth, AHeight: Integer; AAlign: TAlign);
var
  NewWinControl : TWinControl;
begin
  NewWinControl := TWinControl.Create(frmMain);
  With NewWinControl Do
    begin
    Name := AName;
    Top := ATop;
    Left := ALeft;
    Width := AWidth;
    Height := AHeight;
    Align := AAlign;
    WindowHandle := AHandle;
    Visible := True;
  end;
  TWinControl(frmMain).InsertControl(NewWinControl);
end;

The issue now is that when the application closes, I get the invalid error AV...I shall continue!!

EDIT 2: Ok, so it is TWinControl.DestroyWindowHandle that raises the AV because the window handle doesn't exist any more. I'm trying to think of a clean solution.

like image 273
ThievingSix Avatar asked Nov 11 '11 18:11

ThievingSix


1 Answers

Derive a new class from TWinControl and override its virtual DestroyWindowHandle() method to not free the HWND you provide. The default implementation of TWinControl.DestroyWindowHandle() calls the Win32 API DestroyWnd() function.

like image 153
Remy Lebeau Avatar answered Sep 28 '22 12:09

Remy Lebeau