首先,对不起我的英语,因为它不是我的母语。
问题出现在我的.NET Windows Forms应用程序中,并且由于某些原因仅在Virtaul PC Windows 7(x64)上重现。
在调整包含大量自定义控件的主应用程序表单时,我在我的应用程序的某个地方遇到了Win32Exception,其中包括两个堆叠到彼此的tabcontrols等... Exception的消息是“创建窗口句柄时出错”。花了一些时间我在Microsoft支持网站上找到了解决该问题的方法。这与我的情况非常相似。来自网站的Qoute:
在.NET应用程序中,如果嵌套控件的深度嵌套,则父级调整大小时有时不会调整大小。在64位和32位平台上都会出现此问题,但在32位系统上需要更深的嵌套级别。
当顶层控件布局时,SetBoundsCore会调用SetWindowPos来调整子控件的大小。每个子控件都会收到一个WM_WINDOWPOSCHANGED通知,该通知会触发一个布局事件,该事件调用调用SetWindowPos的SetBoundsCore。每次调用SetWindowPos进入内核模式,然后退出以传递WM_WINDOWPOSCHANGED通知。最终,线程耗尽内核堆栈空间,SetWindowPos无声地失败。
建议的解决方案是覆盖容器控件的OnSizeChanged方法(如Panels或TabControl(在我的例子中)):
protected override void OnSizeChanged(EventArgs e)
{
if (this.Handle != null)
{
BeginInvoke((MethodInvoker)(() => base.OnSizeChanged(e);));
}
}
我成功地将此解决方案应用于我的案例:创建自定义TabControl(继承自普通winforms TabControl类)并使用我的自定义控件实例更改了TabControl实例。问题消失了!但...
...当我在CCnet构建机器上启动Build时,我在不同的单元测试中有很多Win32Exception-s“创建窗口句柄的错误”,但最有趣的不是代替我有问题的控件(包含自定义TabControl)对象)单元测试!错误情况不同但它们与我的自定义控件无关。当我恢复更改后,一切正常,构建已创建(单元测试成功执行)。
完全让我感到困惑的是,应用更改修复了用户应用程序的可操作性,但在创建构建时导致单元测试失败。
顺便说一句,在本地机器上,所有单元测试都可以在任何情况下执行(有或没有修复)。
我花了很多时间研究这个问题,现在我只有一个假设:ccnet构建机器在一个进程中执行所有单元测试,并且测试不正确地处理测试数据(gdi对象)(在拆解时)错误“创建窗口句柄时出错”通常在达到允许的gdi对象句柄限制(10000)时发生。
您能否分享一下您的专业意见?非常感谢。
答案 0 :(得分:0)
与您的错误没有直接关系,但访问Handle属性将强制创建窗口句柄,有时这是不可能的。不应该像这样执行句柄检查:
if (this.Handle != null)
而是检查IsHandleCreated属性:
if (this.IsHandleCreated)
前一段时间我曾在the blog post上就此问题提到过这个问题,但似乎并未注意到这一点。
答案 1 :(得分:0)
如果在CCNET机器上作为单元测试运行,它可能在非交互式服务上下文中运行,这可能会阻止创建窗口,因为没有人可以查看它们。
答案 2 :(得分:0)
深层嵌套控件遇到了同样的问题。但是,对于正确的解决方案,我不得不覆盖并延迟SetBoundsCore
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
if (this.IsHandleCreated)
{
this.BeginInvoke(
(MethodInvoker)delegate
{
base.SetBoundsCore(x, y, width, height, specified);
});
}
}