Link to home
Start Free TrialLog in
Avatar of article
article

asked on

Hard problem: Focus of embeded DLL Form

   I wrote a DLL which has a Form.  In another Exe, I load  the DLL, and embed it's Form to a panel of the Exe's mainform by set the ParentWindow property.
    The problem is when I press TAB in the DLL form's control, the focus shift to the main form, and never come back.
    I need to solve this ugently, please feel free to add any comments.
Avatar of geobul
geobul

Hi,
If you have access over the DLL-Form properties, try this way:

with Form2 do begin
  Parent := Panel1;
  Top := 0;
  Left := 0;
  Width := Panel1.ClientWidth;
  Height := Panel1.ClientHeight;
end;

Regards, Geo
article,

I have encountered this problem as well... Tab keys, and even Alt keys do not work well.

One of my colleagues added this to the child's OnCreate:
PostMessage(Self.Handle, WM_USER + $1000, 0, 0);

I do not know what it is, what it does, or how my colleague came up with this number... but it seems to have solved the Tab problem.

As for the Alt key problem, it was solved when I compiled the DLL with runtime packages and then distribuing the DLL together with the BPL. That's the only solution I came up with so far :(


DragonSlayer
Avatar of article

ASKER

To geobul:
    I tried your method, but the DLL form hide itself.

To DragonSlayer:
    I tried your magic postmessage, but nothing happens.
I use C++ Builder 5, I think it's just identical with Delphi 5.  I don't encounter any problem in C++ Builder 4, do you know why?
On exe form find the last component in tab order, and this line in OnExit procedure :

Windows.SetFocus(DllForm.Handle);


So when you are on EXE form and you get to the last control in tab order focus will be switched on DLL form.


Another aproach :
Question is how are you creating your form, if you are not doing it like this, try it, I put show code in DLL, in your case you must send and parent window handle or pointer to parent TForm object, but important thing is to set up Application variable in DLL, you will do this by putting forms in uses clause of your DLL, here is the code:

call this from exe form :

DllForm := ShowChildForm(Application.Handle, self.Handle);

kill it

KillDllForm(dllForm);


DLL CODE:

Funtion ShowChildForm(AHandle: THandle, ParentWnd: HWND):LongInt;
var
  dllForm: TDllForm;
begin
  Application.Handle := AHandle
  dllForm := TDllForm.Create(Application);
  dllForm.ParentWindow := ParentWnd;
  Result := LongInt(dllForm);
  dllForm.Show;
end;

procedure KillDllForm(dllForm: LongInt);
begin
  TDllForm(dllForm).Release;
end;

This is mostly copied from "Delphi 5 Developer's Guide".
 


Avatar of article

ASKER

To geobul:
    I tried your method, but the DLL form hide itself.

To DragonSlayer:
    I tried your magic postmessage, but nothing happens.
I use C++ Builder 5, I think it's just identical with Delphi 5.  I don't encounter any problem in C++ Builder 4, do you know why?
Avatar of article

ASKER

Baksa,
Thanks to your comment.  The first part of your comment is right, it can shift to the DLL Form.  But when I press TAB in the DLL Form, it will not execute the onExit code.
I create the dll just the same you've mentioned.
Avatar of article

ASKER

Baksa,
Thanks to your comment.  The first part of your comment is right, it can shift to the DLL Form.  But when I press TAB in the DLL Form, it will not execute the onExit code.
I create the dll just the same you've mentioned.
Article,

You must put similar code in OnExit procedure of last control in tab order on dllForm, like this :

Windows.SetFocus(ExeForm.Handle);


If you allready do this, maybe your exeform handle is not valid, I can't think of anything else.
DragonSlayer,

About your "magic" message, probably your collegue implemented handling of that message in same child's window window procedure. Posting WM_USER + $1000 will do nothing unless window procedure of window that message is send to doesn't handle that message.
Baksa,

Nope, the child window doesn't handle any messages.

You need to set the parent property of the DLL form to be the panel in your exe form. I think this is re-iterating Geobul suggestion. I've tried it with a simple exe and dll and the tabbing seems to work ok.

procedure TForm1.Button1Click(Sender: TObject);
var
  frm:TForm;
begin
  frm:=CreateForm;  // this is the DLL function to create form
  frm.Parent := panel2;
  frm.WindowState:=wsMaximized;
  frm.Show;
end;
If exe and dll is Delphi projects,
try build it with "build with runtime packages"
options.
DragonSlayer,

Maybe this message handling is implemented in ancestor class of child window, which class is that window, TForm ?

I searched Delphi source code and only WM_USER + $1000 was defined in comctrls, but this is some message for ToolBars
Avatar of article

ASKER

Hi all,
Can anyone post whole tested source code?  If it works, I will give additional points.
I can be reached at article@163.com.
Avatar of article

ASKER

Baksa,
I put the similar code in OnExit procedure in the DLL Form, the problem is it will never be executed.  Press Tab will shift to the main form, not the next control in the tab order.
Baksa,

All forms in that project are direct descendants of TForm...


Article,

Did you try rebuilding everything with 'Build with runtime packages' as I'd earlier suggested?
Avatar of article

ASKER

Hi all,
I solve this problem myself.  It's a delphi bug, and nobody point it out.  Now, who wants the points?
How did you solve it?
Avatar of article

ASKER

Hi DragonSlayer,
Just open the forms.pas, and look at the TApplication.IsKeyMsg function, you'll find out the bug.  If you use Delphi, you'll be lucky, just fix it, and everything will be OK.  I've tested it.  Now, my problem is I don't know how to patch the vcl50.bpl.  I have to do it because all the projects build with packages.  
And anybody knows how to patch the vcl bug in C++ builder?  I just include the fixed forms.pas, but the build speed will be very slow, and only solve part of the problem, the tab key still not functions properly.
Need Help!!!
Oh... it uses the Mainform's handle instead of the Client's handle, is it?
ASKER CERTIFIED SOLUTION
Avatar of SChertkov
SChertkov

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of article

ASKER

SChertkov,
Yes, your code works.  But it's impossible for me to use.  Cause we use dll form to seperate the program.  If the customer need new functions, we just write a new dll, and the main program won't need to modify and recompile.
So we must use loadlibrary to load the dll.  But if you use the loadlibrary,  when you call these code,
 Form := GetForm.Create(self);
 Form.Parent := Panel1;
 Form.Visible := True;
the Form will still invisible.  You must set the ParentWindow instead of Parent property.  That's the problem.  If you use the loadlibrary, and set Parent,  the Dll Form is still visible, just let me know, I'll very appreciate it.
Avatar of article

ASKER

SChertkov,
I'm wrong.  Use LoadLibrary still works.  Lucky Delphi users!  But I have to use C++ Builder.  The problem now is how to translate it into C++ Builder.
The skill is to get a form varible, and call its create method.  But it seems C++ Builder don't have the corresponding create method.
Anyone knows?  It seems that the success is very near.

Unfortunately i am not work with C++ Builder.
Class variable is the beautiful but not necessarily.
You can successfully create form inside you dll and
pass pointer to it in main program.
It is important
1. Build projects with 'runtime package' option.
2. Use TWinControl.Parent property instead of
   ParentWindow property.
For form creation you can use
Application.CreateForm in DLL.
I think this approach have analogs in C++ Builder.

If you have any problems later sign me
i can install C++ Builder.
Avatar of article

ASKER

SChertkov,
Use TWinControl.Parent property cause the dll form invisible in c++ builder, that's why I use ParentWindow.  I always choose build project with packages.  I will try to use the Application.CreateForm.
Could you please do some test in C++ Builder?  Thanks a lot.
Avatar of article

ASKER

SChertkov,
I just write a function in delphi,  and include the pas file in the c++ builder project,  call the function from c++ file, and solve the problem.
If you can use c++ builder's own method, just let me know.
Thank you very much.
Hi,
As I see the accepted answer is what I've written at the beginning of this thread. Please, correct me if I'm wrong.

Regards, Geo
Avatar of article

ASKER

Geo,
Sorry, but your answer is too simple.  The key is dll return the class, and exe use the class to create the form, not dll create the form itself.  Maybe you know exactly the answer,  but just I'm not smart enough to understand you.
And I don't know how to give points to several person.