Delphi: cierre con gracia el proceso creado en servicio. (usando tprocess / createProcess)
-
06-07-2019 - |
Pregunta
Tengo un servicio de Windows escrito en Delphi que ejecuta varios programas.
Al detener el servicio, también quiero cerrar estos programas. Cuando el servicio se escribió originalmente, funcionó bien, pero creo que actualicé el componente tProcess y ahora: los programas subordinados no se están cerrando.
en tProcess: aquí está el código que inicia los nuevos procesos.
if CreateProcess( nil , PChar( FProcess.Command ) , nil , nil , False ,
NORMAL_PRIORITY_CLASS , nil , Directory ,
StartupInfo , ProcessInfo ) then
begin
if FProcess.Wait then
begin
WaitForSingleObject( ProcessInfo.hProcess , Infinite );
GetExitCodeProcess( ProcessInfo.hProcess , ExitCode );
if Assigned( FProcess.FOnFinished ) then
FProcess.FOnFinished( FProcess , ExitCode );
end;
CloseHandle( ProcessInfo.hProcess );
CloseHandle( ProcessInfo.hThread );
end;
Cada uno de los ejecutables llamados por esto son Programas GUI de Windows (con un botón de cierre en la parte superior)
Cuando detengo el servicio, también quiero detener (no eliminar) los programas que inicié mediante el procedimiento createProcess.
¿Cómo harías esto?
Solución
Desea enumerar las ventanas abiertas que coinciden con su ProcessId lanzado y decirles que se cierren. Aquí hay un código de muestra para eso:
uses Windows;
interface
function MyTerminateAppEnum(hHwnd:HWND; dwData:LPARAM):Boolean; stdcall;
implementation
function MyTerminateAppEnum(hHwnd:HWND; dwData:LPARAM):Boolean;
var
vID:DWORD;
begin
GetWindowThreadProcessID(hHwnd, @vID);
if vID = dwData then
begin
PostMessage(hHwnd, WM_CLOSE, 0, 0); //tell window to close gracefully
Result := False; //can stop enumerating
end
else
begin
Result := TRUE; //keep enumerating until you find your id
end;
end;
Entonces querrá utilizar esto en su código cuando quiera cerrar las aplicaciones iniciadas:
Procedure TerminateMe(YourSavedProcessInfo:TProcessInformation);
var
vExitCode:UINT;
begin
GetExitCodeProcess(YourSavedProcessInfo.hProcess, vExitCode);
if (vExitCode = STILL_ACTIVE) then //launched app still running..
begin
//tell it to close
EnumWindows(@MyTerminateAppEnum, YourSavedProcessInfo.dwProcessId);
if WaitForSingleObject(YourSavedProcessInfo.hProcess, TIMEOUT_VAL) <> WAIT_OBJECT_0 then
begin
if not TerminateProcess(YourSavedProcessInfo.hProcess, 0) then //didn't close, try to terminate
begin
//uh-oh Didn't close, didn't terminate..
end;
end;
end;
CloseHandle(YourSavedProcessInfo.hProcess);
CloseHandle(YourSavedProcessInfo.hThread);
end;
Otros consejos
Usaría el componente TJvCreateProcess de JVCL que envuelve cualquier proceso funcionalidad relacionada de win32 de una manera elegante. Esta respuesta proviene del departamento de no toque-winapi-a menos que sea realmente necesario :-)
La única forma genérica de detener un proceso es usar TerminateProcess . Pero eso está tan lejos de ser elegante como puedas. Para cerrar un proceso con gracia, debe decirle al proceso que desea que se detenga y luego esperar que obedezca. Sin embargo, no hay forma de hacerlo en general.
Para un programa GUI, la forma habitual de decirle que desea que deje de ejecutarse es cerrar su ventana principal. No hay una idea formal de "ventana principal". aunque. Un programa puede tener cero o más ventanas, y no hay forma de saber externamente cuál se supone que debes cerrar para que el programa deje de funcionar.
Puede usar EnumWindows para recorrer todas las ventanas y seleccione los que pertenecen a su proceso. (Serían aquellos para los que GetWindowThreadProcessId ofrece el mismo proceso ID que CreateProcess le dio.)
Cerrar una ventana puede no ser suficiente. El programa puede mostrar un cuadro de diálogo (solicitando confirmación o solicitando guardar cambios, etc.). Debería saber de antemano cómo descartar ese cuadro de diálogo.
Los programas que no son GUI pueden tener problemas similares. Puede ser suficiente simular una combinación de teclas Ctrl + C. Sin embargo, podría atrapar y manejar esa pulsación de tecla de manera diferente. Es posible que tenga un sistema de menús que espere que escriba " Q " para salir del programa.
En resumen, no puede cerrar con gracia un programa a menos que sepa de antemano cómo espera que se cierre ese programa.