1

カスタム dll をロードするマルチスレッド アプリケーションがあります。
この dll では、ウィンドウを作成する必要があります。
私は新しいスレッドを作成することでそれを行っており、その中でこのウィンドウを作成しようとしていますが、エラーが発生しました: EInvalidOperation - Canvas does not allow drawing.

ネットで検索したところ、そのスレッド用のカスタム メッセージ ポンプが必要であることがわかりました。
それで、私の質問は、これをどのように適切に行うのですか?

私が今していることは次のとおりです:
- 外部アプリがdllをロードしています - 別のスレッドのこのアプリがdllから関数を
呼び出しているよりも -関数はスレッドを作成します -次のように宣言されています: Init
Init
TMyThread

type
  TMyThread = class(TThread)
  private
    Form: TMyForm;
    FParentHWnd: HWND;
    FRunning: Boolean;
  protected
    procedure Execute; override;
  public
    constructor Create(parent_hwnd: HWND); reintroduce;
  end;

constructor TMyThread.Create(parent_hwnd: HWND);
begin
  inherited Create(False); // run after create
  FreeOnTerminate:=True;
  FParentHWnd:=parent_hwnd;
  FRunning:=False;
end;

procedure TMyThread.Execute;
var
  parent_hwnd: HWND;
  Msg: TMsg;
  XRunning: LongInt;
begin
  if not Terminated then begin
    try
      try
        parent_hwnd:=FParentHWnd;

        Form:=TMyForm.Create(nil); // <-- here is error
        Form.Show;

        FRunning:=True;

        while FRunning do begin
          if PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then begin
            if Msg.Message <> WM_QUIT then
              Application.ProcessMessages
            else
              break;
          end;
          Sleep(1);
          XRunning:=GetProp(parent_hwnd, 'XFormRunning');
          if XRunning = 0 then
            FRunning:=False;
        end;
      except
        HandleException; // madExcept
      end;
    finally
      Terminate;
    end;
  end;
end;

例外EInvalidOperation - Canvas does not allow drawingは、スレッドが既存のメッセージ ポンプ コードに到達する前に発生します。

私は何を間違っていますか、それを機能させる正しい方法は何ですか?
助けてくれてありがとう。


DLL で 2 番目の GUI スレッドを作成するには、標準アプリケーションとまったく同じように行う必要があります。
誰かが私の考えを確認できますか?

DLLbegin...end.セクションでは、次のことを行います。

begin
  Application.CreateForm(THiddenForm, HiddenForm);
  Application.Run;
end.

私がしTMyThread.Executeなければならないことで:

procedure TMyThread.Execute;
begin
  if not Terminated then begin
    try
      try
        Application.CreateForm(TMyForm, Form);

        ???? how to make a thread that has remained in this place until you close this window ???
      except
        HandleException; // madExcept
      end;
    finally
      Terminate;
    end;
  end;
end;

これは正しい方法ですか?そんなに単純なのだろうか?

4

2 に答える 2

2

スレッドでメッセージ キューを実行する最も簡単な方法は次のとおりです。

procedure PerformThreadLoop;
var
  Msg: TMsg;
begin
  while GetMessage(Msg, 0, 0, 0) and not Terminated do begin
    Try
      TranslateMessage(Msg);
      DispatchMessage(Msg);
    Except
      Application.HandleException(Self);
    End;
  end;
end;

そして、スレッドプロシージャでは次のようになります。

procedure TMyThread.Execute
begin
  InitialiseWindows;
  PerformThreadLoop;
end;

とはいえ、あなたが試みていることはうまくいきません。メイン スレッドから離れた VCL コンポーネントを使用しようとしているようです。それは特に許可されていません。VCL のスレッド モデルは、すべての VCL コードがメイン スレッドで実行されることを示しています。メイン スレッドから離れて VCL フォームを作成しようとすると、失敗する運命にあります。


新しいスレッドを作成したいというあなたの意欲に疑問を呈します。Delphi DLL は、DLL をロードして呼び出したスレッドからフォームを実行する場合、VCL フォームを表示できます。そのスレッドから呼び出しShowて、モードレス フォームを表示できます。これは、ウィンドウにメッセージを配信するために、ホスト アプリケーションのメッセージ キューに依存していることを意味します。概して、これを機能させることができます。フォームがモーダルの場合は、単純に呼び出すことができShowModal、フォームは標準の Delphi モーダル メッセージ ループによって処理されます。

したがって、すべての GUI をホスト アプリの GUI スレッドに保持することをお勧めします。DLL が GUI を表示することが期待されており、ホスト アプリの GUI スレッドから離れてそれを行うことも期待されている場合、問題が発生しています。しかし、そうなる可能性は極めて低いと思います。

于 2013-03-06T13:03:29.177 に答える