这个问题比较有趣,跟大家分享一下吧。
问题来源于昨天要改的一个bug。其表现大概是这样的:
在钢筋中打开工程3,在对量中对比工程3和工程4。然后在对量中使用“定位到GGJ”的功能,能够正常应用到工程3(审核)。接着在钢筋中将“工程3”另存为“工程31”,这个时候在对量中使用应用到GGJ的时候,系统没有任何反应。既不告诉不能定位,也没弹出定位列表,但是对量软件最小化了。
这说明在钢筋中另存为后,对量使用“定位到GGJ”时,钢筋有响应,但是这个响应有问题。这与我们的直观想象不同,因为我们“定位到GGJ”的原理是首先是通过审核工程名或者送审工程名来找窗体,判断这个工程是否在钢筋软件中打开。那么,一旦另存为之后,我们应该找不到窗体才对。(这里需要注意的是,钢筋中将“工程3”另存为“工程31”之后是相当于改名了,但是对量中还是“工程3”)。
通过跟代码,发现在“另存为…”之前,用“工程3”的名字找到的窗体的句柄是986482,“另存为…”之后,用“工程3”找到的窗体的句柄是983638。
这是什么原因呢?难道是“另存为…”的时候又产生了一个新的窗体?
为了验证一下,看了钢筋软件中”SaveAs”的代码,发现它其实就是将“工程3”改名为“工程31”然后后再复制一份以“工程3”的名字保存。所以这个过程不可能创建新的窗体。
后面想到,既然它是一个窗体,那么我何不用spy++查看一下,看是不是真的存在一个这样的窗体,一查才发现,原来真有一个这样的窗体,但又没有一个这样的窗体。这是为何?真相就在下面的图中。
图1 另存为…之前的图
图2 另存为…之后的图
从上面的图就可以看到,原来是一直被我们忽视的TApplication类在作怪。“另存为…”之后找到的窗体不是我们想要的主窗体而是TApplication的实例。TApplication是Delphi中用来跟windows进行交互的东东,每个delphi编写的程序在运行时都会有TApplication实例Application,而且,它的名字跟主窗体一样,并且修改主窗体的名字不会改变Application的名字,并且它排在MainForm的后面,那么按Caption递归查询子窗体的时候查不到它,但如果“另存为…”之后再按Caption递归查询子窗体的时候就一定会查到它,除非你把程序关了。
找到原因所在,写了一个按照Caption和类名查询窗体的方法,一试,“另存为…”之后再定位时果然给出了“审核工程未打开,是否打开?”的提示。