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!