1

我的应用程序具有基于 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()。显然这是一个荒谬的技巧,但只要我在每次用户打开连击等时暂停聚焦(否则它们每秒都会关闭),它就可以工作。

我做错了什么或者什么可能导致这种有些不可预测的行为?

4

1 回答 1

0

我不知道为什么或它与任何事情有什么关系,但应用程序挂起不知何故源自 WM_ACTIVATE。所以解决方案是在主 CDialog 覆盖 WINPROC 并阻止该消息的转发。从那时起,一切都正常运行,没有任何问题。

我不会将此标记为答案,因为我不知道为什么此解决方案有效。

于 2020-01-09T19:23:50.010 回答