有一个bug:在XP系统下,某些机器上ShellExecute在打开文件时会造成程序卡死,看了Dump文件,堆栈如下:
说明ShellExecute内部使用了DDE,即Dynamic Data Exchange,动态数据交换技术。
DDE用于进程间通讯,需要两个Windows应用程序,其中一个作为服务器处理信息,另外一个作为客户机从服务器获得信息。客户机应用程序向当前所激活的服务器应用程序发送一条消息请求信息,服务器应用程序根据该信息作出应答,从而实现两个程序之间的数据交换。 为了DDE,程序A(后来卡死的程序)会使用SendMessage广播WM_DDE_INITIATE 消息,其他程序收到WM_DDE_INITIATE 后根据参数决定是否处理该消息,处理的话返回一个WM_DDE_ACK 消息,否则就什么都不做。假设,收到WM_DDE_INITIATE 的程序中有那么几个程序阻塞的处理了这个消息(比如sleep(100000)或者干了一些其他很耗时的事情),那我们的程序A就悲剧了,它将一直等待,一直等待,一直等待......会不会就这么一直等待下去?答案是no,看上面的堆栈,有一个SendMessageTimeOut,DDE使用的是带超时控制的SendMessage,在无响应后一段时间内(对于DDE这里,是30s,太卡怕了),程序A将继续执行,这也就是ShellExecute卡一段时间的原因,有可能是卡了超时的30s,也有可能是卡了多个程序响应WM_DDE_INITIATE 花的时间的累加。
其实DDE技术是一项过时的技术,它适用于16位系统下的进城间通信,比如Windows Shell 外壳打开某些文件类型(这不就是我们用的ShellExecute么?!),win7下已经放弃了,但是xp下为了兼容一些16的程序还是保留了。
那么为什么DDE在16系统下没问题在32位就不行了呢?
看这里:
16位系统是co-operatively multitasking系统,即合作式系统,充分信任每一个task,让它一直独占CPU,直到它自己把CPU用烦了,这样的缺点很明显:如果有一些很戳的程序很不自觉的长时间占用CPU,那其他程序就无法得到CPU执行,更严重的是,整个系统也将被卡住!!但是这样也保证了类似于DDE的广播技术可以实施而不用担心会无响应卡住。
以后的per-emptive multitasking系统,即抢占式系统,系统充分参与调度,并且给每个task一个执行时间限制。而在windows系统下(也是per-emptive multitasking系统),由于调度策略是“饥饿策略”(调度策略有多重,可以参考这里),对于广播技术,就可能造成A程序广播后,B程序拿到CPU执行权,然后他响应了广播消息,但是一直占用CPU,这时A就卡死了。
具体的各种CPU模型和调度可以看这里,主要是分时系统啦
知道原因后,这个卡死的问题也可以解决,猥琐且迅速的方法就是改掉SendMessageTimeout的超时时间限制,ShellExecute用DDE时默认设置的是30s,自己的程序Hook住SendMessageTimeout后将超时时间改小一些就好了。比如10ms?