哪些窗口事件会影响 MDIGetActive()?导致 OnMDIActivate() 的事件不是唯一的事件

What windowing events affect MDIGetActive()? The event leading to OnMDIActivate() isn't the only one

我正在尝试缓存 MDI 应用程序的活动子 window,因为分析表明不断调用 MDIGetActive() 是一个热点,更改结构以避免它可能很困难。

我有一个 child_frame class 继承自 CMDIChildWnd,我的 OnMDIActivate() 函数基本上如下所示:

afx_msg void child_frame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
{
   if(bActivate) {
      // Cache the new active child window.
      main_frame *parent = dynamic_cast<main_frame>(this->GetMDIFrame());
      parent->set_cached_active_child(pActivateWnd);
   }
   // Do application-specific activation/deactivation stuff
   // ...
   // ...
   // ...
   // Run the base class event handler
   CMDIChildWnd::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
}

我已经使用断言在 OnMDIActivate() 中的每个点针对 parent->MDIGetActive() 测试了缓存的子级,并且它在开始、中间、结束等处始终正确。我还在child_frame::OnActivate()、main_frame::OnMDIActivate() 和 main_frame::OnActivate() 中的每个点,缓存的结果在那里也总是正确的……所以更新缓存的结果没有必要也没有用那些特定处理程序中的指针。

不幸的是,当我在实际的应用程序代码中检索它时,缓存的结果有时与真正的 MDIGetActive() 结果不同。这似乎表明我缺少一些活动 MDI window 可以更改的事件处理程序,除了 OnMDIActivate()。

那么...究竟有哪些事件可以改变激活的MDI window?

编辑: 至少其中一部分看起来与线程相关。根据 this thread,来自 MDIGetActive() 的 CWnd* 跨线程不可靠。在我的例子中,MDIGetActive() 在生成工作线程的函数中返回一个有效的 CWnd* 并在工作线程中返回一个 nullptr,并且没有调用明显相关的事件处理程序。如果这是唯一的 exception/problem,我会重新发布它作为答案。

CWnd* 跨线程无效,因此当您从除主线程之外的任何线程中的 GetMDIActive() 时,Windows 是谎言并且 returns 是 nullptr。由于线程变化不发送任何WM_ACTIVATE、WM_MDIACTIVATE等消息,它们不会触发OnActivate()、OnMDIActivate()等

至少有两种可能的解决方法,但我选择了第一种,因为它很简单并且符合我的用例:

  1. 如果您生成一堆工作线程来执行相同的函数调用,并且您永远不会在主线程上查找缓存的活动子帧,至少在所有这些线程重新加入之后,您可以缓存一个nullptr 在每个工作线程的开头,并在它们重新加入后重新缓存真正的 MDIGetActive() 值。在此期间主线程会继续pump messages,缓存的值在这个过程中会不正确,但是如果主线程中没有任何东西真正使用它也没关系。
  2. 否则,您可以找到一种方法为缓存的子帧使用线程本地存储并将其初始化为 nullptr,并确保在调用 OnMDIActivate() 时仅更新适当的线程本地缓存值(on主线程)。根据您的程序结构,这可能会很棘手,但它应该更健壮并且与更多线程模式兼容。

最后,缓存子框架将相关代码从大约 8% 的运行时间减少到大约 0.02%。