2010-04-10 5 views
3

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

Répondre

6

Le problème est que vous créez une fenêtre pour recevoir des messages, mais vous avez pas de boucle de message standard pour récupérer effectivement les messages de la file d'attente de messages et laisser la fenêtre cible les gère. Qu'est-ce que vous avez besoin est l'équivalent de la boucle de message Application, qui sous sa forme API ressemble à:

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

Faire cela (ou quelque chose de similaire) dans le code de fil sera nécessaire. Notez que vous n'avez pas du tout vraiment besoin d'une fenêtre d'assistance dans votre thread de travail, car un thread peut lui-même contenir une file d'attente de messages dans laquelle les messages peuvent être placés en file d'attente en appelant PostThreadMessage(). C'est l'équivalent de l'appel de fonction standard PostMessage(). Les deux n'attendront pas que le message soit traité, mais reviendront immédiatement. Si cela ne fonctionne pas pour vous, alors vous devez en effet créer une fenêtre dans votre thread et appelez le SendMessage() pour cela. La boucle de message sera cependant nécessaire dans tous les cas.

Étant donné que GetMessage() est un appel bloquant, vous n'avez pas non plus besoin d'avoir peur de "charger la CPU", les appels Sleep() ne sont donc pas nécessaires.

+0

Merci mghie. L'appel TranslateMessage() est-il nécessaire puisque je ne gère pas les clés virtuelles? Ou est juste "bonne pratique"? –

+0

@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

Questions connexes