2010-04-10 4 views
3

주 VCL 스레드의 컨텍스트에서 실행할 때 잘 작동하는 코드가 있습니다. 이 코드는 SendMessage() 호출을 처리하기 위해 자체 WndProc()을 할당했습니다. SendMessage() 트래픽이 주 VCL 스레드에 부정적인 영향을 미치기 때문에 백그라운드 스레드로 이동하려고합니다. 그래서 WndProc() 스레드의 Execute() 메서드에서 스레드의 실행 컨텍스트에서 WndProc() 보장하기 위해 할당 할 목적으로 작업자 스레드를 만들었습니다. WndProc() 들어오는 SendMessage() 호출을 처리합니다. 문제는 작업자 스레드의 WndProc() 메서드는 결코 트리거됩니다.Delphi - 스레드에서 WndProc()을 호출하지 않았습니다

참고 doExecute()는 Delphi의 TThread의 자손 인 TThreadExtended 클래스에서 호출하는 템플릿 메서드의 일부입니다. TThreadExtended는 스레드 Execute() 메서드를 구현하고 루프에서 doExecute()를 호출합니다. 나는 트리플 체크하고 doExecute()가 반복적으로 호출되고있다. 또한 Windows가 스레드의 메시지 대기열을 생성하는지 확인하기 위해 WndProc()을 생성 한 직후 PeekMessage()를 호출합니다. 그러나 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 ======================== 

덕분에, 로버트

답변

6

문제는 당신이 메시지를 수신 할 수있는 창을 만들 수 있다는 것입니다,하지만 당신은 실제로 메시지 큐에서 메시지를 검색 할 수있는 표준 메시지 루프가 없습니다 대상 창이 그들을 처리합니다.

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

필요합니다 스레드 코드에서이 (유사하거나 뭔가를) 수행 : 당신이 필요로하는 자사의 API 형태처럼 보이는 Application 메시지 루프의 것과 동일합니다.

스레드가 메시지 대기열 자체를 가질 수 있으므로 실제로는 도우미 창에 도우미 창이 필요하지 않습니다. 메시지는 대기열에 넣을 수 있으므로 PostThreadMessage()을 호출하십시오. 이것은 표준 PostMessage() 함수 호출과 동일합니다. 둘 다 메시지 처리를 기다리지 않고 즉시 반환합니다. 그게 당신을 위해 작동하지 않는다면, 당신은 실제로 당신의 스레드에 창을 만들고 그것에 대해 SendMessage()를 호출해야합니다. 그러나 메시지 루프는 모든 경우에 필요합니다.

GetMessage()은 블로킹 콜이므로 "CPU 호이닝"을 두려워 할 필요가 없으므로 Sleep() 콜은 필요하지 않습니다.

+0

감사합니다. mghie. 가상 키를 처리하지 않으므로 TranslateMessage() 호출이 필요합니까? 아니면 "좋은 습관"일까요? –

+0

@ 로버트 : 필자는 그것이 필요하다고 생각하지 않지만, 그렇게 관용적이어서 코드 블록을 변경 한 적이 없다. 그러나 어떤 메시지를 보내거나 게시할지 여부를 결정하면 메시지를 남겨 두는 것이 안전해야합니다. 익숙한 코드의 경우에만 개인적으로 보관합니다. – mghie

관련 문제