3

メイン VCL スレッドのコンテキストで実行すると、問題なく動作するコードがありました。このコードは、SendMessage() 呼び出しを処理するために独自の WndProc() を割り当てました。SendMessage() トラフィックがメインの VCL スレッドに悪影響を及ぼしているのではないかと懸念しているため、バックグラウンド スレッドに移動しようとしています。そこで、スレッドの Execute() メソッドに WndProc() を割り当てて、スレッドの実行コンテキストに WndProc() が確実に存在するようにすることのみを目的として、ワーカー スレッドを作成しました。WndProc() は、受信した SendMessage() 呼び出しを処理します。問題は、ワーカー スレッドの WndProc() メソッドがトリガーされないことです。

doExecute() は、Delphi の TThread の子孫である私の TThreadExtended クラスによって呼び出されるテンプレート メソッドの一部であることに注意してください。TThreadExtended は、スレッドの Execute() メソッドを実装し、ループ内で doExecute() を呼び出します。トリプルチェックしたところ、doExecute() が繰り返し呼び出されています。また、WndProc() を作成した直後に PeekMessage() を呼び出して、Windows がスレッドのメッセージ キューを確実に作成するようにしていることにも注意してください。ただし、WndProc() メソッドがトリガーされないため、私が行っていることは間違っています。以下にコードを示します。

// ========= BEGIN: CLASS - TWorkerThread ========================

constructor TWorkerThread.Create;
begin
    FWndProcHandle := 0;

    inherited Create(false);
end;

// ---------------------------------------------------------------

// This call is the thread's Execute() method.
procedure TWorkerThread.doExecute;
var
    Msg: TMsg;
begin
    // Create the WndProc() in our thread's context.
    if FWndProcHandle = 0 then
    begin
        FWndProcHandle := AllocateHWND(WndProc);

        // Call PeekMessage() to make sure we have a window queue.
        PeekMessage(Msg, FWndProcHandle, 0, 0, PM_NOREMOVE);
    end;

    if Self.Terminated then
    begin
        // Get rid of the WndProc().
        myDeallocateHWnd(FWndProcHandle);
    end;

    // Sleep a bit to avoid hogging the CPU.
    Sleep(5);
end;

// ---------------------------------------------------------------

procedure TWorkerThread.WndProc(Var Msg: TMessage);
begin
    // THIS CODE IS NEVER CALLED.
    try
        if Msg.Msg = WM_COPYDATA then
        begin
            // Is LParam assigned?
            if (Msg.LParam > 0) then
            begin
                // Yes.  Treat it as a copy data structure.
                with PCopyDataStruct(Msg.LParam)^ do
                begin
      ... // Here is where I do my work.
                end;
            end; // if Assigned(Msg.LParam) then
        end; // if Msg.Msg = WM_COPYDATA then
    finally
        Msg.Result := 1;
    end; // try()
end;

// ---------------------------------------------------------------

procedure TWorkerThread.myDeallocateHWnd(Wnd: HWND);
var
    Instance: Pointer;
begin
    Instance := Pointer(GetWindowLong(Wnd, GWL_WNDPROC));

    if Instance <> @DefWindowProc then
    begin
        // Restore the default windows procedure before freeing memory.
        SetWindowLong(Wnd, GWL_WNDPROC, Longint(@DefWindowProc));
        FreeObjectInstance(Instance);
    end;

    DestroyWindow(Wnd);
end;

// ---------------------------------------------------------------


// ========= END  : CLASS - TWorkerThread ========================

ありがとう、ロバート

4

1 に答える 1

6

問題は、メッセージを受信するウィンドウを作成しても、実際にメッセージ キューからメッセージを取得してターゲット ウィンドウに処理させるための標準的なメッセージ ループがないことです。必要なのはApplicationメッセージ ループに相当するもので、API 形式では次のようになります。

while integer(GetMessage(Msg, HWND(0), 0, 0)) > 0 do begin
  TranslateMessage(Msg);
  DispatchMessage(Msg);
end;

スレッド コードでこれ (または同様のこと) を行う必要があります。

ワーカー スレッドにヘルパー ウィンドウはまったく必要ないことに注意してください。スレッドはメッセージ キュー自体を持つことができ、メッセージは を呼び出すことでキューに入れることができますPostThreadMessage()。これは、標準のPostMessage()関数呼び出しと同等です。どちらもメッセージが処理されるのを待たず、すぐに戻ります。それがうまくいかない場合は、実際にスレッドにウィンドウを作成して呼び出すSendMessage()必要があります。ただし、メッセージ ループはすべての場合に必要です。

ブロッキング呼び出しであるためGetMessage()、「CPU を占有する」ことを恐れる必要もないため、Sleep()呼び出しは必要ありません。

于 2010-04-10T21:30:46.757 に答える