最近修了一个Bug, 发现在.net 框架下, 消息处理的函数混乱, 导致一些不可预知的错误产生,比如当一个Datagrid view 的cell还在编辑的过程中的时候, 其他的消息处理函数对这个cell 进行了重新写内容的操作,会造成程序的Crash. The exception is "Operation is not valid because it results in a reentrant call to the SetCurrentCellAddressCore function." 如何解决这个问题呢, 给出了我的看法。
下面首先对这个问题进行描述。
有两个List Control, 分别为A和B, 还有一个datagridview C, 他们之间的响应关系如下:
1)当选中A的一个item时, 会更新B和C的内容, 并使选B和C选中的内容为Highlight状态
2)当选中B或C的一个item时, 会更新其他两个的选中状态并更新内容。
3)当从C中更新数据时, A中相应的内容也会更改。并能够根据C中当前的选中状态更新A中的选中状态。
本来这个问题并不复杂, 但是响应消息偏偏都用了SelectionChange来处理消息, 这个问题就复杂多了。我们假设这三个消息处理函数为Afunc(), Bfunc(), Cfunc(). 看下面的例子:
当A更改选中的状态的时候, 消息是怎么走的呢?
1)响应Afunc(), 在Afunc中找到响应的B的对应的数据,并使其选中的状态改变。
2)由于B的选中状态已经改变, Windows响应了Bfunc(), 此时程序已经进入Bfunc()来处理,注意此时第一次进入Afunc()还没有走完。
3)进入Bfunc(), 在Bfunc()中,根据当前的状态更改A的选中状态, 这是Windows响应了Afunc(), 这时Bfunc()还没有走完
4)进入Afunc(), 更改B的选中状态, 由于现在B的选中状态和前面相同, 所以不会再次触发Bfunc().
5) Afunc()走完, 返回Bfunc().
6) Bfunc()走完,返回到Afunc().
7) Afunc()走完, 返回到Afunc().
复杂不? 也不知道一开始是谁写的,不会用这个久别用这个消息处理函数阿。真郁闷!!!!
解决方法:
1)把所有selectionChange的消息的响应,全部改为对click消息的响应,这样就可以彻底把对用户的消息响应和程序中消息更改彻底分开了。这样就不会产生如上的ugly 的处理过程了。
2)没有更好的了。就用上面的吧
总结:在处理控件的消息过程中,一定要注意对消息响应类型的选择,否则会造成灾难性的后果。把用户输入的消息和程序中对控件的消息彻底分开,这样才能保证消息处理函数的原子性,不会产生其他不可控的结果。