windows - tagger - tag folders
Gracefully Exit Explorer(mediante programaciĆ³n) (4)
¿Cómo se cierra graciosamente Explorer mediante programación?
Con eso quiero decir, ¿cómo invocas esta función de forma programática?
Editar: Typo en la imagen, debería decir "Ctrl-Shift-Right-Click" en lugar de "Shift-Click".
@Luke: antes que nada, ¡gracias por el análisis detallado y la pista sobre el mensaje del usuario 0x5B4 al Shell_TrayWnd!
Desafortunadamente, el método tiene dos inconvenientes; Primero, utiliza un mensaje de usuario no documentado, que puede cambiar en futuras versiones de Windows, y segundo, no funciona en Windows XP, ya que el ''procedimiento mágico'' para salir de Windows es diferente (abra el cuadro de diálogo de apagado y luego cancélelo presionando SHIFT -CTRL-ALT-ESC) y no se publica ningún mensaje allí.
Sería bueno tener una forma confiable y portátil de terminar el explorador de forma limpia desde otro proceso, independientemente de la versión de Windows. Así que continué depurando el desmontaje del código que termina limpiamente al explorador para encontrar una pista sobre cómo podría lograrlo. Todavía no tengo la solución perfecta, pero hice algunas observaciones interesantes (sobre Windows 7 y Windows XP) que quiero compartir con quien pueda estar interesado:
Windows 7
El mensaje 0x5B4 finalmente se maneja con el método CTray :: _ DoExitExplorer. Si tiene habilitado el servidor de símbolos, puede establecer un punto de interrupción en
{,,explorer.exe}CTray::_DoExitExplorer
(sintaxis de Visual Studio)
resp.
explorer!CTray::_DoExitExplorer
(sintaxis windbg)
Windows XP
En WinXP, debe establecer su punto de interrupción en
{,,explorer.exe}CTray::_ExitExplorerCleanly
(sintaxis visual del estudio)
resp.
explorer!CTray::_ExitExplorer
(sintaxis windbg)
antes de ingresar las ''pulsaciones de teclas mágicas'' (SHIFT-CTRL-ALT-ESC) en el cuadro de diálogo de apagado. Ambos métodos son muy similares, como se puede ver en el desmontaje (ver la publicación de seguimiento). El pseudo código es
if (bUnnamedVariable == FALSE) {
g_fFakeShutdown = TRUE; // (1)
PostMessage(hWndProgMan, WM_QUIT, 0, TRUE); // (2)
if (PostMessage(hWndTray, WM_QUIT, 0, 0)) { // (3)
bUnnamedVariable = TRUE;
}
}
Tenga en cuenta que la primera llamada de PostMessage () pasa VERDADERO como lParam, que WM_QUIT oficialmente no utiliza. El significado de lParam parece ser bShutdown == TRUE.
Por supuesto, es imposible (o no es factible) configurar g_fFakeShutdown desde otra aplicación. Así que probé diferentes combinaciones de PostMessage (hWndProgMan, WM_QUIT, 0, TRUE / FALSE) seguido o no por PostMessage (hWndTray, WM_QUIT, 0, FALSE). Parece que el explorador muestra un comportamiento diferente en Windows XP y Windows 7.
Los siguientes dos métodos parecen ser buenos candidatos para finalizar el explorador en Windows XP. Lamentablemente no funcionan en 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;
}
Comportamiento en Windows XP
En ambos casos, el shell (explorer.exe) termina y antes de terminar establece la clave de registro
HKCU/Software/Microsoft/Windows/CurrentVersion/Explorer/CleanShutdown = TRUE
como se puede observar usando Sysinternals Process Monitor, o estableciendo un punto de interrupción en {,, explorer} _WriteCleanShutdown @ 4 (¡o explorador! _WriteCleanShutdown).
Comportamiento en Windows 7
Ambos métodos no funcionan: aunque parece que el shell finalizó, el proceso explorer.exe aún se está ejecutando.
Observación
Si solo publico un WM_QUIT a hWndProgMan con lParam = TRUE sin publicar un mensaje en hWndTray, es decir,
BOOL ExitExplorer3() {
HWND hWndProgMan = FindWindow(_T("Progman"), NULL);
PostMessage(hWndProgMan, WM_QUIT, 0, TRUE);
return TRUE;
}
luego obtengo un comportamiento interesante (tanto Win7 como WinXP): Aparece el cuadro de diálogo de apagado. Si lo cancela, todo parece ser normal, pero después de dos o tres (!) Segundos, el explorador finaliza.
Conclusión
Quizás la mejor solución es usar ExitExplorer () con la función WM_USER no documentada para Windows 7 y ExitExplorer1 () o ExitExplorer2 () para Windows XP. ¿Alguno de los dos métodos XP tiene ventajas sobre el otro? No lo sé.
Apéndice
Desmontaje de CTray :: _ DoExitExplorer (Windows 7) y 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'' es una variable global de módulo en la dirección 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'' parece ser un miembro de CTray en offset relativo 440h)
Observación Parece que WM_QUIT se usa aquí de una manera no estándar, compare el siguiente extracto de MSDN WM_QUIT en MSDN
Este mensaje no tiene un valor de retorno porque hace que el bucle de mensaje finalice antes de que el mensaje se envíe al procedimiento de ventana de la aplicación.
Comentarios El mensaje WM_QUIT no está asociado a una ventana y, por lo tanto, nunca se recibirá a través del procedimiento de ventana de una ventana. Solo se recupera mediante las funciones GetMessage o PeekMessage.
No publique el mensaje WM_QUIT utilizando la función PostMessage; usa PostQuitMessage.
En Windows Vista y versiones superiores, puede usar RestartManager API para cerrar el explorador.
En pseudocódigo se verá así:
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(...);
Lo depuré por curiosidad. Todo lo que hace es publicar un mensaje en una de las ventanas del explorador:
BOOL ExitExplorer()
{
HWND hWndTray = FindWindow(_T("Shell_TrayWnd"), NULL);
return PostMessage(hWndTray, 0x5B4, 0, 0);
}
Por supuesto, este es un mensaje de WM_USER no documentado, por lo que es muy posible que el comportamiento cambie en el futuro.
No creo que el explorador se pueda cerrar "Agraciadamente". EnumProcesses -> comparar ruta -> TerminateProcess
Editar: intente enviar WM_CLOSE / WM_QUIT ( http://support.microsoft.com/kb/178893 ) o EndTask