Подтвердить что ты не робот

Изящно Exit Explorer (программно)

Как вы изящно закрываете программу Explorer программно?

Под этим я подразумеваю, как вы программно вызываете эту функцию:

Изменить: на картинке на картинке должно быть указано "Ctrl-Shift-Right-Click" вместо "Shift-Click".

4b9b3361

Ответ 1

Я отлаживал это из любопытства. Все, что он делает, - это сообщение в одно из окон проводника:

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

Конечно, это недокументированное сообщение WM_USER, поэтому поведение в будущем может измениться в будущем.

Ответ 2

@Luke: прежде всего, спасибо за подробный анализ и подсказку о сообщении пользователя 0x5B4 для Shell_TrayWnd!

К сожалению, метод имеет два недостатка; Во-первых, он использует недокументированное сообщение пользователя, которое может измениться в будущих версиях Windows, а во-вторых, оно не работает под Windows XP, так как "волшебная процедура" для выхода из окна отличается (откройте диалоговое окно завершения работы, а затем отмените его нажатием клавиши SHIFT -CTRL-ALT-ESC), и там нет сообщений.

Было бы неплохо иметь надежный и переносимый способ полностью прекратить работу проводника независимо от версии Windows. Поэтому я продолжил отладку в разборке кода, который полностью очищает проводник, чтобы найти подсказку о том, как я мог бы это достичь. У меня все еще нет идеального решения, но я сделал несколько интересных замечаний (в Windows 7 и Windows XP), которые я хочу поделиться с тем, кто может быть заинтересован:

Windows 7

Сообщение 0x5B4 в конечном итоге обрабатывается методом CTray:: _ DoExitExplorer. Если у вас включен сервер символов, вы можете установить точку останова в

{,,explorer.exe}CTray::_DoExitExplorer (синтаксис визуальной студии)

соответственно.

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

В обоих случаях оболочка (explorer.exe) завершается и до ее завершения устанавливает раздел реестра

    HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\CleanShutdown = TRUE

как это можно наблюдать с помощью Sysinternals Process Monitor, или путем установки точки останова в {, explorer} _WriteCleanShutdown @4 (или explorer! _WriteCleanShutdown).

Поведение в Windows 7

Оба метода не работают: хотя кажется, что оболочка завершена, процесс 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() с недокументированной функцией WM_USER для Windows 7 и ExitExplorer1() или ExitExplorer2() для Windows XP. Есть ли у одного из двух 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 [[email protected] (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 WM_QUIT на MSDN

Это сообщение не имеет возврата значение, потому что оно вызывает сообщение цикл для завершения перед сообщением отправляется в окно приложения процедура.

Заметки Сообщение WM_QUIT не связанных с окном, и поэтому никогда не будут получены через оконное окно. это извлекается только с помощью GetMessage или Функции PeekMessage.

Не отправляйте сообщение WM_QUIT, используя функция PostMessage; использование PostQuitMessage.

Ответ 3

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