4

私は今、かなりまれな状況にあります。Windows のメッセージ キューと直接対話するアプリケーションがあります。このアプリケーションは、LuaJIT を使用して外部 Lua スクリプトも実行します。これらのスクリプトのデバッグ機能が欲しかったので、単純な VCL アプリケーションを作成し、それを DLL ライブラリに変換しました。最初のアプリケーションがライブラリを使用してデバッグ セッションを開始すると、この DLL は別のスレッドを作成し、そこで VCL 機能全体が初期化されて実行されます。

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

VCL はこの方法での実行を完全にサポートしていますか? どのスレッドにTThread.Synchronize (Proc: TThreadProc)メッセージを送信しますか?

Inb4「VCL とメイン アプリケーションへのメッセージは混乱します」 - すべてのスレッドが独自のメッセージ キューを持っているため、混乱しません。

また、ここにソースが表示される場合があります。(たぶん) 問題のあるライブラリの名前はLuaDebugger. 適切なクライアント ( Core, Engine, Client) の代わりに、私が現在使用してLuaDefaultHostいるlua.exe. コンソール クライアントを使用すると、デバッガーは驚くほどスムーズに動作します。私が遭遇した唯一の問題は、ライブラリがまだ使用されている間にコンソール ウィンドウを閉じると、VCL が「ウィンドウ ハンドラーが無効になりました」(ロシア語:/) をスローすることです。クライアントが本来の方法でデバッガーとの対話を終了できるようにすると、すべてがうまくいきます。おそらくWindows.TerminateThread、ユニットのファイナライズ中に呼び出すと、それが修正されるはずです。

4

3 に答える 3

6

唯一の希望は、スレッドを作成し、そのスレッドから DLL をロードすることです。したがって、できるだけ明確にするために、スレッドを作成し、そのスレッド内で実行されているコードから呼び出しLoadLibraryて DLL をロードします。

VCL は、DLL をロードするスレッドから実行する必要があります。VCL の初期化は DLL の初期化中に行われ、どのスレッドが VCL メイン スレッドであるかが決定されます。VCL メイン スレッドは、VCL を初期化するスレッドであり、DLL をロードするスレッドです。

1 つのプロセスで 2 つの GUI スレッド、2 つのメッセージ ポンプを使用するため、このアプローチ全体について明確な頭を保つ必要があります。モーダル ウィンドウを表示するには、両方の GUI スレッドでウィンドウを無効にする必要があります。

この一般的なアプローチ (同じプロセスに 2 つの GUI スレッドがあり、そのうちの 1 つは 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
于 2013-09-21T11:43:38.493 に答える
1

Remy Lebeau による EDN からの回答:

DLL には、メイン アプリのコピーとは別の VCL と RTL の自己完結型のコピーがあります。ほとんどの場合、この種の DLL 内でのスレッド化された使用法は通常問題ありませんが、スレッドが定期的に呼び出さない限り、「メイン」スレッドの変数を手動で更新しない限り、TThread.Synchronize()や などのメインスレッドに依存する機能は機能しません。 (これは通常、 /操作が実行されたときに「メイン」スレッドを「ウェイクアップ」すると自動的に呼び出されます)。TThread.Queue()System.MainThreadIDThreadIDCheckSynchronize()TThreadSynchronizeQueue

手動で調整することが安全かどうかはわかりませんSystem.MainThreadIDが、ここでの主な質問に対する答えは「一般的には問題ありません」です。

于 2013-09-26T02:28:18.807 に答える
-1

ああ、私は自分の質問に答えています。

そう、

VCL はこの方法での実行を完全にサポートしていますか?

そうらしい。ここで得たものから、VCL コードは「現在の」スレッドで実行されるだけで、別のスレッドがあるかどうか、またはこの「現在の」スレッドがプロセスのメイン スレッドであるか、同じバイナリで作成されているかを認識しません。このスレッドが他のスレッドを混乱させない限り、すべてがうまくいきます。

TThread.Synchronize (Proc: TThreadProc) はどのスレッドにメッセージを送信しますか?

実験によると、メッセージは VCL スレッドではなく、プロセスのメイン スレッドに送信されます (当然、Application.Run動作するスレッドです)。VCL スレッドと同期するには、メッセージを VCL フォームに明示的に送信する必要があります。ここでは ( LuaDebugger/USyncForm.pas )、同期コードへのポインターを保持する WParam とオプションのvoid*引数を保持する LParam を使用したカスタム UM_MethodCall です。

于 2013-09-25T18:34:57.027 に答える