您唯一的希望是创建线程,然后从该线程内部执行的代码调用
LoadLibrary
来加载DLL。必须在加载DLL的线程中运行VCL。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.
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.
输出
这是主线程的进程:2788
这是DLL的初始化线程:5752
这是在DLL中创建的线程:6232
这是在DLL中执行同步方法的线程:5752
Application.*
方法在调试器线程中运行。我假设由单元初始化创建的资源不绑定到特定线程。看起来是这样,但我可能错了。之前你说只有在我从新线程调用LoadLibrary
时才能工作;现在你说移动Application.Initialize
是可以的。这是什么意思? - DelfigamerLoadLibrary
的线程。因此,单元初始化(DllMain
的一部分)和Application.*方法必须在同一线程上运行。 - David Heffernan