2011-05-15 20 views
8

Tengo una pregunta con suerte rápida: ¿Es posible retrasar un poco la ejecución de ShellExecute?¿Esperar antes de llevar a cabo ShellExecute?

Tengo una aplicación con autoupdater. Después de que descarga todos los archivos necesarios, cambia el nombre de los archivos actuales a * .OLD y el nuevo como el anterior. Suficientemente simple. Pero luego necesito eliminar esos archivos .OLD. Este procedimiento de "limpieza" se ejecuta en MainForm.OnActivate (con una marca de verificación si es el primero que activa el proceso). Pero esto aparentemente sucede demasiado rápido (obtengo False de DeleteFile). Este es el procedimiento:

procedure TUpdateForm.OKBtnClick(Sender: TObject); 
const SHELL = 'ping 127.0.0.1 -n 2'; 
begin 
    ShellExecute(0,'open',pchar(SHELL+#13+Application.ExeName),nil,nil,SW_SHOWNORMAL); 
    Application.Terminate; 
end; 

Se supone que este procedimiento reinicia la aplicación. Estoy seguro de que el problema de eliminación está causado por el inicio rápido de la segunda aplicación, porque si lo reinicio yo mismo, otorgándole un poco de tiempo, los archivos se eliminan normalmente.

tl; dr versión: Necesito llamar a ShellExecute() que espera un poco (0.1 seg o menos) y LUEGO ejecuta el comando.

Nota

He intentado utilizar el comando -ping para tratar de retrasar, pero no funcionó.

Muchas gracias de antemano

Editar: reformulado

Necesito que esto suceda || La primera aplicación se cierra; Espere 100 ms; la segunda aplicación abre ||. Necesito llamar primero a ShellExecute, luego esperar hasta que la aplicación de llamada se cierre por completo y luego ejecutar el shell (es decir, abrir la segunda aplicación)

+3

No lo entiendo.¿Quieres esperar 100 ms y luego hacer 'ShellExecute'? Bueno, entonces hazlo! 'Sueño (100); ShellExecute (...) '. ¿O desea que 'ShellExecute' * no regrese hasta que el proceso recién creado haya salido *? Si es así, en teoría, quiere el 'Sleep' *** después de ***' ShellExecute', no * before * it. Pero esa es una solución fea. En su lugar, debe 'WaitForSingleObject' o algo así. –

+1

Trataré de reformularlo: necesito que esto suceda || La primera aplicación se cierra; Espere 100 ms; se abre la segunda aplicación ||. Necesito llamar primero a ShellExecute, luego esperar hasta que la aplicación de llamada se cierre por completo, luego ejecutar el shell (es decir, abrir la segunda aplicación) -> Ninguna de sus sugerencias. Necesito el ShellExecute _not para ejecutar_ hasta que la aplicación actual esté completamente cerrada (para poder eliminar sus archivos) –

+0

No puede hacer eso. Después de que se ha cerrado la aplicación 1, pero antes de que se haya abierto la aplicación 2, no puede hacer nada porque no tiene un programa en ejecución. Lo que puede hacer es llamar a una aplicación, dormir y luego llamar a otra aplicación y luego finalizarla. Pero en lugar de un 'Sleep 'fijo, debe' WaitForSingleObject' u otra cosa más robusta. –

Respuesta

5

Estás haciendo un autopatcher ¿no?

que he tenido el mismo problema y esto es lo que me bypassed que:

Ejecuta segunda aplicación con el argumento "--delay" o algo por el estilo. La segunda aplicación maneja el argumento "--delay" y duerme durante 100 ms, luego continúa ejecutándose normalmente.

+0

Parece limpio. Tonto de mí. Vota por ti, gracias –

+4

¿Qué pasa si estás copiando un archivo de WTV a un HDD externo en al mismo tiempo? Entonces 100 ms es probable que no sea suficiente, y todavía fallas. Una solución basada en 'WaitForSingleObject' es mejor. Alternativamente, la segunda aplicación puede hacer' while not done do sleep (100) 'o algo así en el inicio –

+0

bien. Lo voy a ver también, gracias –

3

Esta rutina es un código utils en nuestro motor de juegos. Puede ejecutar un ejecutable y, opcionalmente, esperar a que salga. Devolverá su código de salida:

function TSvUtils.FileExecute(ahWnd: Cardinal; const aFileName, aParams, aStartDir: string; aShowCmd: Integer; aWait: Boolean): Integer; 
var 
    Info: TShellExecuteInfo; 
    ExitCode: DWORD; 
begin 

    Result := -1; 
    FillChar(Info, SizeOf(Info), 0); 
    Info.cbSize := SizeOf(TShellExecuteInfo); 
    with Info do begin 
    fMask := SEE_MASK_NOCLOSEPROCESS; 
    Wnd := ahWnd; 
    lpFile := PChar(aFileName); 
    lpParameters := PChar(aParams); 
    lpDirectory := PChar(aStartDir); 
    nShow := aShowCmd; 
    end; 

    if ShellExecuteEx(@Info) then 
    begin 
    if aWait then 
    begin 
     repeat 
     Sleep(1); 
     Application.ProcessMessages; 
     GetExitCodeProcess(Info.hProcess, ExitCode); 
     until (ExitCode <> STILL_ACTIVE) or Application.Terminated; 
     CloseHandle(Info.hProcess); 
     Result := ExitCode; 
    end; 
    end 
end; 

Aquí hay algunos códigos que pueden verificar si existe un proceso. Entonces ... la aplicación actual llama al actualizador y finaliza. El proceso de actualización puede comprobar para ver si el viejo aplicación ha terminado y hacerlo es cosa (renombrar, actualizar, eliminar, etc):

function TSvUtils.ProcessExists(const aExeFileName: string; aBringToForgound: Boolean=False): Boolean; 
var 
    ContinueLoop: BOOL; 
    FSnapshotHandle: THandle; 
    FProcessEntry32: TProcessEntry32; 
begin 

    FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 
    FProcessEntry32.dwSize := SizeOf(FProcessEntry32); 
    ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32); 
    Result := False; 
    while Integer(ContinueLoop) <> 0 do 
    begin 
    if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) = 
     UpperCase(aExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) = 
     UpperCase(aExeFileName))) then 
    begin 
     if aBringToForgound then 
     EnumWindows(@BringToForgroundEnumProcess, FProcessEntry32.th32ProcessID); 
     Result := True; 
    end; 
    ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32); 
    end; 
    CloseHandle(FSnapshotHandle); 
end; 
+0

Gracias por la respuesta, pero creo que no entendió mi pregunta. Necesitaba poner un ejecutable en una 'consulta', salir de la aplicación actual. ion (el que llama), y luego ejecuta el exec desde la consulta. –

+0

nice busy-loop, BTW – user422039

+0

Ok .... pensando ... tu aplicación actual genera el actualizador para hacer el trabajo y se cierra. El actualizador comprueba para asegurarse de que la aplicación anterior ha finalizado (tengo un código que puede usar para verificar esto), luego actualiza, cambia el nombre, elimina, etc. ¿Cuando lo completa genera la nueva aplicación y termina? –

2

Si puede utilizar CreateProcess en lugar de ShellExecute, se puede esperar en el identificador de proceso. El identificador del proceso se señala cuando la aplicación finaliza. Por ejemplo:

function ExecAndWait(APath: string; var VProcessResult: cardinal): boolean; 
var 
    LWaitResult : integer; 
    LStartupInfo: TStartupInfo; 
    LProcessInfo: TProcessInformation; 
begin 
    Result := False; 

    FillChar(LStartupInfo, SizeOf(TStartupInfo), 0); 

    with LStartupInfo do 
    begin 
    cb := SizeOf(TStartupInfo); 

    dwFlags := STARTF_USESHOWWINDOW or STARTF_FORCEONFEEDBACK; 
    wShowWindow := SW_SHOWDEFAULT; 
    end; 

    if CreateProcess(nil, PChar(APath), nil, nil, 
        False, NORMAL_PRIORITY_CLASS, 
        nil, nil, LStartupInfo, LProcessInfo) then 
    begin 

    repeat 
     LWaitResult := WaitForSingleObject(LProcessInfo.hProcess, 500); 
     // do something, like update a GUI or call Application.ProcessMessages 
    until LWaitResult <> WAIT_TIMEOUT; 
    result := LWaitResult = WAIT_OBJECT_0; 
    GetExitCodeProcess(LProcessInfo.hProcess, VProcessResult); 
    CloseHandle(LProcessInfo.hProcess); 
    CloseHandle(LProcessInfo.hThread); 
    end; 
end; 

Después vuelve ExecAndWait, a continuación, se puede dormir durante 100 ms si es necesario.

N @

Cuestiones relacionadas