你如何优雅地closures浏览器编程?
我的意思是,你如何以编程方式调用这个函数:
编辑:图片中的错字,应该说“Ctrl-Shift-Right-Click”,而不是“Shift-Click”。
我出于好奇调试了这个。 它所做的只是发送一条消息到浏览器的窗口之一:
BOOL ExitExplorer() { HWND hWndTray = FindWindow(_T("Shell_TrayWnd"), NULL); return PostMessage(hWndTray, 0x5B4, 0, 0); }
当然这是一个没有记录的WM_USER消息,所以这个行为在未来可能会发生很大的变化。
@Luke:首先,感谢您对Shell_TrayWnd的0x5B4用户消息的详细分析和提示!
不幸的是,这个方法有两个缺点。 首先,它使用未公开的用户消息,这可能会在将来的Windows版本中更改;其次,在Windows XP下不起作用,因为退出窗口的“魔术过程”不同(打开关闭对话框,然后按下SHIFT取消它-CTRL-ALT-ESC),并且不存在消息发布。
不管windows版本如何,有一个可靠的,可移植的方法可以清楚地终止资源管理器。 所以我继续调试到彻底终止资源管理器的代码的反汇编,以便找到一个关于如何实现这一点的提示。 我仍然没有完美的解决方案,但是我提出了一些有趣的观察(在Windows 7和Windows XP上),我想与谁可能感兴趣的人分享:
Windows 7的
0x5B4消息最终由方法CTray :: _ DoExitExplorer处理。 如果你已经启用了符号服务器,那么你可以在其中设置一个断点
{,,explorer.exe}CTray::_DoExitExplorer
(visual studio语法)
RESP。
explorer!CTray::_DoExitExplorer
(windbg语法)
Windows XP
在WinXP中,你必须设置你的断点
{,,explorer.exe}CTray::_ExitExplorerCleanly
(visual studio语法)
RESP。
explorer!CTray::_ExitExplorer
(windbg语法)
然后在关机对话框中输入“魔法键盘”(SHIFT-CTRL-ALT-ESC)。 这两种方法都非常相似,正如您从反汇编中所看到的(请参阅后续文章)。 伪代码是
if (bUnnamedVariable == FALSE) { g_fFakeShutdown = TRUE; // (1) PostMessage(hWndProgMan, WM_QUIT, 0, TRUE); // (2) if (PostMessage(hWndTray, WM_QUIT, 0, 0)) { // (3) bUnnamedVariable = TRUE; } }
请注意,第一个PostMessage()调用作为lParam传递,这是WM_QUIT正式未使用的。 lParam的含义似乎是bShutdown == TRUE。
当然,从另一个应用程序中设置g_fFakeShutdown是不可能的(或不可行)。 所以我测试了PostMessage(hWndProgMan,WM_QUIT,0,TRUE / FALSE)跟随或不是PostMessage(hWndTray,WM_QUIT,0,FALSE)的不同组合。 看来,资源管理器在Windows XP和Windows 7下显示不同的行为。
以下两种方法似乎是在Windows XP终止浏览器的好候选人。 不幸的是,他们不能在Windows 7下工作:
BOOL ExitExplorer1() { HWND hWndProgMan = FindWindow(_T("Progman"), NULL); PostMessage(hWndProgMan, WM_QUIT, 0, TRUE); // <= lParam == TRUE ! HWND hWndTray = FindWindow(_T("Shell_TrayWnd"), NULL); PostMessage(hWndTray, WM_QUIT, 0, 0); return TRUE; } BOOL ExitExplorer2() { HWND hWndProgMan = FindWindow(_T("Progman"), NULL); PostMessage(hWndProgMan, WM_QUIT, 0, FALSE); // <= lParam == FALSE ! return TRUE; }
在Windows XP中的行为
在这两种情况下,shell(explorer.exe)终止,并在终止之前设置注册表项
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\CleanShutdown = TRUE
如使用Sysinternals进程监视器可以观察到的那样,或者通过在{,explorer} _WriteCleanShutdown @ 4(分别为explorer!_WriteCleanShutdown)处设置断点。
在Windows 7中的行为
这两种方法都不起作用:虽然shell看起来终止了,但explorer.exe进程仍在运行。
备注
如果我只发布一个WM_QUIT hWndProgMan与lParam = TRUE而不发布消息到hWndTray,即,
BOOL ExitExplorer3() { HWND hWndProgMan = FindWindow(_T("Progman"), NULL); PostMessage(hWndProgMan, WM_QUIT, 0, TRUE); return TRUE; }
然后我得到一个有趣的行为(Win7和WinXP):出现关机对话框。 如果你取消它,一切似乎都是正常的,但是在两三秒(!)秒后,浏览器终止。
结论
也许最好的解决方案是使用ExitExplorer()和Windows XP的无证WM_USER函数,并使用ExitExplorer1()或ExitExplorer2()。 两种XP方法中的任何一种都比其他方法有优势吗? 我不知道。
附录
CTray :: _DoExitExplorer(Windows 7)和CTray :: _ ExitExplorerCleanly(Windows XP)的反汇编
Windows 7的
{,,explorer.exe}CTray::_DoExitExplorer: explorer!CTray::_DoExitExplorer: 00fdde24 833df027020100 cmp dword ptr [explorer!g_fInSizeMove+0x4 (010227f0)],0 ds:0023:010227f0=00000000 00fdde2b 53 push ebx 00fdde2c 8bd9 mov ebx,ecx 00fdde2e 7535 jne explorer!CTray::_DoExitExplorer+0x41 (00fdde65) 00fdde30 56 push esi 00fdde31 8b35ec14f700 mov esi,dword ptr [explorer!_imp__PostMessageW (00f714ec)] 00fdde37 57 push edi 00fdde38 33ff xor edi,edi 00fdde3a 47 inc edi 00fdde3b 57 push edi 00fdde3c 6a00 push 0 00fdde3e 6a12 push 12h 00fdde40 ff35e8000201 push dword ptr [explorer!v_hwndDesktop (010200e8)] 00fdde46 893ddc270201 mov dword ptr [explorer!g_fFakeShutdown (010227dc)],edi 00fdde4c ffd6 call esi 00fdde4e 6a00 push 0 00fdde50 6a00 push 0 00fdde52 6a12 push 12h 00fdde54 ff7304 push dword ptr [ebx+4] 00fdde57 ffd6 call esi 00fdde59 85c0 test eax,eax 00fdde5b 7406 je explorer!CTray::_DoExitExplorer+0x3f (00fdde63) 00fdde5d 893df0270201 mov dword ptr [explorer!g_fInSizeMove+0x4 (010227f0)],edi 00fdde63 5f pop edi 00fdde64 5e pop esi 00fdde65 a1f0270201 mov eax,dword ptr [explorer!g_fInSizeMove+0x4 (010227f0)] 00fdde6a 5b pop ebx 00fdde6b c3 ret
('bUnnamedVariable'是地址为g_fInSizeMove + 4的模块全局变量)
Windows XP
{,,explorer.exe}CTray::_ExitExplorerCleanly: 01031973 8B FF mov edi,edi 01031975 57 push edi 01031976 8B F9 mov edi,ecx 01031978 83 BF 40 04 00 00 00 cmp dword ptr [edi+440h],0 0103197F 75 35 jne CTray::_ExitExplorerCleanly+43h (10319B6h) 01031981 53 push ebx 01031982 56 push esi 01031983 8B 35 94 17 00 01 mov esi,dword ptr [__imp__PostMessageW@16 (1001794h)] 01031989 33 DB xor ebx,ebx 0103198B 43 inc ebx 0103198C 53 push ebx 0103198D 6A 00 push 0 0103198F 6A 12 push 12h 01031991 FF 35 8C 60 04 01 push dword ptr [_v_hwndDesktop (104608Ch)] 01031997 89 1D 48 77 04 01 mov dword ptr [_g_fFakeShutdown (1047748h)],ebx 0103199D FF D6 call esi 0103199F 6A 00 push 0 010319A1 6A 00 push 0 010319A3 6A 12 push 12h 010319A5 FF 77 04 push dword ptr [edi+4] 010319A8 FF D6 call esi 010319AA 85 C0 test eax,eax 010319AC 74 06 je CTray::_ExitExplorerCleanly+41h (10319B4h) 010319AE 89 9F 40 04 00 00 mov dword ptr [edi+440h],ebx 010319B4 5E pop esi 010319B5 5B pop ebx 010319B6 8B 87 40 04 00 00 mov eax,dword ptr [edi+440h] 010319BC 5F pop edi 010319BD C3 ret
('bUnnamedVariable'似乎是相对偏移440h的CTray的成员)
备注看起来,WM_QUIT在这里以非常标准的方式使用,比较以下摘自MSDN WM_QUIT的MSDN
此消息没有返回值,因为它会导致消息循环在消息发送到应用程序的窗口过程之前终止。
备注WM_QUIT消息不与窗口关联,因此将永远不会通过窗口的窗口过程接收。 它只能通过GetMessage或PeekMessage函数获取。
不要使用PostMessage函数发布WM_QUIT消息; 使用PostQuitMessage。
在Windows Vista及更高版本上,您可以使用RestartManager API正常关闭资源管理器。
伪代码看起来像这样:
RmStartSession(...); RM_UNIQUE_PROCESS[] processes = GetProcesses("explorer.exe"); // get special handles to process you want to close RmRegisterResources(processes); // register those processes with restart manager session RmShutdown(RM_SHUTDOWN_TYPE.RmForceShutdown); RmRestart(...); // restart them back, optionally RmEndSession(...);
我不认为探险家可以“优雅地”关闭。 EnumProcesses – >比较路径 – > TerminateProcess
编辑:尝试发送WM_CLOSE / WM_QUIT( http://support.microsoft.com/kb/178893 )或EndTask