Usando CreateProcess para se integrar com aplicativos externos
Introdução
Falamos em outra dica sobre o uso do ShellExecute, para abrir aplicações externas. A questão é que apesar do comando ShellExecute ser extremamente poderoso, as vezes queremos mais. Exemplo, queremos executar uma aplicação externa e esperar esta terminar, para então continuarmos o nosso processamento.
Nesta dica iremos trabalhar com a função CreateProcess da API do Windows, onde iremos criar um processo (que será abrir o notepad para editar um arquivo) e faremos nossa aplicação ficar esperando o processo criado ser finalizado.
2. A estrutura do Comando
O comando CreateProcess tem a seguinte assinatura:
function CreateProcess(lpApplicationName: PChar; lpCommandLine: PChar; lpProcessAttributes, lpThreadAttributes: PSecurityAttributes; bInheritHandles: BOOL; dwCreationFlags: DWORD; lpEnvironment: Pointer; lpCurrentDirectory: PChar; const lpSta
Onde:
lpApplicationName: Aplicação que deve ser executada. Exemplo, Notepad.exe. lpCommandLine: Se você preferir, pode colocar NIL no primeiro parâmetro e neste parâmetro indicar a aplicação e parâmetros. Exemplo indicar Notepad.exe e o arquivo que o mesmo tem que abrir.
lpProcessAttributes: Não vamos tratar neste exemplo, deixe NIL.
lpThreadAttributes: Não vamos tratar neste exemplo, deixe NIL.
bInheritHandles: Não vamos tratar neste exemplo, deixe FALSE.
dwCreationFlags: Permite especificar algumas flags para a criação do processo. Exemplo CREATE_SUSPENDED (permite criar o processo "parado", de modo que o programador pode configurar o ponto ideal para colocar o processo em execução. Por padrão o processo é criado e colocado em execução. Neste parâmetro é possível também configurar a prioridade do processo, exemplo REALTIME_PRIORITY_CLASS, que indica que o novo processo deve ser executado com a mais alta das prioridades. Outros valores referentes a prioridades são NORMAL_PRIORITY_CLASS, HIGH_PRIORITY_CLASS e IDLE_PRIORITY_CLASS.
lpEnvironment: Não vamos tratar neste exemplo, deixe NIL.
lpCurrentDirectory: Indica o diretório corrente para o processo que está sendo criado.
lpStartupInfo: Esta estrutura define parâmetros que indicam como a janela do processo que está sendo criado vai aparecer. Podemos citar alguns atributos relevantes para o nosso processo:
cb: Tamanho da estrutura. Utilizei 2048 como exemplo.
lpReserved e lpReserved2: atribuir NIL para estes parâmetros.
lpDesktop: deixar NIL neste parâmetro, utilizaremos configurações do processo pai.
lpTitle: pode ser colocado o nome que deve aparecer na janela do Console DOS. Caso a aplicação executada seja visual, se pode passar NIL no parâmetro.
dwFlags: aqui pode se indicar uma série de parâmetros como STARTF_USESHOWWINDOW (indica parâmetro para abertura de tela), STARTF_USEPOSITION (permite especificar atributos dwX w dwY da estrutura, com as posições iniciais da janela), STARTF_USESIZE (permite indicar o tamanho da tela através dos atributos dwXSize e dwYSize).
wShowWindow: indica o padrão da abertura da janela, como SW_SHOWNORMAL, SW_SHOWMAXIMIZED e SW_SHOWMINIMIZED.
lpProcessInformation: Esta estrutura é a chave da dica, pois é com ela que iremos monitorar a execução do processo filho e é através dela que saberemos quando o mesmo terminou. Esta estrutura possui identificadores do processo e da Thread, através dos atributos disponibilizados na estrutura do record TProcessInformation.
3. Aplicação de Exemplo
Iremos construir uma aplicação que permite selecionar um arquivo .txt ou .pas e feita esta seleção poderemos acionar um botão chamado "Create Process".
Este botão irá criar um outro processo (Notepad - Bloco de Notas) e vai ficar aguardando a janela ser fechada para deixar a nossa aplicação continuar. Assim que a janela do Notepad é aberta, é criada uma tela na nossa aplicação de exemplo indicando para o usuário aguardar o fechamento da aplicação. O que esta tela de apoio faz é possuir um timer e a cada ocorrência do evento de timer é realizado um teste para identificar se o processo criado ainda está ativo. Se o processo não está mais ativo, a janela de apoio é fechada.
Veja o click do botão para criar o processo, com os comentários de apoio:
procedure TFormPrincipal.btnCreateProcessClick(Sender: TObject);
var
lStartUpInfo:TStartUpInfo;
lProcesso: TProcessInformation;
begin
// Inicializa a estrutura TStartUpInfo
// indicando formato de abertura da janela
// e setando os atributos obrigatórios
// de serem inicializados.
With lStartUpInfo do
begin
cb:=2048;
lpReserved:=NIL;
lpDesktop:=NIL;
lpTitle:=NIL;
dwFlags:=STARTF_USESHOWWINDOW;
wShowWindow:=SW_SHOWNORMAL;//SW_Hide; //para não aparecer na tela!
cbReserved2:=0;
lpReserved2:=NIL;
end;
// Cria o processo notepad.exe e passa por
// parâmetro o arquivo a ser aberto.
// Passa também as estruturas de controle,
// lStartUpInfo e lProcesso.
if not CreateProcess(NIL,PChar('notepad.exe ' + edArquivo.Text),
NIL, NIL, False, 0, NIL, PChar(ExtractFilePath(edArquivo.Text)),
lStartUpInfo, lProcesso) then
ShowMessage('Erro para executar CreateProcess')
else
begin
// Abre tela de apoio para aguardar o
// término do processo recém criado.
// A tela de apoio fica testando
// o status do processo, através da
// função da API do Windows, chamada
// GetExitCodeProcess
FormAguardar := TFormAguardar.Run(Self, lProcesso);
try
FormAguardar.ShowModal;
finally
FormAguardar.Free;
end;
end;
end;
O método Run da classe TFormAguardar é um construtor que recebe o parâmetro Owner e a variável de status do processo, para que seja possível realizar consultas. Assim que a tela é aberta e o evento de timer começa a ser acionado, é verificado o status do processo que foi executado. Veja o fonte do código do Timer:
procedure TFormAguardar.TimerTimer(Sender: TObject);
var
lExitCode: dword;
begin
MoveLabel;
// Testa se Processo ainda está ativo...
// Continua rodando o timer enquanto o status
// for diferente de STILL_ACTIVE, que indica que
// o processo está ativo.
if GetExitCodeProcess(FProcesso.hProcess, lExitCode) then
if (lExitCode<>STILL_ACTIVE) then
close;
end;
4. Conclusões
Com esta dica verificamos como trabalhar com a função CreateProcess, para iniciar um processo externo e monitorar o término do mesmo. Veja o exemplo para download que permite que você selecione arquivos e execute e teste a abertura de um processo.
Por hoje era isto pessoal. Até a próxima!