メイン 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 ========================
ありがとう、ロバート