为什么我的属性表(从系统托盘图标显示)会锁定任务栏?

发布于 2024-08-06 09:43:24 字数 2095 浏览 10 评论 0原文

注意:代码示例已被简化,但整体结构保持不变。

我正在开发一个 Win32 应用程序,其主界面是系统托盘图标。我创建一个虚拟窗口,使用 HWND_MESSAGE 作为其父窗口,以接收图标的消息:

WNDCLASSEX wndClass;
wndClass.lpfnWndProc = &iconWindowProc;
// ...
iconWindowHandle = CreateWindow(wndClass.lpszClassName, _T(""), 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, GetModuleHandle(NULL), 0);

然后创建图标,引用此仅消息窗口:

NOTIFYICONDATA iconData;
iconData.hWnd = iconWindowHandle;
iconData.uCallbackMessage = TRAYICON_MESSAGE;
// ...
Shell_NotifyIcon(NIM_ADD, &iconData)

双击托盘图标时,我创建并显示属性表(来自comctl32.dll):

LRESULT CALLBACK iconWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  switch (uMsg) {
    case TRAYICON_MESSAGE:
      switch (lParam) { // that contains the "real" message
        case WM_LBUTTONDBLCLK:
          showPropertySheet();
          return 0;
        // ...
      }
      break;
    // ...
  }
  return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

属性表没有父窗口。 PropertySheet 函数是从纯消息窗口的窗口过程中调用的。未设置 PSH_MODELESS 标志;因此,PropertySheet 仅在属性表窗口再次关闭后返回:

void showPropertySheet() {
  PROPSHEETPAGE pages[NUM_PAGES];
  pages[0].pfnDlgProc = &firstPageDialogProc;
  // ...
  PROPSHEETHEADER header;
  header.hwndParent = NULL;
  header.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK;
  header.ppsp = pages;
  // ...
  PropertySheet(&header);
}

现在所有这些都工作得很好,直到我在属性表页面之一的对话框过程中设置断点:

BOOL CALLBACK firstPageDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  return FALSE; // breakpoint here
}

当程序停止于断点,整个任务栏锁定

调用堆栈毫无用处;它表明对话框过程是通过user32.dll内部的一些调用从comctl32.dll内部的某个地方调用的。我自己的窗口过程没有介于两者之间。

使属性表成为无模式似乎没有帮助。另外,我不想这样做,因为它会使代码更加复杂。

只要我的对话框程序返回得足够快,这就不成问题。但奇怪的是,对话框过程中较长的操作不仅会锁定对话框本身,还会锁定整个 shell。我可以想象,仅消息窗口过程有能力导致此行为,因为它与托盘图标更密切相关......但此函数未显示在调用堆栈上。

我是否在做一些根本性错误的事情?谁能解释一下这个问题?

Note: code samples have been simplified, but the overall structure remains intact.

I am working on a Win32 application whose main interface is a system tray icon. I create a dummy window, using HWND_MESSAGE as its parent, to receive the icon's messages:

WNDCLASSEX wndClass;
wndClass.lpfnWndProc = &iconWindowProc;
// ...
iconWindowHandle = CreateWindow(wndClass.lpszClassName, _T(""), 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, GetModuleHandle(NULL), 0);

Then the icon is created, referring to this message-only window:

NOTIFYICONDATA iconData;
iconData.hWnd = iconWindowHandle;
iconData.uCallbackMessage = TRAYICON_MESSAGE;
// ...
Shell_NotifyIcon(NIM_ADD, &iconData)

When the tray icon is double-clicked, I create and show a property sheet (from comctl32.dll):

LRESULT CALLBACK iconWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  switch (uMsg) {
    case TRAYICON_MESSAGE:
      switch (lParam) { // that contains the "real" message
        case WM_LBUTTONDBLCLK:
          showPropertySheet();
          return 0;
        // ...
      }
      break;
    // ...
  }
  return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

The property sheet has no parent window. The PropertySheet function is called from the window procedure of the message-only window. The PSH_MODELESS flag is not set; thus, PropertySheet only returns after the property sheet window is closed again:

void showPropertySheet() {
  PROPSHEETPAGE pages[NUM_PAGES];
  pages[0].pfnDlgProc = &firstPageDialogProc;
  // ...
  PROPSHEETHEADER header;
  header.hwndParent = NULL;
  header.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK;
  header.ppsp = pages;
  // ...
  PropertySheet(&header);
}

Now all this works just fine, until I set a breakpoint inside the dialog procedure of one of the property sheet's pages:

BOOL CALLBACK firstPageDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  return FALSE; // breakpoint here
}

When the program stops on the breakpoint, the entire taskbar locks up!

The call stack is quite useless; it shows that the dialog procedure is called from somewhere inside comctl32.dll, via some calls inside user32.dll. No window procedure of my own is in between.

Making the property sheet modeless doesn't seem to help. Also, I'd rather not do this because it makes the code more complex.

As long as my dialog procedure returns quickly enough, this shouldn't be a problem. But it seems so weird that a longer operation inside the dialog procedure would not only lock up the dialog itself, but the entire shell. I can imagine that the message-only window procedure has the power to cause this behaviour, since it's more closely related to the tray icon... but this function is not shown on the call stack.

Am I doing something fundamentally wrong? Can anyone shed some light on this issue?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

乄_柒ぐ汐 2024-08-13 09:43:25

事实上,这是相当明显的,而混乱一定是由于缺少咖啡造成的。

任务栏可能使用 SendMessage 将消息发送到我的应用程序,这会导致它阻塞,直到消息得到处理。 SendMessageTimeout 显然没有被使用。

我仍然觉得奇怪,我自己的函数没有出现在调用堆栈上。当然,这样的消息必须流经我的消息循环才能被处理?也许“该行下方的堆栈帧可能不完整或丢失”的警告实际上是正确的。

Actually, it's rather obvious, and the confusion must have been due to a lack of coffee.

The taskbar probably uses SendMessage to send the message to my application, which causes it to block until the message is handled. SendMessageTimeout is apparently not used.

I still think it's strange that no function of my own shows up on the call stack. Surely, such a message must flow through my message loop in order to be processed? Maybe the warning that "stack frames below this line may be incomplete or missing" was actually right, then.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文