4

[編集して追加:答えは非常に退屈で、Win32やダイアログボックスなどとは何の関係もありません。コードにばかげたバグがありました。それを見つけてくれたHansPassantに感謝します。]

(これはちょっと長いです。エグゼクティブサマリー:通知領域にアイコンを作成し、メインウィンドウを表示せず、通知を右クリックして表示できる「バージョン情報」ボックスがある単純なWin32アプリケーションがあります-エリアアイコン。理由もなく、「バージョン情報」ボックスが表示されてから閉じられると、アプリケーションのメインメッセージループが終了メッセージを受け取り、終了します。これを引き起こすために何を間違えたのでしょうか?)


私は、通知領域(「システムトレイ」)に配置され、バックグラウンドでさまざまな関連性のない処理を実行する小さなプログラムを作成しています。そのUIはほとんど些細なものです。通知領域のアイコンを右クリックすると、[終了]と[バージョン情報]のオプションが付いたメニューが表示されます。前者は終了し、後者はこのプログラムについてのモーダルダイアログを少しポップアップします。

アプリケーションはC++で記述されており、Win32を直接使用します(MFCなどは使用しません)。石器時代にとらわれてしまったことをお詫びします。

唯一の問題はこれです:「About」ダイアログが閉じられると、プログラムは終了します! これを引き起こしている可能性がありますか?

これを理解するのにどのような詳細情報が最も役立つかわかりません。ここにいくつかの観察があります。

  • ダイアログの[OK]ボタンがクリックされた後の、アプリケーションの寿命が尽きたときのWindowsメッセージのシーケンスは次のとおりです。
    • ダイアログボックスのプロシージャはWM_CTLCOLORBTNを取得します
    • アプリケーションの(非表示の)メインウィンドウはWM_ENABLE(TRUE)を取得します。
    • ダイアログボックスは、WM_CTLCOLORBTN、WM_IMESETCONTEXT、WM_SETFOCUS、WM_WINDOWPOSCHANGING、WM_WINDOWPOSCHANGED、3xWM_GETICON、WM_NCACTIVATE、2xWM_GETICON、WM_ACTIVATE、WM_WINDOWPOSCHANGINGを取得します。
    • アプリケーションのウィンドウは、WM_WINDOWPOSCHANGING、WM_NCACTIVATE、メッセージ0x93、0x93、0x91、0x92、0x92(これらは何ですか?)、WM_ACTIVATEです。
    • ダイアログボックスにWM_KILLFOCUS、WM_IME_SETCONTEXTが表示されます。
    • アプリケーションのウィンドウはWM_IME_SETCONTEXTを取得します。
    • ダイアログボックスはWM_IME_NOTIFYを取得します。アプリケーションのメインウィンドウも同様です。
    • アプリケーションのウィンドウはWM_SETFOCUSを取得します。
    • ダイアログボックスにメッセージ0x90、WM_DESTROY、WM_NCDESTROYが表示されます。
  • これらの後に、ダイアログウィンドウのprocまたはメインアプリケーションウィンドウのいずれかにそれ以上のメッセージはありません。
  • 次に、GetMessageはメインメッセージループ(メッセージはWM_QUIT)で0を返し、すべて終了します。
  • 私のコードでのPostQuitMessageの唯一の呼び出しは、メインウィンドウのWndProcであり、メインウィンドウがWM_DESTROYを取得したときに発生しますが、このシナリオでは実際には呼び出されません。

おそらく、私のコードには何かおかしなものや欠けているものがあります。ここにいくつかの抜粋があります(簡潔にするために、関連性がないかもしれないいくつかの詳細が省略されています)。

WinMainの大まかな構造は次のとおりです。

WNDCLASSEX wc;
// fill in fields of wc
RegisterClassEx(&wc);

HWND w = CreateWindow(...);

NOTIFYICONDATA nid;
memset(&nid, 0, sizeof(nid));
// fill in fields of nid
Shell_NotifyIcon(NIM_ADD, &nid);

// (start a background thread to do the real work,
// which is of no interest here)

MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
  if (TranslateAccelerator(msg,hwnd, accel, &msg)) continue;
  TranslateMesage(&msg);
  DispatchMessage(&msg);
}

Shell_NotifyIcon(NUM_DELETE, &nid);
return (int)msg.wParam;

メインウィンドウのWndProcは次のようになります。

switch (message) {
  case WM_USER_SHELLICON: // my own, attached to the icon's menu
    if (LOWORD(lParam) == WM_RBUTTONDOWN) // ... create menu and return TRUE
    break;
  case WM_COMMAND:
    // menu item
    switch (LOWORD(wParam)) {
      case IDM_ABOUT:
        DialogBox(the_instance, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, AboutProc);
        break;
      case IDM_EXIT:
        DestroyWindow(hWnd);
        break;
      default: return DefWindowProc(hWnd, message, wParam, lParam);
    }
  case WM_DESTROY:
    PostQuitMessage(0);
    break;
  default: return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;

ダイアログボックスのプロシージャは次のようになります。

switch (message) {
  case WM_INITDIALOG:
    // fill in a version string
    return (INT_PTR)TRUE;
  case WM_COMMAND:
    if (LOWORD(wParam)==IDOK || LOWORD(wParam)==IDCANCEL) {
      EndDialog(hDlg, LOWORD(wParam));
      return (INT_PTR)TRUE;
    }
    break;
}
return (INT_PTR)FALSE;
4

1 に答える 1

4

WndProc()関数にバグがあります。WM_COMMANDケースにブレークがありません。したがって、たとえばIDM_ABOUTを実行すると、WM_DESTROYの場合に分類されます。さよなら。

PC-lintをお勧めします。

于 2012-04-17T19:33:21.760 に答える