将托管控制托管为 CWnd 时应用程序挂起
Posted
技术标签:
【中文标题】将托管控制托管为 CWnd 时应用程序挂起【英文标题】:Application hangs when hosting managed control as CWnd 【发布时间】:2019-02-25 15:22:02 【问题描述】:我的应用程序具有基于 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()。显然这是一个荒谬的技巧,但只要我在每次用户打开连击等时暂停聚焦(否则它们每秒都会关闭),它就可以工作。
我做错了什么或者什么可能导致这种有些不可预测的行为?
【问题讨论】:
【参考方案1】:我不知道为什么或它与任何事情有什么关系,但应用程序挂起不知何故源自 WM_ACTIVATE。所以解决方案是在主 CDialog 覆盖 WINPROC 并阻止该消息的转发。从那时起,一切都在正常工作。
我不会将此标记为答案,因为我不知道为什么此解决方案有效。
【讨论】:
以上是关于将托管控制托管为 CWnd 时应用程序挂起的主要内容,如果未能解决你的问题,请参考以下文章
错误异常大全:正尝试在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。
错误异常大全:正尝试在 OS 加载程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内运行托管代码,这样做会导致应用程序挂起。