Kategória: ObjectPascal

Zmenené: 25. júl 2009

Externé programy

S rôznych dôvodov býva potrebné spúšťať v svojom programe programy externé, či sa už jedná o nejaké systémové nástroje alebo bežné používateľské programy. Verte mi alebo nie, nie je nič jednoduchšie. V Lazarovi je na to pripravená trieda TProcess, ktorá je potomkom triedy TComponent.

TProcess

Ako som spomínal, trieda TProcess je potomkom triedy TComponent a možno ju použiť priamo v kóde programu, ale je dostupná aj ako komponent v palete komponentov na záložke System. Ja však použijem prvý spôsob, a topoužívanie priamo v kóde programu. V mojich príkladoch neukážem použitie všetkých metód a vlastností triedy, ale pokúsim sa položiť slušný základ pre jej ďalšie používanie. Takže, ktoré možnosti si vlastne ukážeme?

Najprv si vytvoríme algoritmus pre spustenie GUI programu KCalc, ktorý bude bežať nezávisle na našom programe, teda aj po jeho ukončení. Keďže však je niekedy vhodné, aby náš program počkal na ukončenie procesu, ktorý spustil, ukážeme si aj ako dosiahnuť takéto správanie. Toto čakanie si ukážeme na rovnakom programe, pretože si myslím, že práve na GUI programe najlepšie uvidíte to čakanie na ukončenie. Potom sa presunieme na konzolové programy. Tieto programy často spúšťame kvôli tomu, aby sme dostali ich výstup, takže najprv si ukážeme ako sa k ich výstupu dopátrať a dostať ho do formulára. No a nakoniec si ukážeme, akospustiť program, ktorý pre svoj beh potrebuje práva superpoužívateľa.

Formulár

Pre naše príkaldy sivytvorte nový projekt a v ňom formulár, podobný tomu na obrázku, ktorý obsahuje jeden komponent TLabel, jeden TMemo, zatváracie tlačítko a štyri tlačítka, z ktorých každé bude spúšťať jeden z mojich príkladov.

Formulár pre TProcess

Aby ste mali predstavu aj o menách komponentov, pridávam deklaráciu formulára. Z deklarácie som vynechal obsluhy udalostí tlačítiek, ale to nie je dôležité. Ak teda chcete jednoducho kopírovať ukážky kódu, pomenujte si všetky prvky tak ako je v príklade:

Formulár frmMain

type

    { TfrmMain }
    TfrmMain = class(TForm)
        BitBtn1: TBitBtn;
        btnKCalc: TButton;
        btnKCalcWait: TButton;
        btnIptabl: TButton;
        btnNetst: TButton;
        Label1: TLabel;
        memVyst: TMemo;
        private
        { private declarations }
        public
        { public declarations }
    end;

Príklady použitia

Samostatný GUI program

V tomto príklade, ktorý je naozaj základným použitím triedy TProcess, pripravím obsluhu udalosti OnClick tlačidla btnKCalc, dúfam, že priložené komentáre vravia za všetko:

Spustenie samostatného GUI

procedure TfrmMain.btnKCalcClick(Sender: TObject);
var
    Proc: TProcess;
    Retazec: String;
begin
    // Vytvorenie objektu
    Proc := TProcess.Create(nil);
    // nastavenie príkazového riadku (vrátane prípadných argumentov)
    Proc.CommandLine := 'kcalc';
    // spustenie procesu
    Proc.Execute;
    // zapísanie ID a návratovej hodnoty
    Retazec := 'Proces s ID ' + IntToStr(Proc.ProcessID) + ' spustený';
    memVyst.Lines.Add(Retazec);
    // uvoľnenie projektu (v tomto prípade aj pred ukončením procesu)
    Proc.Free;
end;

Vzhľadom k tomu, že pri uvoľnení obejktu Proc, proces ešte môže (a veľmi pravdepodobne aj bude) bežať, nemôžeme pri takomto riešení zisťovať návratovú hodnotu spusteného procesu. Takže ako jedinú interakciu ukazujem vypísanie ID vytvoreného procesu. Pokojne program spustite a vyskúšajte, že po kliknutí na tlačítko Kalkulacka, sa spustí program KCalc, a tento ostane spustený aj keď svoj program ukončíte.

Čakanie na GUI program

V tomto prípadepoužijeme skoro robnaký kód ako v predchdázajúcom príklade, len pridáme jednu voľbu spustenia poWaitOnExit, ktorá má za následok, že volanie metódy Execute skončí, až po skončení nami spusteného procesu:

Čakanie na skončenie procesu

procedure TfrmMain.btnKCalcWaitClick(Sender: TObject);
var
    Proc: TProcess;
    Retazec: String;
begin
    // Vytvorenie objektu
    Proc := TProcess.Create(nil);
    // nastavenie príkazového riadku (vrátane prípadných argumentov)
    Proc.CommandLine := 'kcalc';>
    // nasatvenie volieb spustenia (čakať na skončenie)
    Proc.Options := Proc.Options + [poWaitOnExit];
    // spustenie procesu
    Proc.Execute;
    // zapísanie ID a návratovej hodnoty
    Retazec := 'Proces s ID ' + IntToStr(Proc.ProcessID) + ' vrátil: ' + IntToStr(Proc.ExitStatus);
    memVyst.Lines.Add(Retazec);
    // uvoľnenie projektu (v tomto prípade až po ukončení procesu)
    Proc.Free;
end;

Keďže teraz metóda Execute čaká na skončenie procesu, môžeme po nej získať návratový kód procesu, ktorý vypisujeme do TMemo. Po spustení programu si môežte porovnať správenie, keď kliknete na tlačítko Kalkul Wait.

Získanie výstupu

Teraz prejdeme od GUI programov ku konzolovým, ktoré sú často charakteristické svojim výstupom na konzolu a práve preto tieto programy voláme. Takže potrebujeme nejakým spôsobom ich výstup zachytiť a potom ho predáme do nášho TMemo. Realizácia zachytenia výstupu procesu je založená na použití volieb spustenia poWaitOnExit a poUsePipes, ktoré zaistia okrem čakania na skončenie procesu aj použitie rúr (pipe) na presmerovanie výstupu. Samotný výstup potom načítame prostredníctvom streamu z vlastnosti Output. Ako príklad používam výstup programu netstat, ktorému pridávam aj nejaké voľby:

Výstup procesu

procedure TfrmMain.btnNetstClick(Sender: TObject);
var
    Proc: TProcess;
    Retazec: String;
begin
    // Vytvorenie objektu
    Proc := TProcess.Create(nil);
    // nastavenie príkazového riadku (vrátane prípadných argumentov)
    Proc.CommandLine := 'netstat -tuln';
    // nasatvenie volieb spustenia (čakať na skončenie a použiť rúru)
    Proc.Options := Proc.Options + [poWaitOnExit, poUsePipes];
    // spustenie procesu
    Proc.Execute;
    // zapísanie výstupu
    memVyst.Lines.LoadFromStream(Proc.Output);
    memVyst.Lines.Add('');
    // zapísanie ID a návratovej hodnoty
    Retazec := 'Proces s ID ' + IntToStr(Proc.ProcessID) + ' vrátil: ' + IntToStr(Proc.ExitStatus);
    memVyst.Lines.Add(Retazec);
    // uvoľnenie projektu (v tomto prípade až po ukončení procesu)
    Proc.Free;
end;

Všimnite si, že po vykonaní výstup programu nestat prepíše celý obsah TMemo.

Získanie práv superpoužívateľa

Niekedy je potrebné spustiť systémový program, ktorý však vyžaduje práva superpoužívateľa root. Ani to však nie je problém, v tomto príklade používam nástroj kdesu, ktorý zaistí pridelenie príslušných práv a aj prípadné zadanie hesla. Použitie su, či gksu je veľmi podobné.

Práva root

procedure TfrmMain.btnIptablClick(Sender: TObject);
var
    Proc: TProcess;
    Retazec: String;
    StrList: TStringList;
begin
    // Vytvorenie objektu
    Proc := TProcess.Create(nil);
    // nastavenie príkazového riadku (vrátane prípadných argumentov)
    Proc.CommandLine := 'kdesu -t "iptables -L"';
    // nasatvenie volieb spustenia (čakať na skončenie a použiť rúru)
    Proc.Options := Proc.Options + [poWaitOnExit, poUsePipes];
    // spustenie procesu
    Proc.Execute;
    // vytvorenie TStringList
    StrList := TStringList.Create;
    // zapísanie výstupu
    StrList.LoadFromStream(Proc.Output);
    memVyst.Lines.AddStrings(StrList);
    memVyst.Lines.Add('');
    // zapísanie ID a návratovej hodnoty
    Retazec := 'Proces s ID ' + IntToStr(Proc.ProcessID) + ' vrátil: ' + IntToStr(Proc.ExitStatus);
    memVyst.Lines.Add(Retazec);
    // uvoľnenie projektu (v tomto prípade až po ukončení procesu)
    AStringList.Free;
    Proc.Free;
end;

Nástroju kdesu musíme zadať parameter -t, ktorý sa postará o výstup zadaného programu do konzoly (čiže do našej rúry), potrebné je tiež príkaz aj s jeho parametrom uzavrieť do úvodzoviek, pretože inak sa o použitie parametra -L pokúsi kdesu. Za zmienku stojí ešte to, že riešenie výstupu procesu do TMemo tentokrát jeho obsah nenahradí, ale pridá na koniec.

Záver

Pre reálne využitie je určite viac ako vhodné pridať spracovanie výnimiek (chýb), nechcel som však príklady priveľmi komplikovať, ale zameral som sa na použitie triedy TProcess. Všetky tieto príklady neriešia jeden problém, a tým je spracovanie veľkého výstupu, ktorý môže spôsobiť uviaznutie programu. Postup riešenia problému, ak výstup presiahne 2048 B je uvedený v dokumentácii Lazara.