我有多线程应用程序加载我的自定义DLL。
在这个DLL中我需要创建一个窗口
我是通过创建新线程并在其中进行的,我正在尝试创建此窗口,但我有错误告诉我:EInvalidOperation - Canvas does not allow drawing
。
通过在网上搜索,我发现我需要为该线程定制消息泵 所以,我的问题是,这有多正确呢?
我现在做的是:
- 外部应用程序正在加载DLL
- 比separte线程中的这个应用程序从dll中调用Init
函数
- Init
函数创建线程
- TMyThread
声明为:
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;
在线程到达我现有的消息泵代码之前触发异常EInvalidOperation - Canvas does not allow drawing
。
我做错了什么或使它成功的正确方法是什么? 谢谢你的帮助。
要在DLL中创建第二个GUI线程,我必须完全按照标准应用程序执行操作 谁能证实我的想法?
在DLL begin...end.
部分,我做了:
begin
Application.CreateForm(THiddenForm, HiddenForm);
Application.Run;
end.
在TMyThread.Execute
我必须这样做:
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;
这是正确的方法吗?可能就这么简单吗?
答案 0 :(得分:2)
在线程中运行消息队列的最简单方法如下:
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;
在你的线程程序中看起来像这样:
procedure TMyThread.Execute
begin
InitialiseWindows;
PerformThreadLoop;
end;
所有这一切,你所尝试的不会起作用。您似乎试图使用远离主线程的VCL组件。这是特别不允许的。 VCL的线程模型规定所有VCL代码都在主线程上运行。您尝试在远离主线程的情况下创建VCL表单注定要失败。
我会质疑你想要创建一个新线程。 Delphi DLL可以显示VCL表单,只要它从加载和调用DLL的线程中运行那些表单。您可以从该线程调用Show
并显示无模式表单。这意味着您依靠主机应用程序的消息队列将消息传递到您的窗口。总的来说,这可以起作用。如果你的表单是模态的,那么你可以简单地调用ShowModal
,表格将由标准的Delphi模态消息循环提供服务。
所以我的建议是将所有GUI保存在主机应用程序的GUI线程中。如果你的DLL应该显示GUI,并且还希望远离主机应用程序的GUI线程,那么你就麻烦了。但我认为情况极不可能。
答案 1 :(得分:0)
"To create second GUI thread in a DLL, I must do things exactly as in standard application"
。
这是exactly
所有正在寻找此解决方案的人都应该这样做
让我一步一步解释:
我们必须将我们的应用程序对象添加到我们的线程中:
type
TMyThread = class(TThread)
private
ThreadApplication: TApplication;
现在对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;
所以,就是这样,现在我们在DLL中的第二个GUI线程中有消息泵。
最近我在Delphi-Jedi博客中找到了对此解决方案的确认,由Christian Wimmer撰写: http://blog.delphi-jedi.net/2008/05/27/winlogon-notification-package/
非常感谢你。