Creating controls on main form from a DLL

Posted on 1997-09-04
Medium Priority
Last Modified: 2010-04-04
I have a situation where I need to create objects on the main applications form (or a panel) from inside a DLL. When I create a button in the manner described below, I experience these 3 problems:

1) the button is not displayed until I make the panel invisible then visible again
2) If I TAB to teh button, I generate the 'EInvalid Operation' exception with the message 'Control 'BB' has no parent window'
3) The object will not get destroyed when the application exits

The prime area of my concern is the second one. The strange thing is that if I use EXACTLY the same code inside the main application, none of these problems are evident!

I have drilled down through the Parent panels properties to compare all properties of the button and compared them to when it is created from inside teh main application, and can find no significant variation. In particular, the button's FParent property is set to the Panel!

Here is the code which I used inside the DLL to create the button:

procedure MakePB( UserOutput:TWinControl ); stdcall;
   BB := TButton.Create( UserOutput );
   with UserOutput.ClientRect do begin
     BB.Left := Left + 5;
     BB.Top := Top + 35;
   BB.Font.Style := [];
   BB.Font.Size := 10;
   BB.Parent := UserOutput;
   BB.Caption := 'A PB';
   BB.Name := 'A_PB';

This function is called like this:

procedure TForm1.Button3Click(Sender: TObject);

Any ideas???

(Note: The nature of my application is such that I can't create a Form inside the DLL for the button ...)
Question by:sonique

Expert Comment

ID: 1343800
At first glance, my reaction was to have you set the BB.Parent property before you set the other stuff (still a good idea).   I believe a TWinControl's Owner should be the Form, not the panel.  The 'Parent' is the panel, the form is the 'Owner'.  If you know the panel (UserOutput) has the form as the owner, then BB.Create( UserOutput.Owner ) will set that correctly.  Then set the parent as UserOutput like you are already doing.
Now, as for the object's destruction you might consider using the Form's InsertComponent method instead.  This will have the component inserted in the Form's component array and consequently destroyed when the form cleans up.

Hope this helps,
Ian C.

Author Comment

ID: 1343801
Thanks for the reply, but ....

Setting the Parent before setting the font results in: "ConvertError with message 'Cannot assign a TFont to a TFont'". This is why I placed it near the end (note that this does not happen if compiled into the main program).

Setting the owner to the form I had tried previously to no affect. Using the UserOutput.Owner.InsertComponent method does not seem to make any difference either!

This is all obviously something to do with the DLL accessing the main App's variables in a different manner - but I have no idea what it is. I even tried passing the main Apps Application.Handle and setting it to that value in the DLL to no avail.


Expert Comment

ID: 1343802
The problem is that objects in your dll is not the same as in your app (because they are diff. .exes), so you have to make controls with your app.
To do this, write an exported procedure in your app. This proc. will make the controll.
When you call your dll pass the addres of this proc., so your dll can make controls, as it's code was in the app.

Good luck

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.


Author Comment

ID: 1343803
Thanks for the suggestion, but unfortunately creating the control in the app itself is not an option because it defeats the whole purpose of the DLL. I used a pushbutton as a simple example, but in actual fact the DLL could create any sort of control, and the main APP (by design) does not know ahead of time what sort of control the DLL will create!
I have designed an architecture whereby the main app acts as a sort of interpreter, and a collection of DLL's act as "function Blocks". Some of these DLL's do non-visual work. For example one DLL is a multi-threaded comms manager for an RS422 network. Another provides printer control and status monitoring functions (displayed on a seperate form created by the DLL so that's no problem). Some of the DLL's will create controls on a common form, and the button is just an example.
There has to be a way to make the main App's VCL thread completely aware of the control - all other functionality works OK (such as mouse clicks, button text display, etc.), so why not keyboard control??

Any other takers?


Accepted Solution

JimBob091197 earned 400 total points
ID: 1343804
I had a similar problem before, and found no suitable solution.  There is a way around the problem as follows:

In your DLL you currently declare:
procedure MakePB( UserOutput:TWinControl );

I changed this to:
function MakePB(UserOutputWnd: THandle): TButton;
   NewButton: TButton;
   NewButton := TButton.Create(nil);
   NewButton.Caption := '&Hello';
   NewButton.ParentWindow := UserOutputWnd;
   Result := NewButton;

1)  MakePB expects UserOutputWnd instead of UserOutput.
2)  No owner was given to NewButton, and I assigned NewButton.ParentWindow (NOT .Parent).
3)  I made it a function which returns a button.  The form (in my e.g.) later freed the button.

In your form you now call:
AButton := MakePB(Panel1.Handle);

In your form's Destroy event:

(You can ignore the font stuff without worrying about the "Cannot assign a TFont to a TFont" error.)

All this works fine, but there are several caveats.  I mentioned above that I never found a SUITABLE solution.  Here's why:
1) In your form, after the call to MakePB, try assigning AButton.OnClick := .....;
The OnClick event will be ignored...  (However, AButton.Caption := '&Hit Me' will change the caption!!)

2) Instead of a TButton, try a control with an Align property (e.g. TListBox).  The listbox will not be resized when Panel1 is resized.

There are a few other disadvantages too, but I'll let you experiment...  You may find many solutions that I missed.

Author Comment

ID: 1343805
Sounds like it will do the trick ... but there doesn't seem to be a ParentWindow property ... I even searched the VCL source!

Which version of Delphi are you talking about? I'm using v2.02


Expert Comment

ID: 1343806
Mmm... I'm using Delphi 3.  

The VCL reveals that setting the ParentWindow property uses the Windows SetParent API call.  You could try this.  

e.g.  SetParent(MyButton.Handle, MyPanel.Handle);

Hope this works for you!!

Author Comment

ID: 1343807
I'll try that when I'm back at work on Monday .... if It doesn't work I'll have to use a different approach to the problem.

Thanks for the help!


Expert Comment

ID: 1343808
Welcome.  Keep me posted.

Author Comment

ID: 1343809
Well, no luck I'm afraid. If you look at the Win32 API Help, SetParent() is used to change Parents. When I try this approach I get an exception raised:

EInvalid Operation. Message 'Control '' has no parent window'

Back to the drawing board .....

Expert Comment

ID: 1343810

The following code will give that exception:
AButton := TButton.Create(nil);
Windows.SetParent(AButton.Handle, APanel.Handle);

The reason for the exception is because at the time that you call "SetParent", AButton does not have a handle. (Put a break on the SetParent line, and look at the value of AButton.Handle.)  Delphi calls CreateHandle, which in turn calls CreateWnd.  CreateWnd raises that exception if the parent does not exist.

In Delphi 3, the call to AButton.ParentWindow (i.e. TButton.SetParentWindow in the VCL) only calls the SetParent API if there is an existing parent window.  If not, then the FParentWindow property is set, which will be used when the handle is created (actually in CreateParams).

That is why the following code does work in Delphi 3, but the code above does not work in Delphi 2 or 3:
AButton := TButton.Create(nil);
AButton.ParentWindow := APanel.Handle;

It OFTEN frustrates me that so much functionality in Delphi has been hidden in the Private sections of the classes!!  (Although many would say that this is required by the Object Oriented approach...)  The trick is to set the FParentWindow value (or the equivalent in Delphi 2), or to get Delphi to create the handle for you (via a call to HandleNeeded or something.)

I'll ask a colleague of mine who uses Delphi 2 when he gets in later.  He may have some suggestions.


Author Comment

ID: 1343811
Thanks ....
I'm working on another problem today (DOS C Code), so I'll wait to see if you come up with anything new before I have another look at this.

I appreciate the assistance by the way!


Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
In this tutorial I will show you how to use the Windows Speech API in Delphi. I will only cover basic functions such as text to speech and controlling the speed of the speech. SAPI Installation First you need to install the SAPI type library, th…
This video tutorial shows you the steps to go through to set up what I believe to be the best email app on the android platform to read Exchange mail.  Get the app on your phone: The first step is to make sure you have the Samsung Email app on your …
Free Data Recovery software is an advanced solution from Kernel Tools to recover data and files such as documents, emails, database, media and pictures, etc. It supports recovery from physical & logical drive after a hard disk crash, accidental/inte…

587 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question