您唯一的希望是创建线程,然后从该线程加载 DLL。因此,为了尽可能清楚,您创建线程,然后从该线程中执行的代码中调用LoadLibrary
以加载 DLL。
VCL 必须用完加载 DLL 的线程。VCL 初始化发生在 DLL 的初始化期间,它决定了哪个线程是 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.
动态链接库
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.
输出
这是进程主线程:2788
这是DLL的初始化线程:5752
这是在 DLL 中创建的线程:6232
这是在 DLL 中执行同步方法的线程:5752