windows - Gracefully Exit Explorer (Programmatisk)

Indlæg af Hanne Mølgaard Plasc

Problem



Hvordan yndefuldt lukker Explorer programmatisk?


Dermed mener jeg, hvordan bruger du denne funktion programmatisk:





Rediger: Typo i billedet, det skal sige 'Ctrl-Shift-Right-Click' i stedet for 'Shift-Click'.

Bedste reference


Jeg debugged dette ud af nysgerrighed. Alt det gør er at sende en besked til en af ​​explorerens windows:


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


Selvfølgelig er dette en ubeskrevet WM\_USER-besked, så adfærden kan helt sikkert ændre sig i fremtiden.

Andre referencer 1


@Luke: Først og fremmest tak for den detaljerede analyse og hint om 0x5B4 brugerbeskeden til Shell\_TrayWnd!


Desværre har metoden to ulemper; For det første bruger den en ikke-dokumenteret brugerbesked, som kan ændre sig i fremtidige Windows-versioner, og for det andet virker den ikke under Windows XP, da den 'magiske procedure' for at afslutte Windows er anderledes (Åbn shutdown-dialogen, og afbryd den derefter ved at trykke på SHIFT -CTRL-ALT-ESC), og der er ikke involveret nogen beskedpostering.


Det ville være rart at have en pålidelig og bærbar måde at afslutte explorer rent fra en anden proces uanset Windows-versionen. Så jeg fortsatte debugging i adskillelsen af ​​koden, der stopper explorer rent for at finde et tip om, hvordan jeg kunne opnå dette. Jeg har stadig ikke den perfekte løsning, men jeg lavede nogle interessante observationer (på Windows 7 og Windows XP), som jeg vil dele med den, der måske er interesseret:


Windows 7


0x5B4-meddelelsen håndteres til sidst ved hjælp af metoden CTray :: \_ DoExitExplorer. Hvis du har aktiveret symbolserver, kan du indstille et breakpoint


{,,explorer.exe}CTray::\_DoExitExplorer (visuel studio syntaks)


hhv.


explorer!CTray::\_DoExitExplorer (windbg syntaks)


Windows XP


I WinXP skal du sætte dit brydepunkt på


{,,explorer.exe}CTray::\_ExitExplorerCleanly (visuel studio syntaks)


hhv.


explorer!CTray::\_ExitExplorer (windbg syntaks)


før du indtaster 'magiske tastetryk' (SHIFT-CTRL-ALT-ESC) i afbrydelsesdialogboksen. Begge metoder er meget ens, som du kan se fra demontering (se opfølgningsindlæg). Pseudokoden er


    if (bUnnamedVariable == FALSE) {
        g\_fFakeShutdown = TRUE;  // (1)

        PostMessage(hWndProgMan, WM\_QUIT, 0, TRUE);   // (2)

        if (PostMessage(hWndTray, WM\_QUIT, 0, 0)) {    // (3)
            bUnnamedVariable = TRUE;
        }
    }


Bemærk, at det første PostMessage () -opkald videregiver SAND som lParam, som officielt er ubrugt af WM\_QUIT. Betydningen af ​​lParam ser ud til at være bShutdown == TRUE.


Det er selvfølgelig umuligt (eller ikke muligt) at indstille g\_fFakeShutdown fra et andet program. Så jeg testede forskellige kombinationer af PostMessage (hWndProgMan, WM\_QUIT, 0, TRUE/FALSE) efterfulgt eller ej af PostMessage (hWndTray, WM\_QUIT, 0, FALSE). Det ser ud til, at explorer viser forskellige opgaver under Windows XP og Windows 7.


Følgende to metoder synes at være gode kandidater til at afslutte explorer under Windows XP. Desværre arbejder de ikke 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;
    } 


Adfærd i Windows XP


I begge tilfælde afslutter skalen (explorer.exe) og inden opsigelsen indstilles registreringsnøglen


    HKCUSoftwareMicrosoftWindowsCurrentVersionExplorerCleanShutdown = TRUE


som kan observeres ved hjælp af Sysinternals Process Monitor eller ved at indstille et breakpoint ved {,, explorer} \_WriteCleanShutdown @ 4 (resp. explorer! \_WriteCleanShutdown).


Adfærd i Windows 7


Begge metoder virker ikke: selv om det ser ud til, at skallen er afbrudt, kører explorer.exe-processen stadig.


Bemærkning


Hvis jeg kun sender en WM\_QUIT til hWndProgMan med lParam=TRUE uden at sende en besked til hWndTray, dvs.


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

        return TRUE;
    } 


så får jeg en interessant opførsel (både Win7 og WinXP): Afbrydelsesdialogboksen vises. Hvis du annullerer det, ser det ud til at være normalt, men efter to eller tre (!) Sekunder slutter explorer.


Konklusion


Måske er den bedste løsning at bruge ExitExplorer () med den ubeskrevne WM\_USER-funktion til Windows 7 og enten ExitExplorer1 () eller ExitExplorer2 () til Windows XP. Har nogen af ​​de to XP-metoder fordele frem for den anden? Jeg ved ikke.


Appendiks


Demontering af CTray :: \_ DoExitExplorer (Windows 7) og 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' er en global modulvariabel på adressen 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' synes at være medlem af CTray ved relative offset 440h)


Bemærkning
Det ser ud som om WM\_QUIT bruges her på en meget ikke-standard måde, sammenligner følgende uddrag fra MSDN WM\_QUIT på MSDN [13]



  Denne meddelelse har ikke returneret
  værdi fordi det forårsager meddelelsen
  loop for at afslutte før meddelelsen
  sendes til programmets vindue
  procedure.

  
  Bemærkninger WM\_QUIT beskeden er ikke
  forbundet med et vindue og derfor
  vil aldrig blive modtaget gennem a
  vindues vindue procedure. Det er
  hentes kun af GetMessage eller
  PeekMessage funktioner.

  
  Send ikke WM\_QUIT beskeden med
  PostMessage funktionen; brug
  PostQuitMessage.


Andre referencer 2


I Windows Vista og derover kan du bruge RestartManager API til gracefully shutdown explorer. [14]


I pseudokode vil det se sådan ud:


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

Andre referencer 3


Jeg tror ikke, at explorer kan lukkes 'Gracefully'. EnumProcesses -> sammenlign path -> TerminateProcess [15] [16]


Rediger: Prøv at sende WM\_CLOSE/WM\_QUIT (http://support.microsoft.com/kb/178893) eller EndTask [17] [18]