Your only hope is to create a stream and then load the DLL from that stream. So, to be as clear as possible, you create a stream, and then from the code that runs in that stream, you call LoadLibrary to load the DLL.
VCL must be started from the thread that loads the DLL. VCL initialization occurs during the initialization of the DLL and determines which thread is the main VCL thread. The main VCL thread is the thread that initializes the VCL, the thread that loads the DLL.
You will probably have to keep a clear chapter with all this approach, because in one process you will have two GUI threads, two message pumps. The display of a modal window is associated with the disabling of windows in both threads of the graphical interface.
I cannot be sure that this general approach (two GUI threads in one process, one of which is a VCL thread) will work without ever doing this. However, I think there are good chances that he will fly.
You also ask a rather specific question:
To which thread will TThread.Synchronize (Proc: TThreadProc) send its message?
The answer is always the thread that initialized the module. Thus, for the executable, this is the main thread of the process. For a DLL, the thread that initialized the module is the thread that called LoadLibrary , the thread that makes the initial DllMain call, the thread that executes the DLL module initialization code. This is known in RTL / VCL as the main module stream. This is the thread whose identifier is specified by System.MainThreadID .
To prove this point, if you do not take my word for it, here is a small demonstration.
Executable
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.
Exit
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