您的位置:首页 > 其它

关于模拟右键刷新的相关研究

2012-08-16 00:00 183 查看
有的人喜欢编写这样一个程序:对“文件夹选项”中的“隐藏文件和文件夹”进行操作。但是常常在修改过注册表

HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced

下的项目之后并不能看到“即时效果”(也就是更改之后隐藏属性的文件显示出来了或者不可见了)。如果修改之后没有得到“即时效果”而必须自己再手动刷新一下的话,这将使程序体验大打折扣,于是程序员们纷纷想办法解决问题,实现这一效果。

关于这一问题,网上有70%的人都称SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);这样一个函数可以实现“刷新”,经过试验它其实是不奏效的,也有人在第二个参数上加上诸如SHCNF_FLUSH、SHCNF_FLUSHNOWAIT这样的叠加参数,最后也不奏效。关于这个函数在MSDN里面的解释,其实是用来刷新因修改文件关联而变得的图片的,并不是所谓的刷新。

有25%的人通过发送按键的方法:

HWND hwnd = FindWindow(_T("Progman"),_T("Program Manager"));
PostMessage(hwnd,WM_KEYDOWN,VK_F5,0);
PostMessage(hwnd,WM_KEYUP,VK_F5,1);


从代码可以看出首先找到了桌面窗口(ShellWindow),然后分别发送了一个F5按键按下和恢复的消息(F5对应着刷新),代码确实奏效。它可以使桌面出现“即时效果”,但是也有问题如果你打开了其他的文件夹窗口的话,那些窗口并没有随着投递消息而也随之产生效果,这个方法虽然是奏效的但还是不完美的。那些试了这种方法的程序员不少都不满意,认为这种方法并不能令人满意,而他们所需要是点击右键然后刷新的那种效果。
在网上搜寻了很久也只找到关于其他方式的只言片语:一个也为这事求助的程序员在网上询问如何知道自己的刷新按键被点击了,一位叫做“近在眼前”的前辈回复了截获“SHELLDELL_DefView窗口的msg = 273 wParam=28931”消息,例程没有奉上,不知道他是如何截获这段消息的,猜测可能是通过HOOK一些函数吧。我猜测这应该通过SendMessage()或者PostMessage()函数投递的一个消息,将273转换成16进制为0x111在WinUser.h文件中查获对应为WM_COMMAND消息类型,于是顺藤摸瓜在MSDN里面找关于WM_COMMAND的一些信息,结果并没有太多关于系统菜单消息的一些介绍,这应该是微软并不希望将这些内部的消息公开吧。循着上面得到只言片语我开始写一个向SHELLDELL_DefView窗口投递WM_COMMAND 28931的程序(test.exe),先用之前修改注册表显示隐藏的程序修改完注册表,没有“即时效果”。然后再运行刚才写好的投递消息的测试程序(test.exe),桌面上确实起了反应,类似于右键刷新而且具有隐藏属性的文件显示出来了!看来这确实就是刷新的消息,于是将代码合并到修改注册表修改程序当中(RegAces.exe)重新编译生成。在桌面上运行效果很明显,和刚才那个模拟发送F5按键的程序效果差不多,感觉应该优于那个程序。为什么这么说呢,这串代码发送的直接就是“刷新”的消息,而那个程序发送的是F5,加入F5被别的程序注册为热键了呢,那刷新功能岂不是失效了。在桌面测试完成之后,又开始把程序复制到磁盘里面进行测试结果并不理想,程序每次运行之后桌面有了刷新但是所在磁盘并没有反应,只有通过手动刷新之后才能显示出来,这个方法确实也是一种遗憾。
于是通过在网上搜索关于WM_COMMAND 28931,在微软社区里面发现一些外国的程序员也在为实现这一效果发愁,一个自称经验丰富的外国专家提出将SendMessage()和SHChangeNotify()同时使用,他自称在Vista x64和XP sp3 x86测试通过后面也有跟帖的程序员称完美运行的但是我在WIN7 x64上测试并没有成功。
不过上面这些例子倒是启发了我,既然是消息传递机制,和不向每个窗口都发送刷新呢。之前也有个人在发送F5按键时候提出向每个窗口发送F5,但是那个楼主认为想所有窗口发送按键消息资源开销过大而最终没有实现。对此我也饶了一些弯子比如说:如何向哪些窗口发送WM_COMMAND 28931消息。最后还是微软的开发工具SPY++帮了大忙,这里有可以监视各种窗口消息,以及消息类型的。于是我选择了所有窗口,监控WM_COMMAND消息。到一个文件夹窗口下,用右键刷新得到消息类型、消息附加值、以及接受窗口,通过SPY++可以很容易找到,但是处理过程可能要复杂一点,因为接受消息的菜单和它的顶层(top-level)菜单相差4、5级处理起来确实不容易,毕竟这也是一种方法,以下是实现代码

HWND hPar = NULL, hChd1 = NULL, hChd2 = NULL, hChd3 = NULL, hChd4 = NULL, hChd5 = NULL;
do {
hPar = FindWindowEx(NULL,hPar,_T("CabinetWClass"),NULL);
if (!hPar) break;
hChd1 = FindWindowEx(hPar,hChd1,_T("ShellTabWindowClass"),NULL);
hChd2 = FindWindowEx(hChd1,hChd2,_T("DUIViewWndClassName"),NULL);
hChd3 = FindWindowEx(hChd2,hChd3,_T("DirectUIHWND"),NULL);
do {
hChd4 = FindWindowEx(hChd3,hChd4,_T("CtrlNotifySink"),NULL);
hChd5 =
FindWindowEx(hChd4,hChd5,_T("SHELLDLL_DefView"),_T("ShellView"));
} while(!hChd5 && hChd4);
PostMessage(hChd5,WM_COMMAND,28931,NULL);
hChd1 = NULL; hChd2 = NULL; hChd3 = NULL; hChd4 = NULL; hChd5 = NULL;
} while (hPar);

hChd5 = (HWND)((DWORD)GetShellWindow() + 0x2);
PostMessage(hChd5,WM_COMMAND,28931,NULL);
分别向每个打开的资源管理器窗口发送刷新消息,最后再向桌面发送刷新消息,经调试效果很完美,唯一的瑕疵再与代码太多了。
本来觉得以为事情可以完结了,突然无意中想到在文件夹选项中点击“应用”按钮之后即可完成所有桌面的刷新,它是否发送某些特殊消息呢。于是调用SPY++进行监视,不出所料每次点击应用之后,它会想所有顶层(top-level)的为”CabinetWClass”类的窗口发送WM_COMMAND 41504消息,而CabinetWClass类正是我们之前打开的一个个资源管理器窗口,这省去了我们逐层查找的许多代码,也是系统所采用的真正完美有效的办法。代码实现如下:

HWND hCWC = NULL;
do {
hCWC = FindWindowEx(NULL, hCWC, _T("CabinetWClass"), NULL);
if (hCWC == NULL) break;
PostMessage(hCWC, WM_COMMAND, 41504, NULL);
} while (hCWC);
PostMessage(GetShellWindow(),WM_COMMAND,41504,NULL);
至此关于程序模拟右键刷新得到完美解决。❀
左氏补遗:对于那个经验丰富的老外其实还是很有借鉴地方的尤其是HWND_BROADCAST这个参数的使用。

PostMessage(HWND_BROADCAST,WM_COMMAND,41504,NULL);
一招绝杀。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  刷新 消息