Delphi - WNDProc () в потоке никогда не звонил
-
26-09-2019 - |
Вопрос
У меня был код, который работал нормально при запуске в контексте основной резьбы VCL. Этот код выделил свой WNDProc () для обработки вызовов SendMessage (). Сейчас я пытаюсь переместить его в фоновую нить, потому что я обеспокоен тем, что трафик SendMessage () отрицательно влияет на основной резьб VCL. Поэтому я создал рабочую нить с единственной целью выделения WNDProc () в своем методе execute () для обеспечения того, чтобы WNDProc () существует в контексте выполнения потока. WNDPROC () обрабатывает вызовы SendMessage (), когда они вошли. Проблема в том, что метод WHEPROC WHEPPROC () рабочего нити никогда не срабатывает.
Примечание, Doexecute () является частью метода шаблона, который называется моим TthreadextExtended классом, который является потомком TTHEAD DELPHI. TTHREADEXTESTEDENDALTELETELETION EXECUTE () метод Execute () и вызывает Doexecute () в цикле. Я тройной проверен и doexecute () называется неоднократно называться. Также обратите внимание, что я звоню Peekmessage () сразу после того, как я создаю WNDProc (), чтобы убедиться, что 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 ========================
Спасибо, Роберт
Решение
Проблема в том, что вы создаете окно для получения сообщений, но у вас нет стандартной циклы сообщений, чтобы фактически извлечь сообщения из очереди сообщений, и пусть целевое окно обрабатывает их. То, что вам нужно, это эквивалент Application
Loop сообщений, который в его форме API выглядит:
while integer(GetMessage(Msg, HWND(0), 0, 0)) > 0 do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
Это (или что-то подобное) в коде потока потребуется.
Обратите внимание, что вам на самом деле не нужна окно вспомогательного ввода в вашу рабочую нить, так как нить может иметь сама очередь сообщений, какие сообщения могут быть включены в очередь, вызывая PostThreadMessage()
. Отказ Это эквивалент стандарта PostMessage()
Функциональный вызов. Оба не будут ждать обработки сообщения, но возвращайте немедленно. Если это не будет работать для вас, то вы действительно должны создать окно в вашей теме и позвонить SendMessage()
для этого. Петля сообщения, однако, будет необходимо во всех случаях.
С GetMessage()
это блокирующий звонок, который вы также не должны бояться «ввода ЦП», поэтому Sleep()
Звонки не нужны.