Fechar sistema ao rodar aplicativo externo

Delphi

09/04/2007

Dai pessoal 8)

Estou terminando a parte de atualização automática do meu sistema, e um probleminha que eu enfrentei sempre foi com o WinExec.

Não com ele, mas vamos supor que o Principal.exe chama o Update.exe via WinExec, e logo após faça um Application.Terminate:

WinExec(PChar(ExtractFilePath(ParamStr(0)) + ´update.exe´), SW_HIDE);
Application.Terminate;


Teoricamente deveria funcionar, até funciona. O problema é que o [b:333459da78]Terminate[/b:333459da78] só é executado junto (ou praticamente junto) com o fim da execução do Update.exe, e dessa forma, o Update.exe não consegue sobreeescrever o Principal.exe

Alguém aí tem uma idéia??
Valeu!! :wink:


Felipeaj

Felipeaj

Curtidas 0

Respostas

Felipeaj

Felipeaj

09/04/2007

Completando o post, já que não tem como editar:

Acho que deveria haver alguma modificação no Update.exe, já que se por exemplo eu colocar um ShowMessage(´Oi´); no início do código do Update.exe (antes das cópias dos arquivos), antes de o usuário poder clicar no OK, o Principal.exe fecha e a cópia ocorre com sucesso.

Sem uma ´pausa´, o Principal.exe não fecha até o fim da execução do Update.exe.
E não adianta colocar Sleep(), nem Application.ProcessMessages...

Valeu 8)


GOSTEI 0
Marco Salles

Marco Salles

09/04/2007

não lhe o seu tópico , mas acho que voce quer fechar o aplicativo e executar via winexec um outro programa ???

voce pode usar um delay para isto , mandando e preocsessando uma mensagem do windows ou mais simples aplicar um retardo na suia aplicação

WinExec(PChar(ExtractFilePath(ParamStr(0)) + ´update.exe´), SW_HIDE); sleep(50) Close; ;// se estiver no form principal



GOSTEI 0
Felipeaj

Felipeaj

09/04/2007

Sim, é isso que eu quero, e está funcionando
Só que o Principal.exe demora pra fechar, fecha junto com o término da execução do Update.exe

Gostaria de saber como fazer p/ que o Update.exe só continuasse a execução dos comandos, quando o principal.exe estivesse fechado (colocar um sleep no update.exe não adianta).


GOSTEI 0
Djjunior

Djjunior

09/04/2007

crie essa função :
function ExecuteFileWait(const FileName, Params, StartDir: PAnsiChar; ShowCmd: Integer): Integer; var Info: TShellExecuteInfo; ExitCode: DWORD; begin Screen.Cursor:= crHourGlass; FillChar(Info, SizeOf(Info), 0); Info.cbSize:= SizeOf(TShellExecuteInfo); with Info do begin fMask:= SEE_MASK_NOCLOSEPROCESS; Wnd:= Application.Handle; lpFile:= FileName; lpParameters:= Params; lpDirectory:= StartDir; nShow:= ShowCmd; end; if ShellExecuteEx(@Info) then begin repeat Application.ProcessMessages; GetExitCodeProcess(Info.hProcess, ExitCode); until (ExitCode <> STILL_ACTIVE) or Application.Terminated; Result:= ExitCode; end else Result:= -1; Screen.Cursor:= crDefault; end;



a chamada fica algo do tipo
ExecuteFileWait(´SeuPrograma´, Parametros, ´´, 1 );


tipo ele vai abrir uma janela do DOS ( O ShowCmd) executa teu aplicativo e retorna o código de erro do programa (se for o caso tipo o errorlevel do chose do DOS)


GOSTEI 0
Djjunior

Djjunior

09/04/2007

crie essa função :
function ExecuteFileWait(const FileName, Params, StartDir: PAnsiChar; ShowCmd: Integer): Integer; var Info: TShellExecuteInfo; ExitCode: DWORD; begin Screen.Cursor:= crHourGlass; FillChar(Info, SizeOf(Info), 0); Info.cbSize:= SizeOf(TShellExecuteInfo); with Info do begin fMask:= SEE_MASK_NOCLOSEPROCESS; Wnd:= Application.Handle; lpFile:= FileName; lpParameters:= Params; lpDirectory:= StartDir; nShow:= ShowCmd; end; if ShellExecuteEx(@Info) then begin repeat Application.ProcessMessages; GetExitCodeProcess(Info.hProcess, ExitCode); until (ExitCode <> STILL_ACTIVE) or Application.Terminated; Result:= ExitCode; end else Result:= -1; Screen.Cursor:= crDefault; end;



a chamada fica algo do tipo
ExecuteFileWait(´SeuPrograma´, Parametros, ´´, 1 );


tipo ele vai abrir uma janela do DOS ( O ShowCmd) executa teu aplicativo e retorna o código de result do programa (se for o caso tipo o errorlevel do chose do DOS)


GOSTEI 0
Felipeaj

Felipeaj

09/04/2007

[b:31b7b5ce51]djjunior[/b:31b7b5ce51], valeu pela ajuda!

Mas o problema é o oposto...

O principal.exe chama a aplicação externa update.exe e se fecha (application.terminate)

O problema é justamente que o principal.exe demora p/ se fechar. Os dois aplicativos NAO PODEM estar rodando junto! Por que? Porque dai o update.exe nao consegue atualizar o principal.exe!

o que eu preciso é de alguma forma que o update.exe fique parado, esperando o principal.exe ser finalizado efetivamente...


GOSTEI 0
Briciosm

Briciosm

09/04/2007

Simples!
Faz assim...
Dentro do teu sistema update.exe coloca um componente timer. E deixa configurado para 5 segundos, por exemplo.
O teu sistema principal irá fechar e chamar o update.exe.
E só após 5 segundos, por exemplo, ele irá atualizar o sistema.

Outra solução seria:
Em vez de tu chamar o sistema principal, tu chama outro aplicativo que verifica se tem alguma atualização.
Se tiver tu atualiza.
E depois chama o sistema. (Derrepente terá que colocar um timer pois a atualização poderá demorar alguns segundos)
E depois tu fecha este aplicativo que faz a atualização.

Seria isso!


GOSTEI 0
Felipeaj

Felipeaj

09/04/2007

Realmente, não sei porque não usei o timer
Achei que não iria funcionar

Mas fiz diferente. Pus um timer, que a cada 0,2 segundos verifica se a aplicação principal está rodando. Se NÃO, pára o timer e roda atualização!

Funcionou perfeito!
Valeu pela ajuda!


GOSTEI 0
Adriano Santos

Adriano Santos

09/04/2007

Aqui eu fiz o contrário camarada. O meu executável Atualizador.exe fecha o Principal.exe, se liga:

[list:de244f7994]
[*:de244f7994] 1. Eu mostro para o usuário todos os módulos que necessitam de atualizações.
[*:de244f7994] 2. Quando o cara clica em ´Atualizar agora´ ai sim eu verifico se existe algum módulo (executável aberto):
          {Aqui verifica se existem módulos abertos. Caso haja o usuário será alertado e os mesmos fechados na confirmação dele}
          PreencherModulosAbertos;
          if ModulosAbertos.Count > 0 then
          begin
            if MessageBox(Handle, ´Um ou mais módulos do sistema América encontram-se em execução.´ + #13 +
              ´Deseja fechá-los e continuar a atualização do sistema?´, ´Alerta´, MB_ICONQUESTION + MB_YESNO) = ID_YES then
              FecharModulosAbertos
            else
            begin
              btnAtualizar.Enabled := True;
              btnFechar.Enabled := True;
              Tamanho_Arquivo := 1;
              Exit;
            end;
          end;

[quote:de244f7994=´Funções PreencherModulosAbertos´]
procedure TfAtualiza.PreencherModulosAbertos;
var
  I: Integer;
begin
  IniRemota := TIniFile.Create(ExtractFilePath(Application.ExeName) + gsArqRemoto);
  ModulosAbertos.Clear;
  for I := 1 to 20 do
  begin
    {Verifica se a chave existe, ou seja, se o "I" atual corresponde à algum módulo no arquivo. local.ini}
    if not IniRemota.ValueExists(´Atualizacoes´, ´Modulo´ + IntToStr(I)) then
      Break;
    {Nome e tamanho do Módulo}
    Titulo := IniRemota.ReadString(´Atualizacoes´, ´Titulo´ + IntToStr(I), Titulo);
    if FindWindow(nil, PChar(Titulo)) > 0 then
      ModulosAbertos.Add(Titulo);
  end;
  IniRemota.Free;
end;

[/quote:de244f7994]
[quote:de244f7994=´Funções FecharModulosAbertos´]
procedure TfAtualiza.FecharModulosAbertos;
var
  I: Integer;
begin
  IniRemota := TIniFile.Create(ExtractFilePath(Application.ExeName) + gsArqRemoto);
  for I := 0 to ModulosAbertos.Count - 1 do
    if FindWindow(nil, PChar(ModulosAbertos[I])) > 0 then
      PostMessage(FindWindow(nil, PChar(ModulosAbertos[I])), WM_CLOSE, 0, 0);
  IniRemota.Free;
end;

[/quote:de244f7994]
[/list:u:de244f7994]

Como que eu sei o módulo que está aberto?
Bem, fiz um esquema simples, mas eficiente hehehe...se liga, eu tenho no meu arquivo Atualizacoes.amk que fica no meu servidor a seguinte estrutura:

[quote:de244f7994=´EStrutura do arquivo´]
...
Modulo1=Operacao.exe
Versao1=vF6.1.02
Titulo1=Operacional

Modulo2=Cobranca.exe
Versao2=vF6.1.02
Titulo2=Faturamento/Cobrança
...
[/quote:de244f7994]

Onde o Titulo? contém o ´titulo´ do meu programa gravado no Project >> Options do projeto, que consequentemente, é o titulo dele no Windows.
Neste caso o meu usuário abre o Atualizador.exe por fora do meu sistema pq ainda não criei o link pra abrir por dentro. Porém mesmo colocando o link dentro do sistema vai funcionar tranquilamente.

Qq coisa eu mando o fonte do projeto Atualizador.exe, sem problemas.


GOSTEI 0
Felipeaj

Felipeaj

09/04/2007

[b:ce96e550f5]Adriano Santos[/b:ce96e550f5], valeu aí cara!

Muito interessante teu sistema. Em casa, com mais calma, vou dar uma olhada melhor!

Eu precisei montar o meu da seguinte forma: durante o uso do sistema, ele chama o update.exe (via WinExec) com parâmetro ´download´, p/ somente baixar (da internet) e guardar numa pasta os arquivos atualizados

Na próxima execução, antes do login, o sistema se fecha, chama o update.exe (parâmetro copia), e o update.exe chama de volta o principal.exe

O por que essa mão toda? Porque o cliente não tem que clicar em NADA! Quanto menos o usuário (em específico) clicar ou mecher nisso, melhor! O sistema se atualiza bonitinho e o cliente fica feliz!! :wink:

Como eu fiz tá funcionando, mas mesmo assim vou dar uma lida na tua idéia, quem sabe mais pra frente... 8)


Novamente, valeu a todos pelas dicas!!


GOSTEI 0
Adriano Santos

Adriano Santos

09/04/2007

[b:753a37dc50]Adriano Santos[/b:753a37dc50], valeu aí cara! Muito interessante teu sistema. Em casa, com mais calma, vou dar uma olhada melhor! Eu precisei montar o meu da seguinte forma: durante o uso do sistema, ele chama o update.exe (via WinExec) com parâmetro ´download´, p/ somente baixar (da internet) e guardar numa pasta os arquivos atualizados Na próxima execução, antes do login, o sistema se fecha, chama o update.exe (parâmetro copia), e o update.exe chama de volta o principal.exe O por que essa mão toda? Porque o cliente não tem que clicar em NADA! Quanto menos o usuário (em específico) clicar ou mecher nisso, melhor! O sistema se atualiza bonitinho e o cliente fica feliz!! :wink: Como eu fiz tá funcionando, mas mesmo assim vou dar uma lida na tua idéia, quem sabe mais pra frente... 8) Novamente, valeu a todos pelas dicas!!

Concordo com vc. Quanto menos o burro do usuário precisar mexer melhor...rsrs...Esta ainda é a 1ª versão do Atualizador, mas no futuro quero que ele possa atualizar via Internet (hoje já faz) e tb via rede. Tb quero fazer algo que o usuário não precise fazer nada, mas cai no problema de atualizar um executável com ele aberto, por isso criei um novo sistema. ;)


GOSTEI 0
Raserafim

Raserafim

09/04/2007

djjunior,

tentei usar a sua sugestão mas tive problemas com a linha
Info: TShellExecuteInfo; (identificador não declarado)

qual unit tenho que declarar?

e que parâmetros são este?

você pode dar um exemplo de como utulizar a função?


GOSTEI 0
Adriano Santos

Adriano Santos

09/04/2007

ShellApi? Veja se é isso.


GOSTEI 0
POSTAR