Jak spustit program a počkat na jeho ukončení - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:



Delphi

Jak spustit program a počkat na jeho ukončení

serial

20. srpna 2001, 00.00 | V dnešním článku se dozvíte, jak z programu v Delphi spustit libovolnou aplikaci a detekovat, kdy je ukončena. Vše je názorně předvedeno na ukázkovém programu.

První část otázky, nadnesené v nadpisu článku, pravděpodobně nebude většině programátorů v Delphi dělat problémy. Pokud chceme z našeho programu spustit jiný program, můžeme použít funkce WinExec nebo ShellExecute, která má lepší možnosti (jejich popis viz nápověda Win32 API). Obě tyto funkce se ale ukončují a vracejí kontrolu na místo volání ihned po spuštění daného programu, a oba programy (původní i spuštěný) pak běží nezávisle. Většinou toto chování vyhovuje, ale někdy je potřeba mít kontrolu nad tím, kdy spouštěný program ukončí svou činnost.

Namátkou mě napadá příklad: Prohlížeč souborů zabalených v archivu. Typicky prohlížený soubor rozbalíme někam do adresáře TEMP a spustíme externí prohlížeč. Bylo by dobré vědět, kdy uživatel tento prohlížeč ukončil, abychom poté mohli smazat soubor z TEMPu a ten nezabíral zbytečně místo na disku. Příkladů na toto téma se určitě najde víc.

Ukázková aplikaceTeď se už pojďme podívat, jak požadované chování (čekání na ukončení spuštěného programu) zařídit. Vše si ukážeme na poměrně triviální aplikaci (viz obrázek). Do textového pole uživatel napíše jméno programu, klikne na tlačítko Spustit, zadaný program je spuštěn a pod dobu jeho běhu jsou textové pole i tlačítko "zašedlé". Když se spuštěný program ukončí, oba ovládací prvky se "odšedí" a je možné spustit další program.

Jak na to?

Klíčovou funkcí programu je WinExecAndWait. Ta má stejné parametry jako systémová funkce WinExec, ale je vylepšená v tom, že vrací kontrolu až po ukončení spouštěného programu.

K vlastnímu spuštění programu použijeme systémovou funkci CreateProcess. Ta má spoustu parametrů (opět viz Win32 API Help), z nichž většinu ani nevyužijeme. Nejdůležitější jsou příkazová řádka (což je program, který se spustí, i s parametry) a dvě struktury (přesněji ukazatele na ně):

První je typu TStartupInfo a obsahuje spoustu informací, které Windows říkají, jakým způsobem má být daný program spuštěn. Opět většinu z možností nevyužijeme, spokojíme se s nastavením způsobu, jakým se má zobrazit okno aplikace (obdoba druhého parametru systémové funkce WinExec).

Druhá struktura je typu TProcessInformation a nese informace o právě vytvořeném procesu. Není to tedy parametr vstupní, ale výstupní. My z této struktury později využijeme jen handle procesu, který se skrývá pod položkou hProcess.

Funkce CreateProcess vrací ihned po spuštění programu. Na samotné čekání na ukončení použijeme funkci jinou WaitforSingleObject. Tato funkce se využívá především při synchronizaci procesů. Čeká, dokud systémový objekt (který identifikujeme pomocí handle) není v "signalizovaném stavu". Pokud je tímto objektem program, za "signalizovaný stav" se považuje, když je program ukončen. Prvním parametrem této funkce je tedy handle daného objektu a druhým je čas (v milisekundách), jak dlouho chceme čekat na daný objekt. Pokud je tento čas překročen, funkce vrátí hodnotu reprezentovanou konstantou WAIT_TIMEOUT, pokud se do uplynutí daného počtu milisekund program ukončí, funkce vrátí hodnotu WAIT_OBJECT_0. Jako čas je možno zadat i speciální konstantu INFINITE, která čekání prodlužuje do nekonečna.

Funkce WinExecAndWait tedy bude vypadat následovně:

function WinExecAndWait(FileName: string; Visibility: Integer): Integer;
var
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
begin
  { Naplníme strukturu StartupInfo. }
  FillChar(StartupInfo, SizeOf(StartupInfo), #0);
  StartupInfo.CB := SizeOf(StartupInfo);
  StartupInfo.dwFlags := STARTF_USESHOWWINDOW; // spustit v normálním okně
  StartupInfo.wShowWindow := Visibility;       // způsob zobrazení okna
                                               // (normální, maximalizované,...)
  if not CreateProcess(
    nil,
    PChar(FileName),       // příkazová řádka - to, co se spustí
    nil,                   // bezpečnostní atributy vzniklého procesu...
    nil,                   // ... a vlákna - ani jedno nepotřebujeme
    False,                 // dědění handlů - nechceme
    NORMAL_PRIORITY_CLASS, // proces má normální prioritu
    nil,                   // ukazatel na proměnné prostředí - hodnota nil...
                           // ... znamená jejich zdědění od volajícího procesu
    nil,                   // aktuální adresář, nil opět znamená zdědění
    StartupInfo,           // ukazatele na potřebné struktury
    ProcessInfo)
  then
    Result := - 1 // při chybě vracíme -1
  else
  begin
    { Pokud spuštění proběhne v pořádku, čekáme na ukončení procesu. }
    WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
    { Nakonec vrátíme návratový kód procesu. }
    GetExitCodeProcess(ProcessInfo.hProcess, Result);
  end;
end;

A procedura "pověšená" na tlačítko Spustit vypadaá takto:

procedure TForm1.Button1Click(Sender: TObject);
begin
  Edit1.Enabled := False;
  Edit1.Color := clBtnFace;
  Button1.Enabled := False;

  { Spustíme program jako normální okno. }
  WinExecAndWait(Edit1.Text, SW_SHOWNORMAL);

  Edit1.Enabled := True;
  Edit1.Color := clWindow;
  Button1.Enabled := True;
end;

Drobné vylepšení

Pokud byste si takto vytvořený program vyzkoušeli, brzo zjistíte, že během čekání na ukončení spuštěného programu nemůžete v "rodičovské" aplikaci vůbec nic dělat - je prostě zablokovaná. Je to dáno tím, že během volání funkce WaitForSingleObject se nezpracovávají žádné zprávy od Windows, které zajišťují například reakce na vstup od uživatele či překreslování okna.

Řešení je celkem jednoduché - čekání na ukončení spuštěného programu rozdělíme na 100ms úseky. Po každém úseku zjistíme návratovou hodnotu funkce WaitForSingleObject. Pokud nebude WAIT_OBJECT_0, znamená to, že program se ještě neukončil a funkce vrátila kvůli vypršení času. V tom případě zpracujeme hlášení od Windows a spustíme čekání znovu. V případě opačném je spuštěný program zřejmě ukončen a naše funkce může vrátit jeho výstupní kód.

Místo volání funkce WaitForSingleObject tedy kódu umístíme následujících pár řádků:

{ Čekáme na ukončení procesu vždy 100 ms, pak necháme aplikaci
  zpracovat nahromaděné události. }
repeat
  Application.ProcessMessages;
until WaitForSingleObject(ProcessInfo.hProcess, 100) = WAIT_OBJECT_0;

Závěr

Posledním krokem jsme dotáhli náš program do finální podoby, kterou si můžete stáhnout (Delphi 3 a vyšší). I když celý postup vypsaný slovně vypadá poměrně složitě, kód je vcelku jednoduchý a myslím i pochopitelný, a tak vám nic nebrání použít vytvořenou proceduru WinExecAndWait i ve svých programech.

Tématické zařazení:

 » Rubriky  » Delphi  

 » Rubriky  » Windows  

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: