Running VCL in a separate thread

怎甘沉沦 提交于 2019-12-03 17:15:14

Your only hope is to create the thread, and then load the DLL from that thread. So, to be as clear as possible, you create the thread and then from code executing within that thread, you call LoadLibrary to load the DLL.

The VCL has to be run out of the thread that loads the DLL. The VCL initialization happens during the initialization of the DLL and that determines which thread is the VCL main thread. The VCL main thread is the thread which initializes the VCL, the thread which loads the DLL.

You'll likely have to keep a clear head with this entire approach because you'll have two GUI threads, two message pumps, in a single process. Showing a modal window involves disabling the windows on both GUI threads.

I cannot be sure that this general approach (two GUI threads in the same process, one of which is a VCL thread) will work, never having done it. However I think there's a good chance it will fly.


You also ask a quite specific question:

To which thread will TThread.Synchronize (Proc: TThreadProc) send its message?

The answer is always the thread which initialized the module. So for an executable this is the main thread of the process. For a DLL the thread which initialized the module is the thread which called LoadLibrary, the thread which executes the initial call to DllMain, the thread which executes the DLL units' initialization code. This is known in the RTL/VCL as the module's main thread. It is the thread whose ID is given by System.MainThreadID.

To prove the point, in case you don't take my word for this, here's a little 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.

Output

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

Answer from EDN by Remy Lebeau:

The DLL has its own self-contained copy of the VCL and RTL that are separate from the main app's copy. For the most part, this kind of threaded usage inside of the DLL is generally OK, but mainthread-sensitive functionality, like TThread.Synchronize() and TThread.Queue(), will not work unless you manually update the System.MainThreadID variable with the ThreadID of your "main" thread, unless your thread calls CheckSynchronize() periodically (which is normally called automatically when TThread "wakes up" the "main" thread when a Synchronize/Queue operation is performed).

Don't know whether adjusting System.MainThreadID manually is safe, but the answer to main question here is "generally OK".

Oh cool I'm answering my own question.

So,

Does VCL fully supports being executed this way?

Seems it does. From what we've got here, VCL code just runs in "current" thread and is not aware if there are another ones or if this "current" thread is process's main one or is created right in the same binary. As long as this thread doesn't mess with others, everything goes fine.

To which thread will TThread.Synchronize (Proc: TThreadProc) send its message?

Experiment says the message will be sent to process's main thread, not to VCL thread (naturally, it's one where Application.Run works). To synchronize with VCL thread I have to explicitly send messages to a VCL form, here (LuaDebugger/USyncForm.pas) it's custom UM_MethodCall with WParam holding pointer to synchronized code and LParam holding optional void* argument.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!