J'ai eu le code qui a bien fonctionné lors de l'exécution dans le contexte du thread VCL principal. Ce code lui a alloué WndProc() afin de gérer les appels SendMessage(). J'essaie maintenant de le déplacer vers un thread d'arrière-plan car je crains que le trafic SendMessage() affecte négativement le thread VCL principal. J'ai donc créé un thread de travail dans le seul but d'allouer le WndProc() dans sa méthode thread Execute() pour s'assurer que WndProc() existait dans le contexte d'exécution du thread. Le WndProc() gère les appels SendMessage() à mesure qu'ils arrivent. Le problème est que la méthode WndProc() du thread de travail n'est jamais déclenchée.Delphi - WndProc() dans le thread n'a jamais appelé
Notez que doExecute() fait partie d'une méthode de modèle appelée par ma classe TThreadExtended qui est un descendant de TThread de Delphi. TThreadExtended implémente la méthode Thread Execute() et appelle doExecute() dans une boucle. J'ai triplé et doExecute() est appelé à plusieurs reprises. Notez également que j'appelle PeekMessage() juste après avoir créé le WndProc() afin de s'assurer que Windows crée une file d'attente de messages pour le thread. Cependant, quelque chose que je fais est faux puisque la méthode WndProc() n'est jamais déclenchée. Voici le code ci-dessous:
// ========= 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 ========================
Merci, Robert
Merci mghie. L'appel TranslateMessage() est-il nécessaire puisque je ne gère pas les clés virtuelles? Ou est juste "bonne pratique"? –
@Robert: Je ne pense pas que ce soit nécessaire, mais je n'ai jamais changé ce bloc de code car c'est tellement idiomatique. Mais si vous contrôlez quels messages sont envoyés ou postés, alors il devrait être prudent de le laisser de côté. Personnellement, je le garderais, ne serait-ce que pour l'aspect familier du code. – mghie