我的应用程序具有基于 ATL 的 GUI(CWnd、CDialog、...),它由多个页面(CDialog)组成。其中一个页面是空的,但它有一个占位符框架 (CWnd),可随对话框调整大小。一切都构建为 x64。
当页面加载时,它使用 COM 互操作从应用程序的托管 (C#) 端请求控制句柄,并将控件作为从该句柄创建的 CWnd 添加到对话框:
托管实施简化:
// Class "ManagedControlProvider"
private Control myUserControl;
public long CreateControl()
{
myUserControl = /*Create some new inheritant of UserControl */
myUserControl.Dock = DockStyle.Fill;
return myUserControl.Handle.ToInt64();
}
原生端简化:
// Call the managed class. Lifetime of m_pManagedControlProvider
// is ensured elsewhere.
LONGLONG lHandle = m_pManagedControlProvider->CreateControl();
// m_pUserCtrlAsCWnd is CWnd*
m_pUserCtrlAsCWnd = CWnd::FromHandle((HWND)lHandle);
m_pUserCtrlAsCWnd->SetParent(this);
// m_ControlFrame is just a native helper-CWnd the dialog that
// resizes with it a so gives us the size we want to set for the
// managed control. This code is also call in every resize -event.
RECT winRect;
m_ControlFrame.GetWindowRect(&winRect);
ScreenToClient(&winRect);
m_pUserCtrlAsCWnd->SetWindowPos(NULL,
winRect.left, winRect.top, winRect.right - winRect.left,
winRect.bottom - winRect.top, 0);
我已经多次这样做了,它通常完全按原样工作。但有时,就像现在一样,我遇到应用程序挂起,没有任何明确的原因。在我目前的控制下,这似乎发生在焦点设置到其他桌面应用程序后大约 5 秒。
我已经验证问题不在托管控件的生命周期或 GC 中。此外,它在调试构建中是可重现的,因此不应该归咎于优化。当挂起发生时,我可以附加调试器并看到一些 ATL 循环继续运行,但这是我能够在堆栈中看到的唯一一段代码(imo 这表明消息循环以某种方式陷入无限循环而没有与我的代码)。
现在要修复脏东西:我在托管控件中添加了一个单独的线程,该线程每秒在 UI 线程上调用 this.Focus()。显然这是一个荒谬的技巧,但只要我在每次用户打开连击等时暂停聚焦(否则它们每秒都会关闭),它就可以工作。
我做错了什么或者什么可能导致这种有些不可预测的行为?