2010-06-23 46 views
8

我寫了一個win32應用程序。我實現了消息循環我自己是這樣的:Win32:我的應用程序凍結,而用戶調整窗口的大小

 bool programcontinue = true; 
    while(programcontinue) 
    { 
       while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) 
       { 
         TranslateMessage(&Msg); 
         DispatchMessage(&Msg); 
       } 

       IdleProcess(); 
    } 

有一個在我的應用程序中的可調整大小的窗口。通常,IdleProcess()每秒被調用幾次。當用戶抓住可調整大小的窗口的角落或邊緣時,IdleProcess()不會再被調用,直到用戶釋放鼠標按鈕。

這裏會發生什麼?

我試着用if來交換內部,但這並不改變行爲。似乎在調整大小開始時,該消息的處理程序在調整大小完成之前不會返回?

有沒有辦法改變這種情況,並在每秒調整大小几次的過程中調用IdleProcess()?

感謝 馬克

編輯:

我的意思是用,如果是更換內部而:

bool programcontinue = true; 
while(programcontinue) 
{ 
      if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) // <<<< 
      { 
        TranslateMessage(&Msg); 
        DispatchMessage(&Msg); 
      } 

      IdleProcess(); 
} 

我的窗口過程有點漫長,但我得到了相同的行爲與一個小測試應用程序。這與VS Project Wizard創建的wndproc相同:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    int wmId, wmEvent; 
    PAINTSTRUCT ps; 
    HDC hdc; 

    switch (message) 
    { 
    case WM_COMMAND: 
     wmId = LOWORD(wParam); 
     wmEvent = HIWORD(wParam); 
     // Parse the menu selections: 
     switch (wmId) 
     { 
     case IDM_ABOUT: 
      DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); 
      break; 
     case IDM_EXIT: 
      DestroyWindow(hWnd); 
      break; 
     default: 
      return DefWindowProc(hWnd, message, wParam, lParam); 
     } 
     break; 
    case WM_PAINT: 
     hdc = BeginPaint(hWnd, &ps); 
     // TODO: Add any drawing code here... 
     EndPaint(hWnd, &ps); 
     break; 
    case WM_DESTROY: 
     PostQuitMessage(0); 
     break; 
    default: 
     return DefWindowProc(hWnd, message, wParam, lParam); 
    } 
    return 0; 
} 
+0

你可以發佈你的WndProc(...)窗口程序,它將接收來自DispatchMessage()的消息,因爲你的關於改變while的註釋變成if是有點好奇 – 2010-06-23 14:34:06

+0

done +「if」的解釋而不是「而「thingy。 – marc40000 2010-06-23 15:22:13

+0

基於更新,顯然TranslateMessage或DispatchMessage不會立即返回。你的下一個任務是弄清楚哪一個,哪個消息觸發了這個。 – 2010-06-23 15:38:27

回答

14

在窗口上發生了許多模態操作。 Win32 Modal操作是指通過啓動自己的事件處理循環直到模式結束,將應用程序置入「模式」的函數。常見的應用程序模式包括拖放操作,移動/大小操作,只要在應用程序可以繼續之前彈出需要輸入的對話框。

所以發生了什麼是:您的消息循環不運行。 您的窗口收到您傳遞給DefWindowProc的WM_LBUTTONDOWN消息。 DefWindowProc確定用戶正在嘗試調整大小或交互式移動窗口並輸入大小/移動模態函數。該功能位於消息處理循環中,用於監視鼠標消息,以便攔截它們以提供交互式大小調整體驗,並且只有在大小調整操作完成時纔會退出 - 通常由用戶釋放所保存的按鈕或按下轉義鍵。

您會收到通知 - DefWindowProc在進入和退出模態事件處理循環時發送WM_ENTERSIZEMOVE和WM_EXITSIZEMOVE消息。

要繼續生成「空閒」消息,通常在調用模態函數之前創建一個定時器(SetTimer) - 或者在獲取DefWindowProc輸入模態函數的消息時,模態循環將繼續調度WM_TIMER消息。 。並從定時器消息處理程序調用空閒過程。模態函數返回時銷燬計時器。

1

在調整大小的過程中,Windows向您的程序發送了不少消息。我沒有證明這一點,但你描述的行爲是熟悉的。我建議打電話給你的函數IdleProcess()也內而(...)循環的某些事件,比如WM_SIZING您的應用程序將窗口大小調整期間經常收到:

bool programcontinue = true; 
while(programcontinue) 
{ 
      while (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) 
      { 
        TranslateMessage(&Msg); 
        DispatchMessage(&Msg); 
        if(Msg.message == WM_SIZING) 
         IdleProcess(); 
      } 

      IdleProcess(); 
} 

要知道,雖然這個假設IdleProcess()不會創建或使用任何事件。如果情況如此,事情變得更加複雜。

+0

這沒有幫助。正如我在我的問題中所寫的那樣:「我試着用if來交換內在的東西」 – marc40000 2010-06-23 15:17:46

4

當DefWindowProc在wParam中使用SC_MOVE或SC_SIZE處理WM_SYSCOMMAND時,它將進入一個循環,直到用戶通過釋放鼠標按鈕或按下enter或escape來停止循環。它這樣做是因爲它允許程序通過處理WM_PAINT和WM_NCPAINT消息(您應該仍然在Window Procedure中接收這些事件)來呈現客戶區(您的小部件或遊戲或任何繪製的位置)以及邊框和標題區域。

它適用於正常的Windows應用程序,它們通過接收消息在窗口過程中執行大部分處理。它隻影響在窗口過程之外進行處理的程序,例如遊戲(通常是全屏並且不受影響)。

但是,有一種解決方法:自己處理WM_SYSCOMMAND,調整大小或移動自己。這需要很大的努力,但可能證明是值得的。或者,您可以在發送WM_SIZING時使用setjmp/longjmp從窗口過程中退出,或使用Windows Fibre沿着相同的行;儘管這些都是駭人的解決方案。

我在上週末解決了它(使用第一種方法),如果您有興趣我已經將代碼發佈到了sourceforge上的公有領域。只要確保閱讀自述文件,尤其是警告部分。那就是:https://sourceforge.net/projects/win32loopl/

1

您仍然可以收到WM_PAINT消息,你只是愛告訴你想讓它(訥河OpenGL教程看到)的WinAPI的:

windowClass.style   = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Redraws The Window For Any Movement/Resizing 

它仍然會阻止您while/PeekMessage - 雖然! WinAPI直接呼叫您的WndProc