C#程序调用Form.Show报“创建窗口句柄出错”

1、 1、发现问题

程序主窗口(称为A)是一个Mdi窗口,先打开一个子窗口B,将其最大化,然后再从子窗口B中打开一个子窗口C,C的父窗口是A,将C最大化,然后调用C.Show()。这时候报错,错误信息为:创建窗口句柄时出错。

      将程序运行在调试环境中,查看更详细的错误信息,错误截图如下:

  

    调用堆栈信息如下:

  

      2、 寻找原因1

我们的代码都是由SVN进行管理的,通过查找提交记录,发现出问题之前,最后一次提交的改动是B窗口中添加了DEV的布局控件LayoutControl,加了这个控件之后,就会出现第一节中的问题。将这个布局控件去掉之后,就不会出现报错信息了。但是这两者之间到底有什么关系,还是一头雾水。

3、 尝试的解决方法1

进行测试,发现如果B窗口不设置MdiParent属性,就可以正常打开。

打开C窗口之前,设置C窗口的Parent,然后最大化打开C窗口,这种方式打开C窗口不会报错,但是这样打开C窗口,再打开其它窗口,C窗口一直在主窗口的最前面,影响与用户交互。

4、 尝试的解决方法2

再次进行测试,发现如果B窗口是正常大小(WindowState=Normal),最大化打开C窗口就不会报错。

于是打开C窗口之前,首先枚举A窗口的MdiChild,将每个子窗口都设置为正常大小,再最大化打开C窗口,这种方式打开C窗口也不会报错。但是这样打开C窗口前,整个程序会有一个卡顿(其它窗口重置为正常大小需要个过程),用户感觉不好。

5、 尝试的解决方法3

同事帮着进行测试,发现如果B窗口显示之后,调整一下窗口大小,再最大化打开C窗口就不会报错。判断是DEV的布局控件LayoutControl有些内容没有初始化,于是查找LayoutControl提供的公共函数,看是否有关于初始化的函数。最终发现了CreateControl方法,该方法介绍如下:

      于是在B窗口的构造函数中调用LayoutControl的CreateControl函数,然后等B窗口显示后,再最大化打开C窗口,问题解决。

PS:必须要在B窗口的构造函数中调用该函数,在窗口的Shown事件中调用就不行,估计是在构造函数中调用时初始化了一些额外的信息。

6、 寻找原因2

报错是在C窗口的Show函数中抛出的。结合函数调用堆栈,分析.Net Framework的源代码。

Form类继承自Control,Control.Show函数如下:

public void Show() {

            Visible = true;

        }

Control.Visible代码如下:

public bool Visible {

            get {

                return GetVisibleCore();

            }

            set {

                SetVisibleCore(value);

            }

        }

Visible调用SetVisibleCore函数,该函数是一个可重写函数,在Form类型进行了重写。重写之后的代码如下: 

SetVisibleCore函数的输入参数值为true,通过查看函数逻辑,发现函数经过红圈圈起来的位置,并调用了Form的Handle属性。Form类的Handle属性继承自Control,所以查看Control的Handle属性。

如果Control的Handle属性没有创建,则调用CreateHandle函数创建Handle,该函数在Form类中进行了重写,代码如下:

       通过查看出错时的调用堆栈,在FormCreateHandle中调用了ControlCreateHandleControlCreateHandle代码如下:

      函数最后调用了一句window.Createhandle(cp)window的声明为:

      private ControlNativeWindow           window;

       ControlNativeWindow类继承自NativeWindowNativeWindow中的CreateHandle函数如下:

    

在这里可以发现两处代码与我们的问题相关:1)函数调用了CreateWindowEx来窗口窗口,就是这个函数调用抛得异常;2)CreateHandle函数捕获了异常,然后又抛了一个OutOfMemoryException异常,也就是我们调用Form.Show时捕获的异常。

 CreateWindowEx函数是在UnsafeNativeMethods类中定义的,其实就是封装的User32中的CreateWindowEx函数。

至此函数调用关系分析完毕,由于电脑编译不通过.Net Framework源代码,也没法动态调试程序,所以无法看到底是哪些参数导致的NullReference异常。

至此整个问题分析完毕,由于能力有限,不能得到最终出错原因,比较遗憾。

 

 

 

 

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值