Reputation: 291
I have multithreaded application that loads my custom dll.
In this dll I need to create a window.
I'm doing it by creating new thread and inside it I'm trying to create this window, but I have got error that tells me: EInvalidOperation - Canvas does not allow drawing
.
By searching in the net, I have discovered that I need custom message pump for that thread.
So, my question is, how properly do this?
What I do now is:
- external app is loading dll
- than this app in separte thread is calling Init
function from dll
- Init
function creates thread
- TMyThread
is declared as:
type
TMyThread = class(TThread)
private
Form: TMyForm;
FParentHWnd: HWND;
FRunning: Boolean;
protected
procedure Execute; override;
public
constructor Create(parent_hwnd: HWND); reintroduce;
end;
constructor TMyThread.Create(parent_hwnd: HWND);
begin
inherited Create(False); // run after create
FreeOnTerminate:=True;
FParentHWnd:=parent_hwnd;
FRunning:=False;
end;
procedure TMyThread.Execute;
var
parent_hwnd: HWND;
Msg: TMsg;
XRunning: LongInt;
begin
if not Terminated then begin
try
try
parent_hwnd:=FParentHWnd;
Form:=TMyForm.Create(nil); // <-- here is error
Form.Show;
FRunning:=True;
while FRunning do begin
if PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then begin
if Msg.Message <> WM_QUIT then
Application.ProcessMessages
else
break;
end;
Sleep(1);
XRunning:=GetProp(parent_hwnd, 'XFormRunning');
if XRunning = 0 then
FRunning:=False;
end;
except
HandleException; // madExcept
end;
finally
Terminate;
end;
end;
end;
The exception EInvalidOperation - Canvas does not allow drawing
is fired before thread reaches my existing message pump code.
What do I do wrong or what is the right way to make it work?
Thanks for any help.
To create second GUI thread in a DLL, I must do things exactly as in standard application.
Can anyone confirm my thinking?
In the DLL begin...end.
section I do:
begin
Application.CreateForm(THiddenForm, HiddenForm);
Application.Run;
end.
In the TMyThread.Execute
I must do:
procedure TMyThread.Execute;
begin
if not Terminated then begin
try
try
Application.CreateForm(TMyForm, Form);
???? how to make a thread that has remained in this place until you close this window ???
except
HandleException; // madExcept
end;
finally
Terminate;
end;
end;
end;
Is this the right way? Could it be that simple?
Upvotes: 0
Views: 1576
Reputation: 291
Earlier (a year ago) I stated this: "To create second GUI thread in a DLL, I must do things exactly as in standard application"
.
This is exactly
what everybody who is searching for this solution should do.
Let me explain, step by step:
we must add our application object to our thread:
type
TMyThread = class(TThread)
private
ThreadApplication: TApplication;
now some modification to definition of procedure TMyThread.Execute;
procedure TMyThread.Execute;
begin
if not Terminated then begin
try
ThreadApplication:=TApplication.Create(nil);
try
ThreadApplication.Initialize;
ThreadApplication.CreateForm(TMyForm, Form);
ThreadApplication.Run;
finally
ThreadApplication.Free;
end;
finally
Terminate;
end;
end;
end;
so, this is it, now we have message pump in a second GUI thread in a DLL.
Recently I found confirmation to this solution in a Delphi-Jedi blog, wrote by Christian Wimmer:
http://blog.delphi-jedi.net/2008/05/27/winlogon-notification-package/
Thank You very much.
Upvotes: 0
Reputation: 612804
The simplest way to run a message queue in a thread is as follows:
procedure PerformThreadLoop;
var
Msg: TMsg;
begin
while GetMessage(Msg, 0, 0, 0) and not Terminated do begin
Try
TranslateMessage(Msg);
DispatchMessage(Msg);
Except
Application.HandleException(Self);
End;
end;
end;
And in your thread procedure would look like this:
procedure TMyThread.Execute
begin
InitialiseWindows;
PerformThreadLoop;
end;
All that said, what you are attempting is not going to work. You appear to be trying to use VCL components away from the main thread. That is specifically not allowed. The VCL's threading model dictates that all VCL code is run on the main thread. Your attempts to create a VCL form away from the main thread are doomed to failure.
I would question your desire to create a new thread. A Delphi DLL can show VCL forms provided that it runs those forms out of the thread that loaded and called the DLL. You can call Show
from that thread and show a modeless form. This means that you are relying on the host application's message queue to deliver messages to your windows. By and large this can be made to work. If your form is modal then you can simply call ShowModal
and the form will be serviced by the standard Delphi modal message loop.
So my advice to you is to keep all your GUI in the host app's GUI thread. If your DLL is expected to show GUI, and is also expected to do that away from the host app's GUI thread then you are in trouble. But I think that's highly unlikely to be the case.
Upvotes: 2