高权限进程实现拖曳文件操作

在Vista/Win7系统中,由于UAC和UIPI的存在,低权限的进程是无法向高权限的进程发送任何高于WM_USER的消息,而低于WM_USER的消息一部分也会因为安全原因被禁止。详情可见:《UAC的前世今生》 。但这样就会带来很多麻烦,比如文件拖曳的操作在管理员权限的进程中就无法进行。

解决这个问题的方法大致有两种:

  • 如果进程一定需要高权限做一些操作,可以考虑将UI相关的部分设定为普通用户权限,而一些需要特殊权限的事情全丢到另外一个管理员权限的进程中进行,两个进程通过除消息之外的其他方式进行通信,比如管道,共享内存等等。(这种方法实在太蛋疼,除非有很多类型的消息或者特殊事件需要处理,否则不推荐)
  • 调用ChangeWindowMessageFilter 这个API进行消息过滤。在UIPI的作用下,当低权限进程向高权限进程发送消息时,大多数情况都会被Block。而ChangeWindowMessageFilter这个API的作用就是提供一个过滤器:往过滤器中添加消息就可以允许低权限的进程发送消息给当前进程,而移除消息则可以起到阻塞消息的作用。当然这也不是绝对的:某些低于WM_USER的消息无论我们怎么去设置这个过滤器都是可以通行的。

回到正题:管理员权限的进程之所以无法进行拖曳文件的操作就是因为explorer.exe本身的权限低于它,WM_DROPFILES的消息被堵塞了。所以只要在程序或者窗口初始化的时候调用ChangeWindowMessageFilter这个API就可以了。 ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);

嗯,这样就OK了?把这代码往自己的程序中一拷,却发现被坑爹了:还是不行。正确的做法应该是需要放行以下三条消息:

ChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD); ChangeWindowMessageFilter(WM_COPYDATA, MSGFLT_ADD); ChangeWindowMessageFilter(WM_COPYGLOBALDATA , MSGFLT_ADD);

其中WM_COPYGLOBALDATA在winodws.h中并没有相应的定义,也可以直接用它的数值0×0049来代替。至于为啥要这么做,并没有很明确的官方文档说明,不过也可以猜出个大概:WM_COPYDATA和WM_COPYGLOBALDATA是用于explorer.exe和当前进程进行进程通信的消息。不过MSDN上并不推荐使用这个方法,因为这个方法杀伤范围太大,影响的是当前整个进程,更推荐使用ChangeWindowMessageFilterEx,对某个特地窗口进行消息过滤:这样减少了filter对程序的影响,保证了安全又不至于无法实现某些特定功能。