优雅地退出资源管理器(以编程方式)

发布于 2024-11-01 16:10:46 字数 186 浏览 7 评论 0原文

如何以编程方式优雅关闭资源管理器?

我的意思是,如何以编程方式调用此函数:

编辑:图片中的拼写错误,应该显示“Ctrl- Shift-右键单击”而不是“Shift-单击”。

How do you gracefully close 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".

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

呆萌少年 2024-11-08 16:10:46

出于好奇我对此进行了调试。它所做的只是向资源管理器的一个窗口发布一条消息:

BOOL ExitExplorer()
{
    HWND hWndTray = FindWindow(_T("Shell_TrayWnd"), NULL);
    return PostMessage(hWndTray, 0x5B4, 0, 0);
}

当然,这是一条未记录的 WM_USER 消息,因此该行为将来很可能会发生变化。

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.

小瓶盖 2024-11-08 16:10:46

@Luke:首先感谢您对Shell_TrayWnd的0x5B4用户消息的详细分析和提示!

不幸的是,该方法有两个缺点:首先,它使用未记录的用户消息,在未来的 Windows 版本中可能会更改,其次,它在 Windows XP 下不起作用,因为退出 Windows 的“神奇过程”不同(打开关闭对话框,然后按 SHIFT 取消它) -CTRL-ALT-ESC)并且那里不涉及消息发布。

如果有一种可靠且可移植的方法来从另一个进程中干净地终止资源管理器,无论 Windows 版本如何,那就太好了。因此,我继续调试干净地终止资源管理器的代码的反汇编,以便找到有关如何实现此目标的提示。我仍然没有完美的解决方案,但我做了一些有趣的观察(在 Windows 7 和 Windows XP 上),我想与可能感兴趣的人分享:

Windows 7

0x5B4 消息最终得到处理通过 CTray::_DoExitExplorer 方法。如果启用了符号服务器,则可以分别在

{,,explorer.exe}CTray::_DoExitExplorer (Visual Studio 语法)

中设置断点。

explorer!CTray::_DoExitExplorer (windbg 语法)

Windows XP

在 WinXP 中,您必须在

{,,explorer.exe}CTray::_ExitExplorerCleanly 设置断点 (视觉工作室语法)

分别。

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() 调用将 TRUE 作为 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 Process Monitor 观察到),或者通过在 {,,explorer}_WriteCleanShutdown@4 (分别为 explorer!_WriteCleanShutdown)处设置断点。

Windows 7 中的行为

两种方法都不起作用:尽管 shell 似乎已终止,但 explorer.exe 进程仍在运行。

备注

如果我仅使用 lParam = TRUE 将 WM_QUIT 发布到 hWndProgMan,而不将消息发布到 hWndTray,即,

    BOOL ExitExplorer3() {
        HWND hWndProgMan = FindWindow(_T("Progman"), NULL);
        PostMessage(hWndProgMan, WM_QUIT, 0, TRUE); 

        return TRUE;
    } 

我会得到一个有趣的行为(Win7 和 WinXP):出现关闭对话框。如果取消它,一切看起来都很正常,但两三(!)秒后,资源管理器终止。

结论

也许最好的解决方案是将 ExitExplorer() 与 Windows 7 中未记录的 WM_USER 函数一起使用,对于 Windows XP 则使用 E​​xitExplorer1() 或 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' 似乎是 CTray 相对偏移量的成员440h)

备注
看来 WM_QUIT 在这里以一种非常不标准的方式使用,比较以下来自 MSDN 的摘录 MSDN 上的 WM_QUIT

此消息没有返回
值,因为它会导致消息
循环在消息之前终止
被发送到应用程序的窗口
程序。

备注 WM_QUIT 消息不是
与窗口相关联,因此
永远不会通过
window 的窗口过程。这是
仅由 GetMessage 检索或
PeekMessage 函数。

不要使用以下方式发布 WM_QUIT 消息
PostMessage 函数;使用
退出消息后。

@Luke: first of all, thanks for the detailed analysis and the hint about the 0x5B4 user message to the Shell_TrayWnd!

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.

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 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

{,,explorer.exe}CTray::_DoExitExplorer (visual studio syntax)

resp.

explorer!CTray::_DoExitExplorer (windbg syntax)

Windows XP

In WinXP, you have to set your breakpoint at

{,,explorer.exe}CTray::_ExitExplorerCleanly (visual studio syntax)

resp.

explorer!CTray::_ExitExplorer (windbg syntax)

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

    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.

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.

The following two methods seem to be good candidates to terminate explorer under windows XP. Unfortunately they don't work under 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

In both cases the shell (explorer.exe) terminates and before terminating it sets the registry key

    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).

Behaviour in Windows 7

Both methods don't work: although it appears that the shell terminated, the explorer.exe process is still running.

Remark

If I only post a WM_QUIT to hWndProgMan with lParam = TRUE without posting a message to hWndTray, i.e.,

    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.

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.

Appendix

Disassembly of CTray::_DoExitExplorer (Windows 7) and 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' is a module global variable at address 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' seems to be a member of CTray at relative offset 440h)

Remark
It seems that WM_QUIT is used here in a very non-standard way, compare the following excerpt from MSDN WM_QUIT on MSDN

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.

别念他 2024-11-08 16:10:46

在 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(...); 

On Windows Vista and above, you can use RestartManager API to gracefully shutdown explorer.

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(...); 
好倦 2024-11-08 16:10:46

我不认为资源管理器可以“优雅地”关闭。 EnumProcesses ->比较路径-> TerminateProcess

编辑:尝试发送 WM_CLOSE /WM_QUIT (http://support.microsoft.com/kb/178893) 或 结束任务

I don't think explorer can be closed "Gracefully". EnumProcesses -> compare path -> TerminateProcess

Edit: Try to send WM_CLOSE/WM_QUIT (http://support.microsoft.com/kb/178893) or EndTask

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文