今天一位同事找我看了一个非常有意思的BUG,在一个弹出的非模态窗体上面,按下【Ctrl + S】,弹出保存文件窗体,点击取消好,报出0x80808080地址访问错误。但是程序中没有停下断点。
先看这个地址很有意思吧?这么有规律的,绝对很有来头,不过这个BUG先不讲这个。
这个BUG还是很容易跟踪下来的。不过说是容易,也费了不少劲,因为消息循环有其特有的特征,在WndProc里是不能随便下断点的。那如何跟踪呢?
有人想到ExceptionLog,加入异常机制。这反而麻烦了。其实就是慢慢跟踪代码,进行简单分析。基本就能定位到了。
原来,在弹出的窗体中的消息处理中,响应完消息后,会调用自己定义的消息回调事件,如下:
inherited WnProc(Msg);
If Assigned(FOnMessage) then FOnMessage(Self, Msg);
第一行代码,在基类中,会自动调用全局消息处理,关键是,这位同事的系统中,在保存的时候,释放掉了一些窗体,包括当前弹出的窗体。
也就是说,第一行代码执行完成之后,当前对象实例就已经被释放了。所以第二句话,就会出现地址访问错误了。
那么,如何解决这个问题呢?这个问题与一般的不太一样,因为他涉及到了消息循环。因此我的建议是,将保存事务做完之后,再调用释放窗体的操作,且该操作作为一个消息处理,排到消息队列中。即先声明一个自定义消息函数:
procedure DoSomeEvent(var Message); message GM_SelfMessage;
并且在保存完成之后通过PostMessage的方式,调用该函数。这样就能保证WndProc函数处理完成之后,才会调用到窗体释放的函数。这个错误就应该能够避免了。