Raymond Chen 2006年05月05日
如何处理 ShellExecute 函数返回的 HINSTANCE?
在16位Windows中,HINSTANCE
用于标识一个程序。Win32内核是16位内核的完全重新设计,引入了诸如“内核对象”和“安全描述符”等概念。特别是,16位Windows没有“进程ID”;实例句柄就起到了这个作用。这就是为什么WinExec
和ShellExecute
函数返回一个HINSTANCE
的原因。但在32位世界中,HINSTANCE
并不唯一地标识一个正在运行的程序,因为它仅仅是可执行文件的基地址。由于每个程序都在自己的地址空间中运行,因此该值在整个系统中几乎不是唯一的。
那么,你可以用ShellExecute
函数返回的HINSTANCE
做什么呢?你可以检查它是否大于32,这表明调用成功。如果值小于32,则它是一个错误代码。在大于32的情况下,HINSTANCE
的确切值是没有意义的。
我为什么要告诉你这些在MSDN上已经提到了的事情?因为人大家很难将其关联起来。我一直看到有人拿到ShellExecute
函数返回的HINSTANCE
,然后在系统中搜索所有窗口,寻找具有匹配GWLP_HINSTANCE
(或者如果你仍然生活在非64位兼容的世界中,是GWL_HINSTANCE
)的窗口。这是徒劳的,原因有两个。首先,你得到的HINSTANCE
的值是没有意义的,即使它有意义,对你也没用,因为HINSTANCE
不是唯一的。(实际上,一个进程的HINSTANCE
几乎总是0x00400000,因为这是大多数链接器分配给程序可执行文件的默认地址。)
人们想要进行这种操作的最常见原因是他们想要对刚刚启动的程序做一些事情,通常是等待它退出,这表明用户已经关闭了文档,但这是有问题的。
首先,正如我们所注意到的,你从ShellExecute
函数得到的HINSTANCE
是无用的。你必须使用ShellExecuteEx
函数,并在SHELLEXECUTEINFO
结构体中设置SEE_MASK_NOCLOSEPROCESS
标志,此时会在hProcess
成员中返回一个进程句柄。但这仍然不起作用。
一个文档可以被执行而不创建新进程。你会遇到这种情况的最常见情况(但几乎不是唯一的)是,如果注册的文档类型处理程序请求了一个DDE(动态数据交换)对话。在这种情况下,程序的一个现有实例已经接受了对文档的责任。等待进程退出与等待用户关闭文档不是同一回事,因为关闭文档并不退出进程。
仅仅因为用户关闭了文档,并不意味着进程就退出了。大多数程序允许你从“文件”菜单中打开一个新文档。一旦打开了新文档,用户就可以关闭旧的文档。(单文档程序在打开新文档时隐式地关闭旧文档。)此外,关闭与文档相关的所有打开窗口并不一定导致程序退出。有些程序即使在关闭了所有窗口之后也会在后台运行,要么是为了提供某种持续的服务,要么是因为它们只是预计用户很快会再次运行程序,所以它们会延迟最终退出几分钟,看看是否还会被需要。
进程退出了,并不意味着文档就关闭了。有些程序会检测到先前的实例,并将文档传递给该实例。其他程序是启动另一个进程来完成实际工作的存根。在这两种情况下,新创建的进程很快就退出了,但文档仍然打开,因为文档的责任已经转移给了另一个进程。
没有统一的方法来检测文档是否已被关闭。每个程序都以不同的方式处理它。如果你很幸运,程序会暴露出允许你监视打开文档状态的属性。正如我们前面看到的,Internet Explorer通过ShellWindows
对象公开了其打开窗口的属性。我了解到,Microsoft Office也为其组件程序公开了一个相当复杂的自动化接口集。