windows 优雅地退出资源管理器(以编程方式)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5689904/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Gracefully Exit Explorer (Programmatically)
提问by user541686
How do you gracefullyclose Explorer programmatically?
您如何以编程方式优雅地关闭资源管理器?
By that I mean, how do you invoke this function programmatically:
我的意思是,你如何以编程方式调用这个函数:
Edit: Typo in the picture, it should say "Ctrl-Shift-Right-Click" instead of "Shift-Click".
编辑:图片中的错字,它应该说“Ctrl-Shift-Right-Click”而不是“Shift-Click”。
回答by Luke
I debugged this out of curiosity. All it does is post a message to one of explorer's windows:
出于好奇,我调试了这个。它所做的只是向资源管理器的一个窗口发布一条消息:
BOOL ExitExplorer()
{
HWND hWndTray = FindWindow(_T("Shell_TrayWnd"), NULL);
return PostMessage(hWndTray, 0x5B4, 0, 0);
}
Of course this is an undocumented WM_USER message so the behavior could quite possibly change in the future.
当然,这是一个未记录的 WM_USER 消息,因此行为很可能在未来发生变化。
回答by msp
@Luke: first of all, thanks for the detailed analysis and the hint about the 0x5B4 user message to the Shell_TrayWnd!
@Luke:首先,感谢详细的分析和对Shell_TrayWnd 的0x5B4 用户消息的提示!
Unfortunately, the method has two drawbacks; First, it uses an undocumented user message, which may change in future Windows versions, and second, it does not work under Windows XP, since the 'magic procedure' to exit windows is different (open the shutdown dialog, then cancel it pressing SHIFT-CTRL-ALT-ESC) and no message posting is involved there.
不幸的是,该方法有两个缺点;首先,它使用未记录的用户消息,在未来的 Windows 版本中可能会改变,其次,它在 Windows XP 下不起作用,因为退出 Windows 的“魔术程序”是不同的(打开关闭对话框,然后按 SHIFT 取消它-CTRL-ALT-ESC) 并且那里不涉及消息发布。
It would be nice to have a reliable and portable way to terminate explorer cleanly from another process regardless of the windows version. So I continued debugging into the disassembly of the code which terminates explorer cleanly in order to find a hint about how I could achieve this. I still don't have the perfect solution but I made some interesting observations (on Windows 7 and Windows XP) which I want to share with whoever might be interested:
无论 Windows 版本如何,如果有一种可靠且可移植的方式从另一个进程干净地终止资源管理器,那就太好了。所以我继续调试代码的反汇编,它干净地终止资源管理器,以便找到有关如何实现这一点的提示。我仍然没有完美的解决方案,但我做了一些有趣的观察(在 Windows 7 和 Windows XP 上),我想与任何可能感兴趣的人分享:
Windows 7
Windows 7的
The 0x5B4-message is eventually handled by the method CTray::_DoExitExplorer. If you have symbol server enabled, then you can set a breakpoint in
0x5B4 消息最终由方法 CTray::_DoExitExplorer 处理。如果您启用了符号服务器,那么您可以在
{,,explorer.exe}CTray::_DoExitExplorer
(visual studio syntax)
{,,explorer.exe}CTray::_DoExitExplorer
(视觉工作室语法)
resp.
分别
explorer!CTray::_DoExitExplorer
(windbg syntax)
explorer!CTray::_DoExitExplorer
(windbg 语法)
Windows XP
视窗 XP
In WinXP, you have to set your breakpoint at
在 WinXP 中,您必须将断点设置在
{,,explorer.exe}CTray::_ExitExplorerCleanly
(visual studio syntax)
{,,explorer.exe}CTray::_ExitExplorerCleanly
(视觉工作室语法)
resp.
分别
explorer!CTray::_ExitExplorer
(windbg syntax)
explorer!CTray::_ExitExplorer
(windbg 语法)
before you enter the 'magic keystrokes' (SHIFT-CTRL-ALT-ESC) at the shutdown dialog. Both methods are very similar, as you can see from the disassembly (see followup post). The pseudo code is
在关闭对话框中输入“魔法击键”(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;
}
}
Note that the first PostMessage() call passes TRUE as lParam, which is officially unused by WM_QUIT. The meaning of the lParam seems to be bShutdown == TRUE.
请注意,第一个 PostMessage() 调用将 TRUE 作为 lParam 传递,WM_QUIT 正式未使用它。lParam 的意思似乎是 bShutdown == TRUE。
Of course it is impossible (or not feasible) to set g_fFakeShutdown from another application. So I tested different combinations of PostMessage(hWndProgMan, WM_QUIT, 0, TRUE/FALSE) followed or not by PostMessage(hWndTray, WM_QUIT, 0, FALSE). It seems that explorer shows different behaviour under Windows XP and Windows 7.
当然,从另一个应用程序设置 g_fFakeShutdown 是不可能的(或不可行的)。所以我测试了 PostMessage(hWndProgMan, WM_QUIT, 0, TRUE/FALSE) 后跟或不跟 PostMessage(hWndTray, WM_QUIT, 0, FALSE) 的不同组合。似乎资源管理器在 Windows XP 和 Windows 7 下显示不同的行为。
The following two methods seem to be good candidates to terminate explorer under windows XP. Unfortunately they don't work under 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;
}
Behaviour in Windows XP
Windows XP 中的行为
In both cases the shell (explorer.exe) terminates and before terminating it sets the registry key
在这两种情况下,外壳 (explorer.exe) 都会终止并在终止之前设置注册表项
HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\CleanShutdown = TRUE
as can be observed using Sysinternals Process Monitor, or by setting a breakpoint at {,,explorer}_WriteCleanShutdown@4 (resp. explorer!_WriteCleanShutdown).
可以使用 Sysinternals Process Monitor 或通过在 {,,explorer}_WriteCleanShutdown@4 (resp. explorer!_WriteCleanShutdown) 处设置断点来观察到。
Behaviour in Windows 7
Windows 7 中的行为
Both methods don't work: although it appears that the shell terminated, the explorer.exe process is still running.
这两种方法都不起作用:尽管外壳似乎已终止,但 explorer.exe 进程仍在运行。
Remark
评论
If I only post a WM_QUIT to hWndProgMan with lParam = TRUE without posting a message to hWndTray, i.e.,
如果我只用 lParam = TRUE 向 hWndProgMan 发送 WM_QUIT 而不向 hWndTray 发送消息,即,
BOOL ExitExplorer3() {
HWND hWndProgMan = FindWindow(_T("Progman"), NULL);
PostMessage(hWndProgMan, WM_QUIT, 0, TRUE);
return TRUE;
}
then I get an interesting behaviour (both Win7 and WinXP): The shutdown dialog appears. If you cancel it, everything appears to be normal, but after two or three (!) seconds, explorer terminates.
然后我得到一个有趣的行为(Win7 和 WinXP):出现关闭对话框。如果您取消它,一切似乎都正常,但两三 (!) 秒后,资源管理器将终止。
Conclusion
结论
Maybe the best solution is to use ExitExplorer() with the undocumented WM_USER function for Windows 7 and either ExitExplorer1() or ExitExplorer2() for Windows XP. Does any one of the two XP-methods have advantages over the other? I don't know.
也许最好的解决方案是在 Windows 7 中使用 ExitExplorer() 和未记录的 WM_USER 函数,在 Windows XP 中使用 ExitExplorer1() 或 ExitExplorer2()。两种 XP 方法中的任何一种是否比另一种具有优势?我不知道。
Appendix
附录
Disassembly of CTray::_DoExitExplorer (Windows 7) and CTray::_ExitExplorerCleanly (Windows XP)
CTray::_DoExitExplorer (Windows 7) 和 CTray::_ExitExplorerCleanly (Windows XP) 的反汇编
Windows 7
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' is a module global variable at address g_fInSizeMove+4)
('bUnnamedVariable' 是地址 g_fInSizeMove+4 处的模块全局变量)
Windows XP
视窗 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' seems to be a member of CTray at relative offset 440h)
('bUnnamedVariable' 似乎是 CTray 的成员,相对偏移量为 440h)
RemarkIt seems that WM_QUIT is used here in a very non-standard way, compare the following excerpt from MSDN WM_QUIT on MSDN
备注好像这里用的WM_QUIT很不标准,对比下MSDN上的WM_QUIT摘录
This message does not have a return value because it causes the message loop to terminate before the message is sent to the application's window procedure.
Remarks The WM_QUIT message is not associated with a window and therefore will never be received through a window's window procedure. It is retrieved only by the GetMessage or PeekMessage functions.
Do not post the WM_QUIT message using the PostMessage function; use PostQuitMessage.
此消息没有返回值,因为它会导致消息循环在将消息发送到应用程序的窗口过程之前终止。
备注 WM_QUIT 消息与窗口无关,因此永远不会通过窗口的窗口过程接收到。它只能由 GetMessage 或 PeekMessage 函数检索。
不要使用 PostMessage 函数发布 WM_QUIT 消息;使用 PostQuitMessage。
回答by alex
On Windows Vista and above, you can use RestartManager APIto gracefully shutdown explorer.
在 Windows Vista 及更高版本上,您可以使用RestartManager API正常关闭资源管理器。
In pseudocode it will look like this:
在伪代码中,它将如下所示:
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(...);
回答by cprogrammer
I don't think explorer can be closed "Gracefully". EnumProcesses-> compare path -> TerminateProcess
我不认为资源管理器可以“优雅地”关闭。EnumProcesses-> 比较路径 -> TerminateProcess
Edit: Try to send WM_CLOSE/WM_QUIT (http://support.microsoft.com/kb/178893) or EndTask
编辑:尝试发送 WM_CLOSE/WM_QUIT ( http://support.microsoft.com/kb/178893) 或EndTask