在单独的线程中运行VCL

Del*_*mer 3 delphi multithreading vcl c++builder

我现在有一个相当罕见的情况.我有一个直接与Windows的消息队列交互的应用程序.此应用程序还使用LuaJIT运行外部Lua脚本.我想为这些脚本设置调试工具,因此我创建了一个普通的VCL应用程序,然后将其转换为DLL库.当第一个应用程序启动与库的调试会话时,此DLL创建一个分离的线程,其中整个VCL工具被初始化并运行.

procedure TDebuggerThread.Execute;
begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm (TMainForm, MainForm);
  Application.Run;
end;
Run Code Online (Sandbox Code Playgroud)

VCL是否完全支持以这种方式执行?哪个线程会TThread.Synchronize (Proc: TThreadProc)发送消息?

Inb4"向VCL和主应用程序的消息将会混乱" - 它们不会因为每个线程都有自己的消息队列.

此外,您可以在此处查看来源.(也许)有问题的库被命名LuaDebugger.在地方适当的客户端(Core,Engine,Client)我目前使用的LuaDefaultHost,这是一个相当简单的控制台应用程序,要求调试器和行为大多喜欢lua.exe.与控制台客户端,调试器的工作原理出奇的顺利-我遇到的唯一问题是,如果我关闭控制台窗口,而库仍在使用,VCL抛出"窗口句柄不再有效"(俄文:/).如果我让客户端按照预期的方式完成与调试器的交互,一切都很顺利.可能Windows.TerminateThread在单元定稿期间调用应该解决这个问题.

Dav*_*nan 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.
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

产量

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