我现在的情况相当罕见。 我有一个直接与Windows的消息队列交互的应用程序。此应用程序还使用LuaJIT运行外部Lua脚本。我想为这些脚本设置调试工具,因此我创建了一个普通的VCL应用程序,然后将其转换为DLL库。当第一个应用程序启动与库的调试会话时,此DLL创建一个分离的线程,其中整个VCL工具被初始化并运行。
procedure TDebuggerThread.Execute;
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm (TMainForm, MainForm);
Application.Run;
end;
VCL是否完全支持以这种方式执行? TThread.Synchronize (Proc: TThreadProc)
将向哪个帖子发送消息?
对VCL和主应用程序的Inb4“消息将会混乱” - 它们不会因为每个线程都有自己的消息队列。
此外,您可能会看到来源here。 (可能)有问题的库名为LuaDebugger
。取代适当的客户端(Core
,Engine
,Client
)我正在使用LuaDefaultHost
,这是一个相当简单的控制台应用程序,需要调试器和行为大多像lua.exe
。
使用控制台客户端,调试器工作非常顺利 - 我遇到的唯一问题是,如果我在使用库时关闭控制台窗口,VCL会抛出“窗口处理程序不再有效”(俄语:/)。如果我让客户端按照预期的方式完成与调试器的交互,一切都很顺利。在单元定稿期间可能会调用Windows.TerminateThread
来解决这个问题。
答案 0 :(得分:6)
您唯一的希望是创建线程,然后从该线程加载DLL。因此,为了尽可能清楚,您创建线程,然后从该线程中执行的代码,您调用LoadLibrary
来加载DLL。
VCL必须用完加载DLL的线程。 VCL初始化在DLL初始化期间发生,并确定哪个线程是VCL主线程。 VCL主线程是初始化VCL的线程,VCL是加载DLL的线程。
您可能必须对整个方法保持清醒,因为您将在一个进程中拥有两个GUI线程,两个消息泵。显示模态窗口涉及在两个GUI线程上禁用窗口。
我无法确定这种通用方法(同一进程中的两个GUI线程,其中一个是VCL线程)将起作用,从未完成。但是我觉得它很有可能会飞。
您还会提出一个非常具体的问题:
TThread.Synchronize(Proc:TThreadProc)向哪个线程发送消息?
答案始终是初始化模块的线程。因此对于可执行文件,这是该进程的主线程。对于DLL,初始化模块的线程是调用LoadLibrary
的线程,该线程执行对DllMain
的初始调用,该线程执行DLL单元的初始化代码。这在RTL / VCL中称为模块的主线程。它是ID由System.MainThreadID
给出的线程。
为了证明这一点,如果你不接受我的话,这里有一点示范。
<强>可执行强>
program DllThreading;
{$APPTYPE CONSOLE}
uses
Classes, Windows;
type
TMyThread = class(TThread)
protected
procedure Execute; override;
end;
procedure TMyThread.Execute;
var
lib: HMODULE;
proc: procedure; stdcall;
begin
lib := LoadLibrary('dll.dll');
proc := GetProcAddress(lib, 'foo');
proc();
Sleep(INFINITE);
end;
begin
Writeln('This is the process main thread: ', GetCurrentThreadId);
TMyThread.Create;
Readln;
end.
<强> DLL 强>
library Dll;
uses
Classes, Windows;
type
TMyThread = class(TThread)
private
procedure DoStuff;
protected
procedure Execute; override;
end;
procedure TMyThread.DoStuff;
begin
Writeln('This is the thread which executes synchronized methods in the DLL: ', GetCurrentThreadId);
end;
procedure TMyThread.Execute;
begin
Writeln('This is the thread created in the DLL: ', GetCurrentThreadId);
Synchronize(DoStuff);
end;
procedure foo; stdcall;
begin
TMyThread.Create;
CheckSynchronize(1000);
end;
exports
foo;
begin
Writeln('This is the initialization thread of the DLL: ', GetCurrentThreadId);
end.
<强>输出强>
This is the process main thread: 2788 This is the initialization thread of the DLL: 5752 This is the thread created in the DLL: 6232 This is the thread which executes synchronized methods in the DLL: 5752
答案 1 :(得分:1)
来自EDN的回答:Remy Lebeau:
DLL有自己独立的VCL和RTL自包含副本 从主应用程序的副本。在大多数情况下,这种线程使用 DLL内部通常没问题,但是对主线敏感的功能, 像
TThread.Synchronize()
和TThread.Queue()
一样,除非你,否则不会有效 使用您的System.MainThreadID
手动更新ThreadID
变量 “主”线程,除非你的线程定期调用CheckSynchronize()
(哪个 通常在TThread
“唤醒”“主”线程时自动调用 执行Synchronize
/Queue
操作时。)
不知道手动调整System.MainThreadID
是否安全,但这里主要问题的答案是“一般都好”。
答案 2 :(得分:-1)
哦,我正在回答我自己的问题。
所以,
VCL是否完全支持以这种方式执行?
似乎确实如此。从我们这里得到的,VCL代码只是在“当前”线程中运行,并且不知道是否存在其他线程,或者这个“当前”线程是进程的主线程还是在同一个二进制文件中创建。只要这个帖子不与其他人混淆,一切都会顺利。
TThread.Synchronize(Proc:TThreadProc)向哪个线程发送消息?
实验说消息将被发送到进程的主线程,而不是VCL线程(当然,它是Application.Run
工作的地方)。要与VCL线程同步,我必须显式地将消息发送到VCL表单,这里(LuaDebugger/USyncForm.pas)它是自定义UM_MethodCall,其中WParam保存指向同步代码的指针,LParam保存可选的void*
参数。