Difference between revisions of "Executing External Programs/pl"

From Lazarus wiki
Jump to navigationJump to search
(→‎See also: tłumaczenie na j. polski)
 
(26 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
{{Executing External Programs}}
 
{{Executing External Programs}}
 
+
<div style="font-size:1.88em;margin:0.75em 0;">Wykonywanie programów zewnętrznych</div>
 
== Przegląd: Porównanie ==
 
== Przegląd: Porównanie ==
 
Istnieją różne sposoby dostępne w bibliotekach RTL, FCL i LCL dotyczące wykonywania zewnętrznego polecenia/procesu/programu.
 
Istnieją różne sposoby dostępne w bibliotekach RTL, FCL i LCL dotyczące wykonywania zewnętrznego polecenia/procesu/programu.
Line 10: Line 10:
 
!Cechy
 
!Cechy
 
|-
 
|-
|[[Executing External Programs#SysUtils.ExecuteProcess|ExecuteProcess]]
+
|[[Executing External Programs/pl#SysUtils.ExecuteProcess|ExecuteProcess]]
 
|RTL
 
|RTL
 
|Wieloplatformowy
 
|Wieloplatformowy
Line 16: Line 16:
 
|Bardzo ograniczone, synchroniczne.
 
|Bardzo ograniczone, synchroniczne.
 
|-
 
|-
|[[Executing External Programs#MS Windows : CreateProcess, ShellExecute and WinExec|ShellExecute]]
+
|[[Executing External Programs/pl#MS_Windows:_CreateProcess,_ShellExecute_i_WinExec|ShellExecute]]
 
|WinAPI
 
|WinAPI
 
|Tylko MS Windows
 
|Tylko MS Windows
Line 22: Line 22:
 
|Wiele. Może uruchamiać programy z podwyższonymi uprawnieniami administratora.
 
|Wiele. Może uruchamiać programy z podwyższonymi uprawnieniami administratora.
 
|-
 
|-
|[[Executing External Programs#Unix fpsystem, fpexecve and shell |fpsystem, fpexecve]]
+
|[[Executing External Programs/pl#Unix_fpsystem,_fpexecve_i_shell |fpsystem, fpexecve]]
 
|Unix
 
|Unix
 
|Tylko Unix
 
|Tylko Unix
Line 28: Line 28:
 
|
 
|
 
|-
 
|-
|[[Executing External Programs#TProcess|TProcess]]
+
|[[Executing External Programs/pl#TProcess|TProcess]]
 
|FCL
 
|FCL
 
|Wieloplatformowy
 
|Wieloplatformowy
Line 34: Line 34:
 
|Pełny.
 
|Pełny.
 
|-
 
|-
|[[Executing External Programs#(Process.)RunCommand|RunCommand]]
+
|[[Executing External Programs/pl#(Proces.)RunCommand|RunCommand]]
 
|FCL
 
|FCL
 
|Wieloplatformowy '''Wymaga FPC 2.6.2+'''
 
|Wieloplatformowy '''Wymaga FPC 2.6.2+'''
Line 40: Line 40:
 
|Obejmuje typowe użycie TProcess.
 
|Obejmuje typowe użycie TProcess.
 
|-
 
|-
|[[Executing External Programs#LCLIntf Alternatives|OpenDocument]]
+
|[[Executing External Programs/pl#Alternatywy_LCLIntf|OpenDocument]]
 
|LCL
 
|LCL
 
|Wieloplatformowy
 
|Wieloplatformowy
Line 75: Line 75:
 
</syntaxhighlight>  
 
</syntaxhighlight>  
  
But note that not all shell "built-in" commands (eg alias) work because aliases by default are not expanded in non-interactive shells and .bashrc is not read by non-interactive shells unless you set the BASH_ENV environment variable. So, this does not produce any output:
 
 
Zauważ jednak, że nie wszystkie „wbudowane” polecenia powłoki (np. aliasy) działają, ponieważ aliasy domyślnie nie są rozwijane w powłokach nieinteraktywnych, a .bashrc nie jest odczytywany przez powłoki nieinteraktywne, chyba że ustawisz zmienną środowiskową BASH_ENV. tak więc poniższy przykąłd nie daje żadnych danych wyjściowych:
 
Zauważ jednak, że nie wszystkie „wbudowane” polecenia powłoki (np. aliasy) działają, ponieważ aliasy domyślnie nie są rozwijane w powłokach nieinteraktywnych, a .bashrc nie jest odczytywany przez powłoki nieinteraktywne, chyba że ustawisz zmienną środowiskową BASH_ENV. tak więc poniższy przykąłd nie daje żadnych danych wyjściowych:
  
Line 159: Line 158:
 
   p.started:=now;
 
   p.started:=now;
 
   try
 
   try
     // główna pętla wariantów runcommand(), pierwotnie oparta na scenariuszu „dużego wyjścia” na wiki, ale stale rozwijana przez 5 lat.
+
     // główna pętla wariantów runcommand(), pierwotnie oparta na scenariuszu "dużego wyjścia" na wiki, ale stale rozwijana przez 5 lat.
 
     result:=p.RunCommandLoop(outputstring,errorstring,exitstatus)=0;
 
     result:=p.RunCommandLoop(outputstring,errorstring,exitstatus)=0;
 
     if p.timedout then
 
     if p.timedout then
Line 178: Line 177:
 
       if not RunCommandTimeout('someexe',['-v',s,'--output','dest\'+s],err,[],swoNone,60) then
 
       if not RunCommandTimeout('someexe',['-v',s,'--output','dest\'+s],err,[],swoNone,60) then
 
           begin
 
           begin
             // nie udało się uruchomić lub upłynął limit czasu. np. movefile() do „wadliwego” katalogu.
+
             // nie udało się uruchomić lub upłynął limit czasu. np. movefile() do "wadliwego" katalogu.
 
         end
 
         end
 
       else
 
       else
 
         begin
 
         begin
         // ok, plik przeniesiony do „dobrego” katalogu.
+
         // ok, plik przeniesiony do "dobrego" katalogu.
 
         end;
 
         end;
 
     end;
 
     end;
Line 237: Line 236:
 
Opcja fMask może również używać SEE_MASK_DOENVSUBST lub SEE_MASK_FLAG_NO_UI lub SEE_MASK_NOCLOSEPROCESS itp.
 
Opcja fMask może również używać SEE_MASK_DOENVSUBST lub SEE_MASK_FLAG_NO_UI lub SEE_MASK_NOCLOSEPROCESS itp.
  
Jeśli w Delphi używałeś ShellExecute do '''dokumentów''' takich jak dokumenty Word lub adresy URL, spójrz na funkcje open* (OpenURL itp.) w module lclintf (zobacz sekcję [https://wiki.lazarus.freepascal.org/Executing_External_Programs#See_also Zobacz także] na dole tej strony).
+
Jeśli w Delphi używałeś ShellExecute do '''dokumentów''' takich jak dokumenty Word lub adresy URL, spójrz na funkcje open* (OpenURL itp.) w module lclintf (zobacz sekcję [https://wiki.lazarus.freepascal.org/Executing_External_Programs/pl#Zobacz_także Zobacz także] na dole tej strony).
  
 
=== Używanie ShellExecuteEx do podnoszenia uprawnień administratora ===
 
=== Używanie ShellExecuteEx do podnoszenia uprawnień administratora ===
Line 278: Line 277:
 
== TProcess ==
 
== TProcess ==
  
You can use TProcess to launch external programs. Some of the benefits of using TProcess are that it is:
+
Możesz użyć TProcess do uruchamiania zewnętrznych programów. Niektóre z korzyści płynących z używania TProcess to:
  
* Platform Independent
+
* Jest niezależny od platformy.
* Capable of reading from stdout and writing to stdin.
+
* Potrafi czytać ze standardowego wyjścia i zapisywać na standardowe wejście.
* Possible to wait for a command to finish or let it run while your program moves on.
+
* Możliwe jest oczekiwanie na zakończenie polecenia lub pozwolenie na jego działanie podczas działania programu.
  
Important notes:
+
Ważne informacje:
* TProcess is not a terminal/shell! You cannot directly execute scripts or redirect output using operators like "|", ">", "<", "&" etc. It is possible to obtain the same results with TProcess using pascal, some examples are below..
+
* TProcess nie jest terminalem/powłoką! Nie możesz bezpośrednio wykonywać skryptów lub przekierowywać wyjścia za pomocą operatorów takich jak „|, >, <, &” itp. Możliwe jest uzyskanie tych samych wyników za pomocą TProcess i za pomocą pascala - kilka przykładów poniżej.
* Presumably on Linux/Unix: you '''must''' specify the full path to the executable. For example '/bin/cp' instead of 'cp'. If the program is in the standard PATH then you can use the function [[doc:lcl/fileutil/finddefaultexecutablepath.html|FindDefaultExecutablePath]] from the [[doc:lcl/fileutil/index.html|FileUtil]] unit of the LCL.
+
* Przypuszczalnie w systemie Linux/Unix: '''musisz''' podać pełną ścieżkę do pliku wykonywalnego. Na przykład „/bin/cp” zamiast „cp”. Jeśli program znajduje się w standardowej PATH, możesz użyć funkcji [[doc:lcl/fileutil/finddefaultexecutablepath.html|FindDefaultExecutablePath]] z modułu [[doc:lcl/fileutil/index.html|FileUtil]] biblioteki LCL.
* On Windows, if the command is in the path, you don't need to specify the full path.
+
* W systemie Windows, jeśli polecenie znajduje się w ścieżce, nie musisz określać pełnej ścieżki.
* [[doc:fcl/process/tprocess.html|TProcess reference]]
+
* Odnośnik do [[doc:fcl/process/tprocess.html|TProcess]]
  
=== The Simplest Example ===
+
=== Najprostszy przykład ===
  
A lot of typical cases have been prepared in the [[Executing_External_Programs#.28Process..29RunCommand|Runcommand]] functions. Before you start copy and paste the examples below, check them out first.
+
Wiele typowych przypadków zostało przygotowanych w funkcjach [[Executing_External_Programs/pl#.28Proces..29RunCommand|RunCommand]]. Zanim zaczniesz kopiować i wklejać poniższe przykłady, najpierw je sprawdź.
  
=== A Simple Example ===
+
=== Prosty przykład ===
This example ('''that shouldn't be used in production, see Large Output or, better, [[Executing_External_Programs#.28Process..29RunCommand|Runcommand]]''') just shows you how to run an external program, nothing more:
+
Ten przykład ('''który nie powinien być używany w środowisku produkcyjnym, patrz [[Executing_External_Programs/pl#Czytanie_dużych_ilości_danych_wyjściowych|Czytanie dużych ilości danych wyjściowych]] lub, lepiej, [[Executing_External_Programs/pl#.28Proces..29RunCommand|RunCommand]]''') pokazuje tylko, jak uruchomić zewnętrzny program, nic więcej:
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
// This is a demo program that shows
+
// Jest to program demonstracyjny, który
// how to launch an external program.
+
// pokazuje, jak uruchomić zewnętrzny program.
 
program launchprogram;
 
program launchprogram;
 
   
 
   
// Here we include files that have useful functions
+
// Tutaj dołączamy moduły zawierające przydatne
// and procedures we will need.
+
// funkcje i procedury, których będziemy potrzebować.
 
uses  
 
uses  
 
   Classes, SysUtils, Process;
 
   Classes, SysUtils, Process;
 
   
 
   
// This defines the var "AProcess" as a variable
+
// Definiuje zmienną "AProcess" jako zmienną typu "TProcess"
// of the type "TProcess"
 
 
var  
 
var  
 
   AProcess: TProcess;
 
   AProcess: TProcess;
 
   
 
   
// This is where our program starts to run
+
// Tu zaczyna działać nasz program
 
begin
 
begin
   // Now we will create the TProcess object, and
+
   // Teraz utworzymy obiekt TProcess i przypiszemy go
   // assign it to the var AProcess.
+
   // do zmiennej AProcess.
 
   AProcess := TProcess.Create(nil);
 
   AProcess := TProcess.Create(nil);
 
   
 
   
   // Tell the new AProcess what the command to execute is.
+
   // Powiedz nowemu AProcess, jakie jest polecenie do wykonania.
   // Let's use the Free Pascal compiler (i386 version that is)
+
   // Użyjmy kompilatora Free Pascal (dokładniej wersji x64)
   AProcess.Executable:= 'ppc386';
+
  // Lecz jeśli używasz kompilatora 32-bit zmień ppcx64 na ppc386
 +
   AProcess.Executable:= 'ppcx64';
  
   // Pass -h together with ppc386 so actually 'ppc386 -h' is executed:
+
   // Przekaż opcję -h razem z ppcx64, więc faktycznie wykonywane
 +
  // jest polecenie 'ppcx64 -h':
 
   AProcess.Parameters.Add('-h');
 
   AProcess.Parameters.Add('-h');
 
   
 
   
   // We will define an option for when the program
+
   // Zdefiniujemy opcję działającą, w czasie wykonywania programu.
   // is run. This option will make sure that our program
+
   // Ta opcja sprawi, że nasz program nie będzie kontynuowany,
   // does not continue until the program we will launch
+
   // dopóki program zewnętrzy, który uruchomimy, nie przestanie działać.
   // has stopped running.                vvvvvvvvvvvvvv
+
   //                                     /------------\
 
   AProcess.Options := AProcess.Options + [poWaitOnExit];
 
   AProcess.Options := AProcess.Options + [poWaitOnExit];
 
   
 
   
   // Now let AProcess run the program
+
   // Teraz pozwól AProcess uruchomić program zewnętrzny
 
   AProcess.Execute;
 
   AProcess.Execute;
 
   
 
   
   // This is not reached until ppc386 stops running.
+
   // Poniższy kod nie wykona się, dopóki ppcx64 nie przestanie działać.
 
   AProcess.Free;   
 
   AProcess.Free;   
 
end.</syntaxhighlight>
 
end.</syntaxhighlight>
That's it! You have just learned to run an external program from inside your own program.
+
Otóż ​​to! Właśnie nauczyłeś się uruchamiać zewnętrzny program z wnętrza własnego programu.
  
=== An improved example (but not correct yet)===
+
=== Ulepszony przykład (ale jeszcze niepoprawny) ===
That's nice, but how do I read the Output of a program that I have run?
+
Wszystko fajnie, ale jak odczytać dane wyjściowe uruchomionego programu?
  
Well, let's expand our example a little and do just that:
+
Cóż, rozszerzmy nieco nasz przykład, jak poniżej:
'''This example is kept simple so you can learn from it. Please don't use this example in production code, but use the code in [[#Reading large output]].'''
+
'''Ten przykład jest prosty, więc możesz się z niego uczyć. Jednak nie używaj tego przykładu w kodzie produkcyjnym, ale użyj kodu z [[Executing_External_Programs/pl#Czytanie_dużych_ilości_danych_wyjściowych|Czytanie dużych ilości danych wyjściowych]].'''
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
// This is a
+
// To jest
// FLAWED
+
// WADLIWY
// demo program that shows
+
// program demonstracyjny, który pokazuje,
// how to launch an external program
+
// jak uruchomić zewnętrzny program
// and read from its output.
+
// i odczytać z jego dane wyjściowe.
 
program launchprogram;
 
program launchprogram;
 
   
 
   
// Here we include files that have useful functions
+
// Tutaj dołączamy moduły zawierające przydatne
// and procedures we will need.
+
// funkcje i procedury, których będziemy potrzebować.
 
uses  
 
uses  
 
   Classes, SysUtils, Process;
 
   Classes, SysUtils, Process;
 
   
 
   
// This is defining the var "AProcess" as a variable
+
// Definiuje zmienną "AProcess" jako zmienną typu "TProcess"
// of the type "TProcess"
+
// Teraz także dodajemy TStringList do przechowywania danych
// Also now we are adding a TStringList to store the
+
// odczytanych z wyjścia programu.
// data read from the programs output.
 
 
var  
 
var  
 
   AProcess: TProcess;
 
   AProcess: TProcess;
 
   AStringList: TStringList;
 
   AStringList: TStringList;
  
// This is where our program starts to run
+
// Tu zaczyna działać nasz program
 
begin
 
begin
   // Now we will create the TProcess object, and
+
   // Teraz utworzymy obiekt TProcess i przypiszemy go
   // assign it to the var AProcess.
+
   // do zmiennej AProcess.
 
   AProcess := TProcess.Create(nil);
 
   AProcess := TProcess.Create(nil);
 
   
 
   
   // Tell the new AProcess what the command to execute is.
+
   // Powiedz nowemu AProcess, jakie jest polecenie do wykonania.
   AProcess.Executable := '/usr/bin/ppc386';  
+
   AProcess.Executable := '/usr/bin/ppcx64';  
 
   AProcess.Parameters.Add('-h');  
 
   AProcess.Parameters.Add('-h');  
  
   // We will define an option for when the program
+
   // Zdefiniujemy opcję działającą, w czasie wykonywania programu.
   // is run. This option will make sure that our program
+
   // Ta opcja sprawi, że nasz program nie będzie kontynuowany,
   // does not continue until the program we will launch
+
   // dopóki program zewnętrzy, który uruchomimy, nie przestanie działać.
   // has stopped running. Also now we will tell it that
+
   // Dodatkowo teraz powiemy mu, że chcemy odczytać dane wyjściowe.
   // we want to read the output of the file.
+
   //                                     /------------|-----------\
 
   AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
 
   AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
 
   
 
   
   // Now that AProcess knows what the commandline is it can be run.
+
   // Teraz, gdy AProcess wie, jaki jest wiersz poleceń, można go uruchomić.
 
   AProcess.Execute;
 
   AProcess.Execute;
 
    
 
    
   // After AProcess has finished, the rest of the program will be executed.
+
   // Po zakończeniu AProcess zostanie wykonana reszta programu.
 
   
 
   
   // Now read the output of the program we just ran into a TStringList.
+
   // Teraz odczytaj wynik działania programu, za pomocą TStringList.
 
   AStringList := TStringList.Create;
 
   AStringList := TStringList.Create;
 
   AStringList.LoadFromStream(AProcess.Output);
 
   AStringList.LoadFromStream(AProcess.Output);
 
    
 
    
   // Save the output to a file and clean up the TStringList.
+
   // Zapisz dane wyjściowe do pliku i zwolnij obiekt AStringList.
 
   AStringList.SaveToFile('output.txt');
 
   AStringList.SaveToFile('output.txt');
 
   AStringList.Free;
 
   AStringList.Free;
 
   
 
   
   // Now that the output from the process is processed, it can be freed.
+
   // Teraz, gdy dane wyjściowe z procesu są przetworzone, także można je zwolnić.
 
   AProcess.Free;   
 
   AProcess.Free;   
 
end.</syntaxhighlight>
 
end.</syntaxhighlight>
  
=== Reading large output ===
+
=== Czytanie dużych ilości danych wyjściowych ===
In the previous example we waited until the program exited. Then we read what the program has written to its output.
+
W poprzednim przykładzie czekaliśmy, aż program się zakończy. Następnie czytaliśmy, co program zapisał na swoim wyjściu.
  
Suppose the program writes a lot of data to the output. Then the output pipe becomes full and the called progam waits until the pipe has been read from.  
+
Załóżmy, że program zapisuje dużo danych na wyjściu. Następnie potok wyjściowy zapełnia się i wywoływany program czeka, aż potok zostanie odczytany.  
  
But the calling program doesn't read from it until the called program has ended. A deadlock occurs.
+
Ale program wywołujący nie czyta z niego, dopóki wywoływany program się nie zakończy. Następuje impas.
  
The following example therefore doesn't use poWaitOnExit, but reads from the output while the program is still running. The output is stored in a memory stream, that can be used later to read the output into a TStringList.
+
Poniższy przykład nie używa zatem poWaitOnExit, ale odczytuje dane wyjściowe, gdy program jest nadal uruchomiony. Dane wyjściowe są przechowywane w strumieniu pamięci, który można później wykorzystać do odczytania danych wyjściowych do TStringList.
 
+
If you want to read output from an external process, and can't use RunCommand, this is the code you should as a base for production use. If you are using '''FPC 3.2.0+''' a parametrisable form of this loop is available as the method '''RunCommandLoop''' in TProcess. An event '''OnRunCommandEvent''' can be hooked to further modify the behaviour.  
+
Jeśli chcesz odczytać dane wyjściowe z procesu zewnętrznego i nie możesz użyć RunCommand, jest to kod, który powinien być podstawą do użytku produkcyjnego. Jeśli używasz '''FPC 3.2.0+''', parametryzowalna forma tej pętli jest dostępna jako metoda '''RunCommandLoop''' w TProcess. Zdarzenie '''OnRunCommandEvent''' można podłączyć, aby dalej modyfikować zachowanie.
  
 
<syntaxhighlight lang="pascal">program LargeOutputDemo;
 
<syntaxhighlight lang="pascal">program LargeOutputDemo;
Line 416: Line 415:
  
 
uses
 
uses
   Classes, SysUtils, Process; // Process is the unit that holds TProcess
+
   Classes, SysUtils, Process; // Process to moduł, który zawiera TProcess
  
 
const
 
const
   BUF_SIZE = 2048; // Buffer size for reading the output in chunks
+
   BUF_SIZE = 2048; // Rozmiar bufora do odczytu danych wyjściowych w porcjach
  
 
var
 
var
Line 428: Line 427:
  
 
begin
 
begin
   // Set up the process; as an example a recursive directory search is used
+
   // Skonfiguruj proces; jako przykład używane jest rekurencyjne przeszukiwanie
   // because that will usually result in a lot of data.
+
   // katalogów, ponieważ zwykle skutkuje to dużą ilością danych.
 
   AProcess := TProcess.Create(nil);
 
   AProcess := TProcess.Create(nil);
  
   // The commands for Windows and *nix are different hence the $IFDEFs
+
   // Polecenia dla Windows i *nix są różne, stąd dyrektywy $IFDEF
 
   {$IFDEF Windows}
 
   {$IFDEF Windows}
     // In Windows the dir command cannot be used directly because it's a build-in
+
     // W systemie Windows polecenie dir nie może być użyte bezpośrednio, ponieważ jest to
     // shell command. Therefore cmd.exe and the extra parameters are needed.
+
     // wbudowane polecenie powłoki. Dlatego potrzebny jest cmd.exe i dodatkowe parametry.
 
     AProcess.Executable := 'c:\windows\system32\cmd.exe';
 
     AProcess.Executable := 'c:\windows\system32\cmd.exe';
 
     AProcess.Parameters.Add('/c');
 
     AProcess.Parameters.Add('/c');
Line 462: Line 461:
 
   {$ENDIF Unix}
 
   {$ENDIF Unix}
  
   // Process option poUsePipes has to be used so the output can be captured.
+
   // Aby można było przechwycić dane wyjściowe, należy użyć opcji procesu poUsePipes.
   // Process option poWaitOnExit can not be used because that would block
+
   // Nie można użyć opcji procesu poWaitOnExit, ponieważ zablokuje to program główny,
   // this program, preventing it from reading the output data of the process.
+
   // uniemożliwiając mu odczytanie danych wyjściowych procesu zewnętrznego.
 
   AProcess.Options := [poUsePipes];
 
   AProcess.Options := [poUsePipes];
  
   // Start the process (run the dir/ls command)
+
   // Uruchom proces (uruchom polecenie dir dla Windows lub ls dla Linux/Unix)
 
   AProcess.Execute;
 
   AProcess.Execute;
  
   // Create a stream object to store the generated output in. This could
+
   // Utwórz obiekt strumienia, w którym będą przechowywane wygenerowane dane wyjściowe.
   // also be a file stream to directly save the output to disk.
+
   // Może to być również strumień plikowy do bezpośredniego zapisywania danych wyjściowych na dysku.
 
   OutputStream := TMemoryStream.Create;
 
   OutputStream := TMemoryStream.Create;
  
   // All generated output from AProcess is read in a loop until no more data is available
+
   // Wszystkie wygenerowane dane wyjściowe z AProcess są odczytywane w pętli,
 +
  // dopóki nie będzie dostępnych więcej danych
 
   repeat
 
   repeat
     // Get the new data from the process to a maximum of the buffer size that was allocated.
+
     // Pobierz nowe dane z procesu do maksymalnego rozmiaru buforu, który został przydzielony.
     // Note that all read(...) calls will block except for the last one, which returns 0 (zero).
+
     // Zauważ, że wszystkie wywołania Read(...) będą blokowane z wyjątkiem ostatniego, które zwraca 0 (zero).
 
     BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE);
 
     BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE);
  
     // Add the bytes that were read to the stream for later usage
+
     // Bajty, które zostały odczytane, dodaj do strumienia w celu późniejszego wykorzystania
 
     OutputStream.Write(Buffer, BytesRead)
 
     OutputStream.Write(Buffer, BytesRead)
  
   until BytesRead = 0;  // Stop if no more data is available
+
   until BytesRead = 0;  // Zakończ pętlę, jeśli nie ma więcej danych
  
   // The process has finished so it can be cleaned up
+
   // Proces się zakończył, więc można go posprzątać
 
   AProcess.Free;
 
   AProcess.Free;
  
   // Now that all data has been read it can be used; for example to save it to a file on disk
+
   // Teraz, gdy wszystkie dane zostały odczytane, można z nich korzystać;
 +
  // na przykład po to, by zapisać je do pliku na dysku
 
   with TFileStream.Create('output.txt', fmCreate) do
 
   with TFileStream.Create('output.txt', fmCreate) do
 
   begin
 
   begin
     OutputStream.Position := 0; // Required to make sure all data is copied from the start
+
     OutputStream.Position := 0; // Wymagane, aby upewnić się, że wszystkie dane będą kopiowane od początku
 
     CopyFrom(OutputStream, OutputStream.Size);
 
     CopyFrom(OutputStream, OutputStream.Size);
 
     Free
 
     Free
 
   end;
 
   end;
  
   // Or the data can be shown on screen
+
   // Lub dane mogą być wyświetlane na ekranie
 
   with TStringList.Create do
 
   with TStringList.Create do
 
   begin
 
   begin
     OutputStream.Position := 0; // Required to make sure all data is copied from the start
+
     OutputStream.Position := 0; // Wymagane, aby upewnić się, że wszystkie dane będą kopiowane od początku
 
     LoadFromStream(OutputStream);
 
     LoadFromStream(OutputStream);
 
     writeln(Text);
 
     writeln(Text);
     writeln('--- Number of lines = ', Count, '----');
+
     writeln('--- Liczba linii = ', Count, '----');
 
     Free
 
     Free
 
   end;
 
   end;
  
   // Clean up
+
   // Sprzątanie
 
   OutputStream.Free;
 
   OutputStream.Free;
 
end.</syntaxhighlight>
 
end.</syntaxhighlight>
Note that the above could also be accomplished by using RunCommand:
+
Zauważ, że powyższe można również osiągnąć za pomocą RunCommand:
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Line 516: Line 517:
 
RunCommand('c:\windows\system32\cmd.exe', ['/c', 'dir /s c:\windows'], s);</syntaxhighlight>
 
RunCommand('c:\windows\system32\cmd.exe', ['/c', 'dir /s c:\windows'], s);</syntaxhighlight>
  
=== Using input and output of a TProcess ===
+
=== Korzystanie z wejścia i wyjścia TProcess ===
See processdemo example in the [https://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/examples/process Lazarus-CCR SVN].
+
Zobacz przykładowe demo procesu w [https://sourceforge.net/p/lazarus-ccr/svn/HEAD/tree/examples/process Lazarus-CCR SVN].
 
 
=== Hints on the use of TProcess ===
 
When creating a cross-platform program, the OS-specific executable name can be set using directives "{$IFDEF}" and "{$ENDIF}".
 
  
Example:
+
=== Wskazówki dotyczące korzystania z TProcess ===
 +
Podczas tworzenia programu wieloplatformowego nazwę pliku wykonywalnego specyficzną dla systemu operacyjnego można ustawić za pomocą dyrektyw „{$IFDEF}” i „{$ENDIF}”.
 +
Przykład:
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
 
{...}
 
{...}
Line 538: Line 538:
 
{...}</syntaxhighlight>
 
{...}</syntaxhighlight>
  
=== macOS show application bundle in foreground ===
+
=== macOS pokazuje pakiet aplikacji na pierwszym planie ===
  
You can start an '''application bundle''' via TProcess by starting the executable within the bundle. For example:
+
Możesz uruchomić '''pakiet aplikacji''' przez TProcess, uruchamiając plik wykonywalny w pakiecie. Na przykład:
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Line 546: Line 546:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
This will start the ''Calendar'', but the window will be behind the current application.
+
Spowoduje to uruchomienie ''Kalendarza'', ale okno będzie znajdować się za bieżącą aplikacją.
To get the application in the foreground you can use the '''open''' utility with the '''-n''' parameter:
+
Aby uzyskać aplikację na pierwszym planie, możesz użyć narzędzia '''open''' z parametrem '''-n''':
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
 
  AProcess.Executable:='/usr/bin/open';
 
  AProcess.Executable:='/usr/bin/open';
 
  AProcess.Parameters.Add('-n');
 
  AProcess.Parameters.Add('-n');
  AProcess.Parameters.Add('-a'); // optional: specifies the application to use; only searches the Application directories
+
  AProcess.Parameters.Add('-a'); // opcjonalny: określa aplikację do użycia; przeszukuje tylko katalogi aplikacji
  AProcess.Parameters.Add('-W'); // optional: open waits until the applications it opens (or were already open) have exited
+
  AProcess.Parameters.Add('-W'); // opcjonalnie: open czeka, aż aplikacje, które otwiera (lub były już otwarte) zostaną zamknięte
  AProcess.Parameters.Add('Pages.app'); // including .app is optional
+
  AProcess.Parameters.Add('Pages.app'); // dołączenie .app jest opcjonalne
 
</syntaxhighlight>
 
</syntaxhighlight>
  
If your application needs parameters, you can pass '''open''' the '''--args''' parameter, after which all parameters are passed to the application:
+
Jeśli Twoja aplikacja potrzebuje parametrów, możesz przekazać wraz z '''open''' parametr '''--args''', po którym wszystkie dalsze parametry są przekazywane do aplikacji:
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Line 565: Line 565:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
See also: [[macOS_Open_Sesame|macOS open command]].
+
Zobacz także: [[macOS_Open_Sesame|macOS polecenie open]].
  
=== Run detached program ===
+
=== Uruchom oddzielny program ===
  
Normally a program started by your application is a child process and is killed, when your application is killed. When you want to run a standalone program that keeps running, you can use the following:
+
Normalnie program uruchomiony przez twoją aplikację jest procesem potomnym i zostaje zabity, gdy twoja aplikacja zostanie zabita. Jeśli chcesz uruchomić samodzielny program, który nadal będzie działał, możesz użyć:
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Line 582: Line 582:
 
     Process.ShowWindow := swoShow;
 
     Process.ShowWindow := swoShow;
  
     // Copy default environment variables including DISPLAY variable for GUI application to work
+
     // Skopiuj domyślne zmienne środowiskowe, w tym zmienną DISPLAY, aby aplikacja GUI działała
 
     for I := 1 to GetEnvironmentVariableCount do
 
     for I := 1 to GetEnvironmentVariableCount do
 
       Process.Environment.Add(GetEnvironmentString(I));
 
       Process.Environment.Add(GetEnvironmentString(I));
Line 594: Line 594:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
=== Example of "talking" with aspell process ===
+
=== Przykład „rozmowy” z procesem aspell ===
  
Inside [https://github.com/pasdoc/pasdoc/wiki pasdoc] source code you can find two units that perform spell-checking by "talking" with running aspell process through pipes:
+
Wewnątrz kodu źródłowego [https://github.com/pasdoc/pasdoc/wiki pasdoc] można znaleźć dwa moduły, które sprawdzają pisownię poprzez „rozmawianie” przez potoki z uruchomionym procesem aspell:
  
* [https://github.com/pasdoc/pasdoc/blob/master/source/component/PasDoc_ProcessLineTalk.pas PasDoc_ProcessLineTalk.pas unit] implements TProcessLineTalk class, descendant of TProcess, that can be easily used to talk with any process on a line-by-line basis.
+
* [https://github.com/pasdoc/pasdoc/blob/master/source/component/PasDoc_ProcessLineTalk.pas Moduł PasDoc_ProcessLineTalk.pas] implementuje klasę TProcessLineTalk, potomka TProcess, która może być z łatwością używana do komunikacji z dowolnym procesem linia po linii.
  
* [https://github.com/pasdoc/pasdoc/blob/master/source/component/PasDoc_Aspell.pas PasDoc_Aspell.pas units] implements TAspellProcess class, that performs spell-checking by using underlying TProcessLineTalk instance to execute aspell and communicate with running aspell process.
+
* [https://github.com/pasdoc/pasdoc/blob/master/source/component/PasDoc_Aspell.pas Moduł PasDoc_Aspell.pas] implementuje klasę TAspellProcess, która sprawdza pisownię za pomocą bazowej instancji TProcessLineTalk do wykonania aspell i komunikacji z uruchomionym procesem aspell.
  
Both units are rather independent from the rest of pasdoc sources, so they may serve as real-world examples of using TProcess to run and communicate through pipes with other program.
+
Obydwa moduły są raczej niezależne od pozostałych źródeł pasdoc, więc mogą służyć jako rzeczywiste przykłady użycia TProcess do uruchamiania i komunikacji przez potoki z innym programem.
  
=== Replacing shell operators like "| < >" ===
+
=== Zastępowanie operatorów powłoki, takich jak „| < >===
  
Sometimes you want to run a more complicated command that pipes its data to another command or to a file.
+
Czasami chcesz uruchomić bardziej skomplikowane polecenie, które przesyła dane do innego polecenia lub do pliku. Coś podobnego do
Something like
 
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
 
ShellExecute('firstcommand.exe | secondcommand.exe');</syntaxhighlight>
 
ShellExecute('firstcommand.exe | secondcommand.exe');</syntaxhighlight>
or
+
lub
 
<syntaxhighlight lang="pascal">ShellExecute('dir > output.txt');</syntaxhighlight>
 
<syntaxhighlight lang="pascal">ShellExecute('dir > output.txt');</syntaxhighlight>
  
Executing this with TProcess will not work. i.e:
+
Wykonanie tego za pomocą TProcess nie zadziała. tj:
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
// this won't work
+
// to nie zadziała!
 
Process.CommandLine := 'firstcommand.exe | secondcommand.exe';  
 
Process.CommandLine := 'firstcommand.exe | secondcommand.exe';  
 
Process.Execute;</syntaxhighlight>
 
Process.Execute;</syntaxhighlight>
  
==== Why using special operators to redirect output doesn't work ====
+
==== Dlaczego używanie specjalnych operatorów do przekierowywania wyjścia nie działa? ====
TProcess is just that, it's not a shell environment, only a process. It's not two processes, it's only one.
+
TProcess nie jest środowiskiem powłoki, tylko procesem. To nie dwa procesy, to tylko jeden. Możliwe jest jednak przekierowanie danych wyjściowych dokładnie tak, jak chcesz. Zobacz [[Executing_External_Programs/pl#Jak_przekierować_wyjście_za_pomocą_TProcess|następną sekcję]].
It is possible to redirect output however just the way you wanted. See the [[Executing_External_Programs#How_to_redirect_output_with_TProcess |next section]].
 
  
=== How to redirect output with TProcess ===
+
=== Jak przekierować wyjście za pomocą TProcess ===
  
You can redirect the output of a command to another command by using a TProcess instance for '''each''' command.
+
Możesz przekierować dane wyjściowe polecenia do innego polecenia, używając osobnej instancji TProcess dla '''każdego''' polecenia.
  
Here's an example that explains how to redirect the output of one process to another. To redirect the output of a process to a file/stream see the example [[Executing_External_Programs#Reading_large_output | Reading Large Output ]]
+
Oto przykład, który wyjaśnia, jak przekierować dane wyjściowe jednego procesu do drugiego. Aby przekierować wyjście procesu do pliku/strumienia, zobacz przykład [[Executing_External_Programs/pl#Czytanie_dużych_ilości_danych_wyjściowych|Czytanie dużych ilości danych wyjściowych]].
  
Not only can you redirect the "normal" output (also known as stdout), but you can also redirect the error output (stderr), if you specify the poStderrToOutPut option, as seen in the options for the second process.
+
Możesz nie tylko przekierować „normalne” wyjście (znane również jako stdout), ale możesz także przekierować wyjście błędu (stderr), jeśli określisz opcję poStderrToOutPut, jak to widać w opcjach drugiego procesu.
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Line 654: Line 652:
 
   SecondProcess.Executable := 'grep';  
 
   SecondProcess.Executable := 'grep';  
 
   SecondProcess.Parameters.Add(DirectorySeparator+ ' -');  
 
   SecondProcess.Parameters.Add(DirectorySeparator+ ' -');  
   // this would be the same as "pwd | grep / -"
+
   // byłoby to to samo co "pwd | grep / -"
 
    
 
    
 
   FirstProcess.Execute;
 
   FirstProcess.Execute;
Line 663: Line 661:
 
     if FirstProcess.Output.NumBytesAvailable > 0 then
 
     if FirstProcess.Output.NumBytesAvailable > 0 then
 
     begin
 
     begin
       // make sure that we don't read more data than we have allocated
+
       // upewnij się, że nie czytamy więcej danych niż przydzieliliśmy w buforze
      // in the buffer
 
 
       ReadSize := FirstProcess.Output.NumBytesAvailable;
 
       ReadSize := FirstProcess.Output.NumBytesAvailable;
 
       if ReadSize > SizeOf(Buffer) then
 
       if ReadSize > SizeOf(Buffer) then
 
         ReadSize := SizeOf(Buffer);
 
         ReadSize := SizeOf(Buffer);
       // now read the output into the buffer
+
       // teraz wczytaj dane wyjściowe do bufora
 
       ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
 
       ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
       // and write the buffer to the second process
+
       // i zapisz bufor do drugiego procesu
 
       SecondProcess.Input.Write(Buffer[0], ReadCount);
 
       SecondProcess.Input.Write(Buffer[0], ReadCount);
 
    
 
    
       // if SecondProcess writes much data to it's Output then
+
       // jeśli SecondProcess zapisuje dużo danych do swojego wyjścia,
       // we should read that data here to prevent a deadlock
+
       // powinniśmy odczytać te dane tutaj, aby zapobiec zakleszczeniu,
       // see the previous example "Reading Large Output"
+
       // patrz poprzedni przykład "Czytanie dużych ilości danych wyjściowych"
 
     end;
 
     end;
 
   end;
 
   end;
   // Close the input on the SecondProcess
+
   // Zamknij dane wejściowe w SecondProcess,
   // so it finishes processing it's data
+
   // aby zakończyć przetwarzanie swoich danych
 
   SecondProcess.CloseInput;
 
   SecondProcess.CloseInput;
 
   
 
   
   // and wait for it to complete
+
   // i poczekaj, aż się zakończy, lecz uważaj, jakie polecenie uruchamiasz,
   // be carefull what command you run because it may not exit when
+
   // ponieważ może się ono nie zakończyć, gdy jego dane wejściowe są zamknięte,
   // it's input is closed and the following line may loop forever
+
   // to następująca linia może zapętlić się w nieskończoność
 
   while SecondProcess.Running do
 
   while SecondProcess.Running do
 
     Sleep(1);
 
     Sleep(1);
   // that's it! the rest of the program is just so the example
+
   // Gotowe! Reszta programu jest tylko po to, aby przykład był bardziej 'użyteczny'
  // is a little 'useful'
 
  
   // we will reuse Buffer to output the SecondProcess's
+
   // użyjemy ponownie Buffer, aby wyprowadzić wyjście SecondProcess do *tego* programu stdout
  // output to *this* programs stdout
+
   WriteLn('Rozpoczęcie wyjścia grep:');
   WriteLn('Grep output Start:');
 
 
   ReadSize := SecondProcess.Output.NumBytesAvailable;
 
   ReadSize := SecondProcess.Output.NumBytesAvailable;
 
   if ReadSize > SizeOf(Buffer) then
 
   if ReadSize > SizeOf(Buffer) then
Line 702: Line 697:
 
   end
 
   end
 
   else
 
   else
     WriteLn('grep did not find what we searched for. ', SecondProcess.ExitStatus);
+
     WriteLn('grep nie znalazł tego, czego szukaliśmy. ', SecondProcess.ExitStatus);
   WriteLn('Grep output Finish:');
+
   WriteLn('Zakończenie wyjścia grep:');
 
    
 
    
   // free our process objects
+
   // zwolnij obiekty naszych procesów
 
   FirstProcess.Free;
 
   FirstProcess.Free;
 
   SecondProcess.Free;
 
   SecondProcess.Free;
 
end.</syntaxhighlight>
 
end.</syntaxhighlight>
  
That's it. Now you can redirect output from one program to another.
+
W ten sposób możesz przekierować dane wyjściowe z jednego programu do drugiego.
  
==== Notes ====
+
==== Uwagi ====
This example may seem overdone since it's possible to run "complicated" commands using a shell with TProcess like:
+
Ten przykład może wydawać się przesadny, ponieważ możliwe jest uruchamianie „skomplikowanych” poleceń przy użyciu powłoki przy pomocy TProcess, takich jak:
 
<syntaxhighlight lang="pascal">Process.Commandline := 'sh -c "pwd | grep / -"';</syntaxhighlight>
 
<syntaxhighlight lang="pascal">Process.Commandline := 'sh -c "pwd | grep / -"';</syntaxhighlight>
  
But our example is more crossplatform since it needs no modification to run on Windows or Linux etc. "sh" may or may not exist on your platform and is generally only available on *nix platforms. Also we have more flexibility in our example since you can read and write from/to the input, output and stderr of each process individually, which could be very advantageous for your project.
+
Ale nasz przykład jest bardziej wieloplatformowy, ponieważ nie wymaga modyfikacji, aby działać w systemie Windows lub Linux itp. Polecenie „sh” może, ale nie musi istnieć na twojej platformie i jest ogólnie dostępny tylko na platformach *nix. W naszym przykładzie mamy również większą elastyczność, ponieważ możesz czytać i zapisywać z/do wejścia, wyjścia i stderr każdego procesu z osobna, co może być bardzo korzystne dla twojego projektu.
  
===Redirecting input and output and running under root===
+
===Przekierowywanie wejścia i wyjścia oraz działanie z rootem===
A common problem on Unixes (FreeBSD, macOS) and Linux is that you want to execute some program under the root account (or, more generally, another user account). An example would be running the ''ping'' command.
+
Częstym problemem w systemach Unix (FreeBSD, macOS) i Linux jest to, że chcesz uruchomić jakiś program na koncie root (lub, bardziej ogólnie, na innym koncie użytkownika). Przykładem może być uruchomienie polecenia ''ping''.
  
If you can use sudo for this, you could adapt the following example adapted from one posted by andyman on the forum ([http://lazarus.freepascal.org/index.php/topic,14479.0.html]). This sample runs <code>ls</code> on the <code>/root</code> directory, but can of course be adapted.
+
Jeśli możesz użyć do tego sudo, możesz dostosować następujący przykład zaadaptowany z postu opublikowanego przez andymana na forum ([http://lazarus.freepascal.org/index.php/topic,14479.0.html]). Ten przykład uruchamia polecenie <code>ls</code> w katalogu <code>/root</code>, ale oczywiście można go dostosować.
  
A '''better way''' to do this is to use the policykit package, which should be available on all recent Linuxes. [http://lazarus.freepascal.org/index.php/topic,14479.0.html See the forum thread for details.]
+
'''Lepszym sposobem''' na to jest użycie pakietu policykit, który powinien być dostępny we wszystkich najnowszych Linuksach. [http://lazarus.freepascal.org/index.php/topic,14479.0.html Zobacz wątek na forum, aby uzyskać szczegółowe informacje.]
  
Large parts of this code are similar to the earlier example, but it also shows how to redirect stdout and stderr of the process being called separately to stdout and stderr of our own code.
+
Duże części tego kodu są podobne do wcześniejszego przykładu, ale pokazuje również, jak przekierować stdout i stderr procesu wywoływanego oddzielnie na stdout i stderr naszego własnego kodu.
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
 
program rootls;
 
program rootls;
  
{ Demonstrates using TProcess, redirecting stdout/stderr to our stdout/stderr,
+
{ Demonstracja użycia TProcess, przekierowania stdout/stderr do naszego stdout/stderr,
calling sudo on FreeBSD/Linux/macOS, and supplying input on stdin}
+
wywoływania sudo w systemach FreeBSD/Linux/macOS i dostarczania danych wejściowych na stdin}
 
{$mode objfpc}{$H+}
 
{$mode objfpc}{$H+}
  
Line 749: Line 744:
 
     WriteLn('Please enter the sudo password:');
 
     WriteLn('Please enter the sudo password:');
 
     Readln(SudoPassword);
 
     Readln(SudoPassword);
     ExitCode := -1; //Start out with failure, let's see later if it works
+
     ExitCode := -1; //Zacznij od porażki, zobaczmy później, czy to zadziała
     Proc := TProcess.Create(nil); //Create a new process
+
     Proc := TProcess.Create(nil); //Utwórz nowy proces
 
     try
 
     try
       Proc.Options := [poUsePipes, poStderrToOutPut]; //Use pipes to redirect program stdin,stdout,stderr
+
       Proc.Options := [poUsePipes, poStderrToOutPut]; //Użyj potoków, aby przekierować stdin,stdout,stderr
       Proc.CommandLine := 'sudo -S ls /root'; //Run ls /root as root using sudo
+
       Proc.CommandLine := 'sudo -S ls /root'; //Uruchom ls /root jako root za pomocą sudo
       // -S causes sudo to read the password from stdin.
+
       // -S powoduje, że sudo odczytuje hasło z stdin.
       Proc.Execute; //start it. sudo will now probably ask for a password
+
       Proc.Execute; //Uruchom to. sudo prawdopodobnie poprosi teraz o hasło
  
       // write the password to stdin of the sudo program:
+
       // wpisz hasło do stdin programu sudo:
 
       SudoPassword := SudoPassword + LineEnding;
 
       SudoPassword := SudoPassword + LineEnding;
 
       Proc.Input.Write(SudoPassword[1], Length(SudoPassword));
 
       Proc.Input.Write(SudoPassword[1], Length(SudoPassword));
       SudoPassword := '%*'; //short string, hope this will scramble memory a bit; note: using PChars is more fool-proof
+
       SudoPassword := '%*'; // krótki string, mam nadzieję, że trochę pomiesza pamięć; uwaga: używanie PChars jest bardziej niezawodne
       SudoPassword := ''; // and make the program a bit safer from snooping?!?
+
       SudoPassword := ''; // i sprawić, by program był nieco bezpieczniejszy przed węszeniem?!?
  
       // main loop to read output from stdout and stderr of sudo
+
       // główna pętla do odczytu wyjścia z stdout i stderr dla sudo
 
       while Proc.Running or (Proc.Output.NumBytesAvailable > 0) or
 
       while Proc.Running or (Proc.Output.NumBytesAvailable > 0) or
 
         (Proc.Stderr.NumBytesAvailable > 0) do
 
         (Proc.Stderr.NumBytesAvailable > 0) do
 
       begin
 
       begin
         // read stdout and write to our stdout
+
         // odczytaj stdout i zapisz do naszego stdout
 
         while Proc.Output.NumBytesAvailable > 0 do
 
         while Proc.Output.NumBytesAvailable > 0 do
 
         begin
 
         begin
           ReadCount := Min(512, Proc.Output.NumBytesAvailable); //Read up to buffer, not more
+
           ReadCount := Min(512, Proc.Output.NumBytesAvailable); //Czytaj do bufora, nie więcej
 
           Proc.Output.Read(CharBuffer, ReadCount);
 
           Proc.Output.Read(CharBuffer, ReadCount);
 
           Write(StdOut, Copy(CharBuffer, 0, ReadCount));
 
           Write(StdOut, Copy(CharBuffer, 0, ReadCount));
 
         end;
 
         end;
         // read stderr and write to our stderr
+
         // odczytaj stderr i zapisz do naszego stderr
 
         while Proc.Stderr.NumBytesAvailable > 0 do
 
         while Proc.Stderr.NumBytesAvailable > 0 do
 
         begin
 
         begin
           ReadCount := Min(512, Proc.Stderr.NumBytesAvailable); //Read up to buffer, not more
+
           ReadCount := Min(512, Proc.Stderr.NumBytesAvailable); //Czytaj do bufora, nie więcej
 
           Proc.Stderr.Read(CharBuffer, ReadCount);
 
           Proc.Stderr.Read(CharBuffer, ReadCount);
 
           Write(StdErr, Copy(CharBuffer, 0, ReadCount));
 
           Write(StdErr, Copy(CharBuffer, 0, ReadCount));
Line 794: Line 789:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Other thoughts:
+
Inne przemyślenia:
It would no doubt be advisable to see if sudo actually prompts for a password. This can be checked consistently by setting the environment variable SUDO_PROMPT to something we watch for while reading the stdout of TProcess avoiding the problem of the prompt being different for different locales. Setting an environment variable causes the default values to be cleared(inherited from our process) so we have to copy the environment from our program if needed.
+
Bez wątpienia wskazane byłoby sprawdzenie, czy sudo rzeczywiście pyta o hasło. Można to konsekwentnie sprawdzać, ustawiając zmienną środowiskową SUDO_PROMPT na coś, na co zwracamy uwagę podczas czytania stdout TProcess, unikając problemu związanego z różnymi znakami zachęty dla różnych lokalizacji. Ustawienie zmiennej środowiskowej powoduje wyczyszczenie wartości domyślnych (odziedziczonych z naszego procesu), więc w razie potrzeby musimy skopiować środowisko z naszego programu.
  
=== Using fdisk with sudo on Linux ===
+
=== Używanie fdisk z sudo w systemie Linux ===
The following example shows how to run fdisk on a Linux machine using the sudo command to get root permissions. '''Note: this is an example only, and does not cater for large output.'''
+
Poniższy przykład pokazuje, jak uruchomić fdisk na komputerze z systemem Linux za pomocą polecenia sudo, aby uzyskać uprawnienia roota. '''Uwaga: jest to tylko przykład i nie obejmuje dużej ilości danych na wyjściu.'''
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
 
program getpartitioninfo;
 
program getpartitioninfo;
{Originally contributed by Lazarus forums wjackon153. Please contact him for questions, remarks etc.
+
{Pierwotnie napisany przez użytkownika forum Lazarus wjackon153. Prosimy o kontakt w przypadku pytań, uwag itp.
Modified from Lazarus snippet to FPC program for ease of understanding/conciseness by BigChimp}
+
Zmodyfikowan z fragmentu kodu Lazarusa na program FPC dla ułatwienia zrozumienia/zwięzłości przez BigChimp}
  
 
Uses
 
Uses
Line 814: Line 809:
  
 
begin   
 
begin   
   sPass := 'yoursudopasswordhere'; // You need to change this to your own sudo password
+
   sPass := 'yoursudopasswordhere'; // Musisz to zmienić na własne hasło sudo
   OutputLines:=TStringList.Create; //... a try...finally block would be nice to make sure
+
   OutputLines:=TStringList.Create; //dla pewności warto zamknąć to w bloku try...finally...end
   // OutputLines is freed... Same for hProcess.
+
   // OutputLines został utworzony ... To samo dla hProcess.
 
      
 
      
   // The following example will open fdisk in the background and give us partition information
+
   // Poniższy przykład otworzy fdisk w tle i da nam informacje o partycjach
   // Since fdisk requires elevated priviledges we need to
+
   // Ponieważ fdisk wymaga podwyższonych uprawnień, musimy przekazać
   // pass our password as a parameter to sudo using the -S
+
   // nasze hasło jako parametr do sudo za pomocą opcji -S, więc proces
   // option, so it will will wait till our program sends our password to the sudo application
+
   // poczeka, aż nasz program wyśle ​​nasze hasło do aplikacji sudo
 +
 
 
   hProcess := TProcess.Create(nil);
 
   hProcess := TProcess.Create(nil);
   // On Linux/Unix/FreeBSD/macOS, we need specify full path to our executable:
+
   // W systemach Linux/Unix/FreeBSD/macOS musimy podać pełną ścieżkę do naszego pliku wykonywalnego:
 
   hProcess.Executable := '/bin/sh';
 
   hProcess.Executable := '/bin/sh';
   // Now we add all the parameters on the command line:
+
   // Teraz dodajemy wszystkie parametry w wierszu poleceń:
 
   hprocess.Parameters.Add('-c');
 
   hprocess.Parameters.Add('-c');
   // Here we pipe the password to the sudo command which then executes fdisk -l:  
+
   // Tutaj przesyłamy potokiem hasło do polecenia sudo, które następnie wykonuje fdisk -l:  
 
   hprocess.Parameters.add('echo ' + sPass  + ' | sudo -S fdisk -l');
 
   hprocess.Parameters.add('echo ' + sPass  + ' | sudo -S fdisk -l');
   // Run asynchronously (wait for process to exit) and use pipes so we can read the output pipe
+
   // Uruchom asynchronicznie (poczekaj na zakończenie procesu) i użyj potoków, abyśmy mogli odczytać potok wyjściowy
 
   hProcess.Options := hProcess.Options + [poWaitOnExit, poUsePipes];
 
   hProcess.Options := hProcess.Options + [poWaitOnExit, poUsePipes];
   // Now run:
+
   // Teraz wykonaj:
 
   hProcess.Execute;
 
   hProcess.Execute;
  
   // hProcess should have now run the external executable (because we use poWaitOnExit).
+
   // hProcess powinien teraz uruchomić zewnętrzny plik wykonywalny (ponieważ używamy poWaitOnExit).
   // Now you can process the process output (standard output and standard error), eg:
+
   // Teraz możesz przetworzyć wyjście procesu (standardowe wyjście i standardowy błąd), np.:
 
   OutputLines.Add('stdout:');
 
   OutputLines.Add('stdout:');
 
   OutputLines.LoadFromStream(hprocess.Output);
 
   OutputLines.LoadFromStream(hprocess.Output);
 
   OutputLines.Add('stderr:');
 
   OutputLines.Add('stderr:');
 
   OutputLines.LoadFromStream(hProcess.Stderr);
 
   OutputLines.LoadFromStream(hProcess.Stderr);
   // Show output on screen:
+
   // Pokaż dane wyjściowe na ekranie:
 
   writeln(OutputLines.Text);
 
   writeln(OutputLines.Text);
  
   // Clean up to avoid memory leaks:
+
   // Wyczyść, aby uniknąć wycieków pamięci:
 
   hProcess.Free;
 
   hProcess.Free;
 
   OutputLines.Free;
 
   OutputLines.Free;
 
    
 
    
   //Below are some examples as you see we can pass illegal characters just as if done from terminal
+
   //Poniżej znajduje się kilka przykładów, jak widać, możemy przekazywać niedozwolone znaki, tak jak robimy to z terminala
   //Even though you have read elsewhere that you can not I assure with this method you can :)
+
   //Nawet jeśli przeczytałeś gdzie indziej, że nie możesz upewnić się tą metodą, to możesz :)
  
 
   //hprocess.Parameters.Add('ping -c 1 www.google.com');
 
   //hprocess.Parameters.Add('ping -c 1 www.google.com');
 
   //hprocess.Parameters.Add('ifconfig wlan0 | grep ' +  QuotedStr('inet addr:') + ' | cut -d: -f2');
 
   //hprocess.Parameters.Add('ifconfig wlan0 | grep ' +  QuotedStr('inet addr:') + ' | cut -d: -f2');
  
   //Using QuotedStr() is not a requirement though it makes for cleaner code;
+
   //Użycie QuotedStr() nie jest wymagane, chociaż zapewnia czystszy kod;
   //you can use double quote and have the same effect.
+
   //możesz użyć podwójnego cudzysłowu i mieć ten sam efekt.
  
 
   //hprocess.Parameters.Add('glxinfo | grep direct');   
 
   //hprocess.Parameters.Add('glxinfo | grep direct');   
  
   // This method can also be used for installing applications from your repository:
+
   // Ta metoda może być również używana do instalowania aplikacji z repozytorium:
  
 
   //hprocess.Parameters.add('echo ' + sPass  + ' | sudo -S apt-get install -y pkg-name');  
 
   //hprocess.Parameters.add('echo ' + sPass  + ' | sudo -S apt-get install -y pkg-name');  
Line 865: Line 861:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===Parameters which contain spaces (Replacing Shell Quotes)===
+
===Parametry zawierające spacje (Zastępowanie cudzysłowów powłoki)===
  
In the Linux shell it is possible to write quoted arguments like this:
+
W powłoce Linuksa możliwe jest pisanie cytowanych argumentów w następujący sposób:
  
 
  gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram
 
  gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram
  
And GDB will receive 3 arguments (in addition to the first argument which is the full path to the executable):
+
Polecenie GDB otrzyma 3 argumenty (oprócz pierwszego argumentu, który jest pełną ścieżką do pliku wykonywalnego):
 
#--batch
 
#--batch
 
#--eval-command=info symbol 0x0000DDDD
 
#--eval-command=info symbol 0x0000DDDD
#the full path to myprogram
+
#pełna ścieżka do myprogram
  
The best solution to avoid complicated quoting is to to switch to TProcess.Parameters.Add instead of setting the Commandline for non trivial cases e.g.
+
Najlepszym rozwiązaniem, aby uniknąć skomplikowanego cytowania, jest przejście na TProcess.Parameters.Add zamiast ustawiania wiersza poleceń dla nietrywialnych przypadków, np.
  
 
   AProcess.Executable:='/usr/bin/gdb';
 
   AProcess.Executable:='/usr/bin/gdb';
 
   AProcess.Parameters.Add('--batch');
 
   AProcess.Parameters.Add('--batch');
   AProcess.Parameters.Add('--eval-command=info symbol 0x0000DDDD'); // note the absence of quoting here
+
   AProcess.Parameters.Add('--eval-command=info symbol 0x0000DDDD'); // zwróć tutaj uwagę na brak cytowania
 
   AProcess.Parameters.Add('/home/me/myprogram');
 
   AProcess.Parameters.Add('/home/me/myprogram');
  
And also remember to only pass full paths.
+
Pamiętaj też, aby używać tylko pełne ścieżki.
  
TProcess.Commandline however does support some basic quoting for parameters with quoting. Quote the ''whole'' parameter containing spaces with double quotes. Like this:
+
TProcess.Commandline obsługuje jednak tylko podstawowe cytowanie parametrów z cudzysłowami. Ujmij w apostrofy ''cały'' parametr zawierający spacje z podwójnymi cudzysłowami. Jak poniżej:
  
 
  AProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';
 
  AProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';
  
 +
Właściwość .CommandLine jest [http://bugs.freepascal.org/view.php?id=14446 przestarzała], a raporty o błędach do obsługi bardziej skomplikowanych przypadków cytowania nie będą akceptowane.
  
The .CommandLine property is [http://bugs.freepascal.org/view.php?id=14446 deprecated] and bugreports to support more complicated quoting cases won't accepted.
+
==Alternatywy LCLIntf==
 
+
Czasami nie trzeba jawnie wywoływać zewnętrznego programu, aby uzyskać potrzebną funkcjonalność. Zamiast otwierać aplikację i określać dokument, który ma być z nią powiązany, po prostu poproś system operacyjny o otwarcie dokumentu i pozwól mu użyć domyślnej aplikacji powiązanej z tym typem pliku. Poniżej kilka przykładów.
==LCLIntf Alternatives==
 
Sometimes, you don't need to explicitly call an external program to get the functionality you need. Instead of opening an application and specifying the document to go with it, just ask the OS to open the document and let it use the default application associated with that file type. Below are some examples.
 
  
===Open document in default application===
+
===Otwieranie dokumentu w domyślnej aplikacji===
  
In some situations you need to open some document/file using default associated application rather than execute a particular program.  
+
W niektórych sytuacjach musisz otworzyć jakiś dokument/plik przy użyciu domyślnej aplikacji z nim powiązanej, zamiast uruchamiać konkretny program. To zależy od działającego systemu operacyjnego. Lazarus zapewnia niezależną od platformy procedurę OpenDocument, która zajmie się tym za Ciebie. Twoja aplikacja będzie nadal działać, nie czekając na zamknięcie procesu tworzenia dokumentu.
This depends on running operating system. Lazarus provides a platform independent procedure '''OpenDocument''' which will handle it for you. Your application will continue running without waiting for the document process to close.
 
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Line 907: Line 901:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
* [[opendocument|OpenDocument reference]]
+
* [[opendocument|Dokumentacja OpenDocument]]
  
===Open web page in default web browser===
+
===Otwieranie strony web w domyślnej przeglądarce internetowej===
  
Just pass the URL required, the leading http:// appears to be optional under certain circumstances.  
+
Aby to zrobić, po prostu przekaż wymagany adres URL. Przedrostek protokołu http:// wydaje się w pewnych okolicznościach opcjonalny. Ponadto przekazanie nazwy pliku wydaje się dawać takie same wyniki jak OpenDocument()
Also, passing a filename appears to give the same results as OpenDocument()
 
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
 
uses LCLIntf;
 
uses LCLIntf;
Line 919: Line 912:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
See also:
+
Zobacz także:
* [[OpenURL|OpenURL reference]]
+
* [[OpenURL|Odnośnik do OpenURL]]
  
Or, you could use '''TProcess''' like this:
+
Lub możesz użyć '''TProcess''' w taki sposób:
  
 
<syntaxhighlight lang="pascal">
 
<syntaxhighlight lang="pascal">
Line 928: Line 921:
  
 
procedure OpenWebPage(URL: string);
 
procedure OpenWebPage(URL: string);
// Apparently you need to pass your URL inside ", like "www.lazarus.freepascal.org"
+
// Możliwe, że musisz podać swój adres URL w cudzysłowiu np. "www.lazarus.freepascal.org"
 
var
 
var
 
   Browser, Params: string;
 
   Browser, Params: string;
Line 937: Line 930:
 
     Executable := Browser;
 
     Executable := Browser;
 
     Params:=Format(Params, [URL]);
 
     Params:=Format(Params, [URL]);
     Params:=copy(Params,2,length(Params)-2); // remove "", the new version of TProcess.Parameters does that itself
+
     Params:=copy(Params,2,length(Params)-2); // usuń znaki "", nowa wersja TProcess.Parameters robi to sama
 
     Parameters.Add(Params);
 
     Parameters.Add(Params);
 
     Options := [poNoConsole];
 
     Options := [poNoConsole];

Latest revision as of 11:28, 3 November 2023

Deutsch (de) English (en) español (es) français (fr) italiano (it) 日本語 (ja) Nederlands (nl) polski (pl) português (pt) русский (ru) slovenčina (sk) 中文(中国大陆)‎ (zh_CN)

Wykonywanie programów zewnętrznych

Przegląd: Porównanie

Istnieją różne sposoby dostępne w bibliotekach RTL, FCL i LCL dotyczące wykonywania zewnętrznego polecenia/procesu/programu.

Metoda Biblioteka Platformy Pojedyncza linia Cechy
ExecuteProcess RTL Wieloplatformowy Tak Bardzo ograniczone, synchroniczne.
ShellExecute WinAPI Tylko MS Windows Tak Wiele. Może uruchamiać programy z podwyższonymi uprawnieniami administratora.
fpsystem, fpexecve Unix Tylko Unix
TProcess FCL Wieloplatformowy Nie Pełny.
RunCommand FCL Wieloplatformowy Wymaga FPC 2.6.2+ Tak Obejmuje typowe użycie TProcess.
OpenDocument LCL Wieloplatformowy Tak Tylko otwarty dokument. Dokument otworzy się z aplikacją powiązaną z podanym typem dokumentu.

(Proces.)RunCommand

W FPC 2.6.2 niektóre funkcje pomocnicze dla TProcess zostały dodane do modułu process w oparciu o wrappery (funkcje lub klasy opakowujące) używane w projekcie fpcup. Te funkcje są przeznaczone do użytku podstawowego i pośredniego, i mogą przechwytywać dane wyjściowe do pojedynczego ciągu string i w pełni obsługiwać duże dane wyjściowe.

Prosty przykład to:

program project1;

{$mode objfpc}{$H+}

uses 
  Process;

var 
  s : ansistring;

begin

if RunCommand('/bin/bash',['-c','echo $PATH'],s) then
   writeln(s); 

end.

Zauważ jednak, że nie wszystkie „wbudowane” polecenia powłoki (np. aliasy) działają, ponieważ aliasy domyślnie nie są rozwijane w powłokach nieinteraktywnych, a .bashrc nie jest odczytywany przez powłoki nieinteraktywne, chyba że ustawisz zmienną środowiskową BASH_ENV. tak więc poniższy przykąłd nie daje żadnych danych wyjściowych:

program project2;

{$mode objfpc}{$H+}

uses 
  Process;

var 
  s : ansistring;

begin

if RunCommand('/bin/bash',['-c','alias'],s) then
  writeln(s); 

end.

Przeciążony wariant RunCommand zwraca kod wyjścia programu. RunCommandInDir uruchamia polecenie w innym katalogu (ustawia p.CurrentDirectory):

function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string; var exitstatus:integer): integer;
function RunCommandIndir(const curdir:string;const exename:string;const commands:array of string;var outputstring:string): boolean;
function RunCommand(const exename:string;const commands:array of string;var outputstring:string): boolean;

W FPC 3.2.0+ RunCommand otrzymał dodatkowe warianty, które pozwalają nadpisać TProcessOptions i WindowOptions.

Rozszerzenia RunCommand

W FPC 3.2.0+ implementacja RunCommand została uogólniona i ponownie zintegrowana z TProcess, aby umożliwić szybszą budowę własnych wariantów. Jako przykład wariant RunCommand z limitem czasu:

  
{$mode delphi}
uses classes, sysutils, process, dateutils;
type
 { TProcessTimeout }
 TProcessTimeout = class(TProcess)
                   public
                     timeoutperiod: TTime;
                     timedout : boolean;
                     started : TDateTime;
                     procedure LocalnIdleSleep(Sender,Context : TObject;status:TRunCommandEventCode;const message:string);
                   end;
 
procedure TProcessTimeout.LocalnIdleSleep(Sender,Context : TObject;status:TRunCommandEventCode;const message:string);
begin
   if status=RunCommandIdle then
    begin
      if (now-started)>timeoutperiod then
         begin
           timedout:=true;
           Terminate(255);
           exit;
         end;
      sleep(RunCommandSleepTime);
    end;
end;
 
function RunCommandTimeout(const exename:TProcessString;const commands:array of TProcessString;out outputstring:string; Options : TProcessOptions = [];SWOptions:TShowWindowOptions=swoNone;timeout:integer=60):boolean;
Var
    p : TProcessTimeout;
    i,
    exitstatus : integer;
    ErrorString : String;
begin
  p:=TProcessTimeout.create(nil);
  p.OnRunCommandEvent:=p.LocalnIdleSleep;
  p.timeoutperiod:=timeout/SecsPerDay;
  if Options<>[] then
    P.Options:=Options - [poRunSuspended,poWaitOnExit];
  p.options:=p.options+[poRunIdle]; // potrzebne do uruchomienia zdarzenia RUNIDLE. Zobacz Zmiany Użytkownika dla wersji 3.2.0
 
  P.ShowWindow:=SwOptions;
  p.Executable:=exename;
  if high(commands)>=0 then
   for i:=low(commands) to high(commands) do
     p.Parameters.add(commands[i]);
  p.timedout:=false;
  p.started:=now;
  try
    // główna pętla wariantów runcommand(), pierwotnie oparta na scenariuszu "dużego wyjścia" na wiki, ale stale rozwijana przez 5 lat.
    result:=p.RunCommandLoop(outputstring,errorstring,exitstatus)=0;
    if p.timedout then
      result:=false;
  finally
    p.free;
  end;
  if exitstatus<>0 then result:=false;
end;                                      
 
// przykładowe zastosowanie:
 
var
  s : string;
begin
  for s in FileList do
    begin
       if not RunCommandTimeout('someexe',['-v',s,'--output','dest\'+s],err,[],swoNone,60) then
          begin
            // nie udało się uruchomić lub upłynął limit czasu. np. movefile() do "wadliwego" katalogu.
         end
      else
        begin
         // ok, plik przeniesiony do "dobrego" katalogu.
        end;
    end;
end;

SysUtils.ExecuteProcess

(Wieloplatformowy)
Pomimo wielu ograniczeń, najprostszym sposobem uruchomienia programu (modalnego, bez potoków lub jakiejkolwiek formy sterowania) jest po prostu użycie:

SysUtils.ExecuteProcess(UTF8ToSys('/full/path/to/binary'), '', []);

Proces wywołujący działa synchronicznie: „zawiesza się” do momentu zakończenia działania zewnętrznego programu — ale może to być przydatne, jeśli wymagasz od użytkownika wykonania czegoś przed kontynuowaniem pracy w aplikacji. Aby uzyskać bardziej wszechstronne podejście, zapoznaj się z sekcją dotyczącą preferowanego międzyplatformowego RunCommand lub innej funkcjonalności TProcess, lub jeśli chcesz obsługiwać tylko system Windows, możesz użyć ShellExecute.

MS Windows: CreateProcess, ShellExecute i WinExec

Light bulb  Uwaga: Chociaż FPC/Lazarus obsługuje CreateProcess, ShellExecute i/lub WinExec, ta obsługa jest dostępna tylko w Win32/64. Jeśli Twój program jest wieloplatformowy, rozważ użycie RunCommand lub TProcess.
Light bulb  Uwaga: WinExec to 16-bitowe wywołanie, które od lat jest przestarzałe w interfejsie API systemu Windows. W ostatnich wersjach FPC generuje ostrzeżenie.

ShellExecute to standardowa funkcja systemu MS Windows (ShellApi.h) z dobrą dokumentacją na MSDN (zwróć uwagę na ich komentarze na temat inicjalizacji COM, jeśli odkryjesz, że ta jest niewiarygodna).

uses ..., ShellApi;

// Proste jednowierszowe (ignorowanie zwracanych błędów):
if ShellExecute(0,nil, PChar('"C:\my dir\prog.exe"'),PChar('"C:\somepath\some_doc.ext"'),nil,1) =0 then;

// Wykonaj plik wsadowy:
if ShellExecute(0,nil, PChar('cmd'),PChar('/c mybatch.bat'),nil,1) =0 then;

// Otwórz okno poleceń w danym folderze:
if ShellExecute(0,nil, PChar('cmd'),PChar('/k cd \path'),nil,1) =0 then;

// Otwórz adres URL strony internetowej w domyślnej przeglądarce za pomocą polecenia „start” (poprzez ukryte okno cmd):
if ShellExecute(0,nil, PChar('cmd'),PChar('/c start www.lazarus.freepascal.org/'),nil,0) =0 then;

// lub przydatna procedura:
procedure RunShellExecute(const prog,params:string);
begin
  // parametry: ( Uchwyt, nil/'open'/'edit'/'find'/'explore'/'print',   // 'open' nie zawsze jest potrzebny
  //               path+prog, parametry, folder roboczy,
  //        okno command 0=ukryte / 1=SW_SHOWNORMAL / 3=maks / 7=min)   // dla stałych SW_ : uses ... Windows ...
  if ShellExecute(0,'open',PChar(prog),PChar(params),PChar(extractfilepath(prog)),1) >32 then; //sukces
  // zwracane wartości 0..32 są błędami
end;

Istnieje również ShellExecuteExW jako wersja WideChar, a ShellExecuteExA to wersja AnsiChar.

Opcja fMask może również używać SEE_MASK_DOENVSUBST lub SEE_MASK_FLAG_NO_UI lub SEE_MASK_NOCLOSEPROCESS itp.

Jeśli w Delphi używałeś ShellExecute do dokumentów takich jak dokumenty Word lub adresy URL, spójrz na funkcje open* (OpenURL itp.) w module lclintf (zobacz sekcję Zobacz także na dole tej strony).

Używanie ShellExecuteEx do podnoszenia uprawnień administratora

Jeśli potrzebujesz uruchomić zewnętrzny program z podwyższonymi uprawnieniami administratora, możesz użyć metody runas z alternatywną funkcją ShellExecuteEx:

uses ShellApi, ...;

function RunAsAdmin(const Handle: Hwnd; const Path, Params: string): Boolean;
var
  sei: TShellExecuteInfoA;
begin
  FillChar(sei, SizeOf(sei), 0);
  sei.cbSize := SizeOf(sei);
  sei.Wnd := Handle;
  sei.fMask := SEE_MASK_FLAG_DDEWAIT or SEE_MASK_FLAG_NO_UI;
  sei.lpVerb := 'runas';
  sei.lpFile := PAnsiChar(Path);
  sei.lpParameters := PAnsiChar(Params);
  sei.nShow := SW_SHOWNORMAL;
  Result := ShellExecuteExA(@sei);
end;

procedure TFormMain.RunAddOrRemoveApplication;
begin
  // Przykład, który używa podwyższonego uprawnienia dla rundll, aby otworzyć Panel sterowania do programów i funkcji
  RunAsAdmin(FormMain.Handle, 'rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl', '');
end;

Unix fpsystem, fpexecve i shell

Te funkcje są zależne od platformy.

Linux.Shell/Unix.Shell był odpowiednikiem fpsystem w wersji 1.0.x ze słabo zdefiniowaną obsługą błędów i po dekadzie deprecjacji został ostatecznie usunięty. Prawie we wszystkich przypadkach może być zastąpiony przez fpsystem, który obsługuje więcej standardów POSIX, takich jak obsługa błędów.

TProcess

Możesz użyć TProcess do uruchamiania zewnętrznych programów. Niektóre z korzyści płynących z używania TProcess to:

  • Jest niezależny od platformy.
  • Potrafi czytać ze standardowego wyjścia i zapisywać na standardowe wejście.
  • Możliwe jest oczekiwanie na zakończenie polecenia lub pozwolenie na jego działanie podczas działania programu.

Ważne informacje:

  • TProcess nie jest terminalem/powłoką! Nie możesz bezpośrednio wykonywać skryptów lub przekierowywać wyjścia za pomocą operatorów takich jak „|”, „>”, „<”, „&” itp. Możliwe jest uzyskanie tych samych wyników za pomocą TProcess i za pomocą pascala - kilka przykładów poniżej.
  • Przypuszczalnie w systemie Linux/Unix: musisz podać pełną ścieżkę do pliku wykonywalnego. Na przykład „/bin/cp” zamiast „cp”. Jeśli program znajduje się w standardowej PATH, możesz użyć funkcji FindDefaultExecutablePath z modułu FileUtil biblioteki LCL.
  • W systemie Windows, jeśli polecenie znajduje się w ścieżce, nie musisz określać pełnej ścieżki.
  • Odnośnik do TProcess

Najprostszy przykład

Wiele typowych przypadków zostało przygotowanych w funkcjach RunCommand. Zanim zaczniesz kopiować i wklejać poniższe przykłady, najpierw je sprawdź.

Prosty przykład

Ten przykład (który nie powinien być używany w środowisku produkcyjnym, patrz Czytanie dużych ilości danych wyjściowych lub, lepiej, RunCommand) pokazuje tylko, jak uruchomić zewnętrzny program, nic więcej:

// Jest to program demonstracyjny, który
// pokazuje, jak uruchomić zewnętrzny program.
program launchprogram;
 
// Tutaj dołączamy moduły zawierające przydatne
// funkcje i procedury, których będziemy potrzebować.
uses 
  Classes, SysUtils, Process;
 
// Definiuje zmienną "AProcess" jako zmienną typu "TProcess"
var 
  AProcess: TProcess;
 
// Tu zaczyna działać nasz program
begin
  // Teraz utworzymy obiekt TProcess i przypiszemy go
  // do zmiennej AProcess.
  AProcess := TProcess.Create(nil);
 
  // Powiedz nowemu AProcess, jakie jest polecenie do wykonania.
  // Użyjmy kompilatora Free Pascal (dokładniej wersji x64)
  // Lecz jeśli używasz kompilatora 32-bit zmień ppcx64 na ppc386
  AProcess.Executable:= 'ppcx64';

  // Przekaż opcję -h razem z ppcx64, więc faktycznie wykonywane
  // jest polecenie 'ppcx64 -h':
  AProcess.Parameters.Add('-h');
 
  // Zdefiniujemy opcję działającą, w czasie wykonywania programu.
  // Ta opcja sprawi, że nasz program nie będzie kontynuowany,
  // dopóki program zewnętrzy, który uruchomimy, nie przestanie działać.
  //                                     /------------\
  AProcess.Options := AProcess.Options + [poWaitOnExit];
 
  // Teraz pozwól AProcess uruchomić program zewnętrzny
  AProcess.Execute;
 
  // Poniższy kod nie wykona się, dopóki ppcx64 nie przestanie działać.
  AProcess.Free;   
end.

Otóż ​​to! Właśnie nauczyłeś się uruchamiać zewnętrzny program z wnętrza własnego programu.

Ulepszony przykład (ale jeszcze niepoprawny)

Wszystko fajnie, ale jak odczytać dane wyjściowe uruchomionego programu?

Cóż, rozszerzmy nieco nasz przykład, jak poniżej: Ten przykład jest prosty, więc możesz się z niego uczyć. Jednak nie używaj tego przykładu w kodzie produkcyjnym, ale użyj kodu z Czytanie dużych ilości danych wyjściowych.

// To jest
// WADLIWY
// program demonstracyjny, który pokazuje,
// jak uruchomić zewnętrzny program
// i odczytać z jego dane wyjściowe.
program launchprogram;
 
// Tutaj dołączamy moduły zawierające przydatne
// funkcje i procedury, których będziemy potrzebować.
uses 
  Classes, SysUtils, Process;
 
// Definiuje zmienną "AProcess" jako zmienną typu "TProcess"
// Teraz także dodajemy TStringList do przechowywania danych
// odczytanych z wyjścia programu.
var 
  AProcess: TProcess;
  AStringList: TStringList;

// Tu zaczyna działać nasz program
begin
  // Teraz utworzymy obiekt TProcess i przypiszemy go
  // do zmiennej AProcess.
  AProcess := TProcess.Create(nil);
 
  // Powiedz nowemu AProcess, jakie jest polecenie do wykonania.
  AProcess.Executable := '/usr/bin/ppcx64'; 
  AProcess.Parameters.Add('-h'); 

  // Zdefiniujemy opcję działającą, w czasie wykonywania programu.
  // Ta opcja sprawi, że nasz program nie będzie kontynuowany,
  // dopóki program zewnętrzy, który uruchomimy, nie przestanie działać.
  // Dodatkowo teraz powiemy mu, że chcemy odczytać dane wyjściowe.
  //                                     /------------|-----------\
  AProcess.Options := AProcess.Options + [poWaitOnExit, poUsePipes];
 
  // Teraz, gdy AProcess wie, jaki jest wiersz poleceń, można go uruchomić.
  AProcess.Execute;
  
  // Po zakończeniu AProcess zostanie wykonana reszta programu.
 
  // Teraz odczytaj wynik działania programu, za pomocą TStringList.
  AStringList := TStringList.Create;
  AStringList.LoadFromStream(AProcess.Output);
   
  // Zapisz dane wyjściowe do pliku i zwolnij obiekt AStringList.
  AStringList.SaveToFile('output.txt');
  AStringList.Free;
 
  // Teraz, gdy dane wyjściowe z procesu są przetworzone, także można je zwolnić.
  AProcess.Free;   
end.

Czytanie dużych ilości danych wyjściowych

W poprzednim przykładzie czekaliśmy, aż program się zakończy. Następnie czytaliśmy, co program zapisał na swoim wyjściu.

Załóżmy, że program zapisuje dużo danych na wyjściu. Następnie potok wyjściowy zapełnia się i wywoływany program czeka, aż potok zostanie odczytany.

Ale program wywołujący nie czyta z niego, dopóki wywoływany program się nie zakończy. Następuje impas.

Poniższy przykład nie używa zatem poWaitOnExit, ale odczytuje dane wyjściowe, gdy program jest nadal uruchomiony. Dane wyjściowe są przechowywane w strumieniu pamięci, który można później wykorzystać do odczytania danych wyjściowych do TStringList.

Jeśli chcesz odczytać dane wyjściowe z procesu zewnętrznego i nie możesz użyć RunCommand, jest to kod, który powinien być podstawą do użytku produkcyjnego. Jeśli używasz FPC 3.2.0+, parametryzowalna forma tej pętli jest dostępna jako metoda RunCommandLoop w TProcess. Zdarzenie OnRunCommandEvent można podłączyć, aby dalej modyfikować zachowanie.

program LargeOutputDemo;

{$mode objfpc}{$H+}

uses
  Classes, SysUtils, Process; // Process to moduł, który zawiera TProcess

const
  BUF_SIZE = 2048; // Rozmiar bufora do odczytu danych wyjściowych w porcjach

var
  AProcess     : TProcess;
  OutputStream : TStream;
  BytesRead    : longint;
  Buffer       : array[1..BUF_SIZE] of byte;

begin
  // Skonfiguruj proces; jako przykład używane jest rekurencyjne przeszukiwanie
  // katalogów, ponieważ zwykle skutkuje to dużą ilością danych.
  AProcess := TProcess.Create(nil);

  // Polecenia dla Windows i *nix są różne, stąd dyrektywy $IFDEF
  {$IFDEF Windows}
    // W systemie Windows polecenie dir nie może być użyte bezpośrednio, ponieważ jest to
    // wbudowane polecenie powłoki. Dlatego potrzebny jest cmd.exe i dodatkowe parametry.
    AProcess.Executable := 'c:\windows\system32\cmd.exe';
    AProcess.Parameters.Add('/c');
    AProcess.Parameters.Add('dir /s c:\windows');
  {$ENDIF Windows}

  {$IFDEF Unix}
    AProcess.Executable := '/bin/ls';

    {$IFDEF Darwin}
      AProcess.Parameters.Add('-recursive');
      AProcess.Parameters.Add('-all');
    {$ENDIF Darwin}

    {$IFDEF Linux}
      AProcess.Parameters.Add('--recursive');
      AProcess.Parameters.Add('--all');
    {$ENDIF Linux}

    {$IFDEF FreeBSD}
      AProcess.Parameters.Add('-R');
      AProcess.Parameters.Add('-a');
    {$ENDIF FreeBSD}
 
    AProcess.Parameters.Add('-l');
  {$ENDIF Unix}

  // Aby można było przechwycić dane wyjściowe, należy użyć opcji procesu poUsePipes.
  // Nie można użyć opcji procesu poWaitOnExit, ponieważ zablokuje to program główny,
  // uniemożliwiając mu odczytanie danych wyjściowych procesu zewnętrznego.
  AProcess.Options := [poUsePipes];

  // Uruchom proces (uruchom polecenie dir dla Windows lub ls dla Linux/Unix)
  AProcess.Execute;

  // Utwórz obiekt strumienia, w którym będą przechowywane wygenerowane dane wyjściowe.
  // Może to być również strumień plikowy do bezpośredniego zapisywania danych wyjściowych na dysku.
  OutputStream := TMemoryStream.Create;

  // Wszystkie wygenerowane dane wyjściowe z AProcess są odczytywane w pętli,
  // dopóki nie będzie dostępnych więcej danych
  repeat
    // Pobierz nowe dane z procesu do maksymalnego rozmiaru buforu, który został przydzielony.
    // Zauważ, że wszystkie wywołania Read(...) będą blokowane z wyjątkiem ostatniego, które zwraca 0 (zero).
    BytesRead := AProcess.Output.Read(Buffer, BUF_SIZE);

    // Bajty, które zostały odczytane, dodaj do strumienia w celu późniejszego wykorzystania
    OutputStream.Write(Buffer, BytesRead)

  until BytesRead = 0;  // Zakończ pętlę, jeśli nie ma więcej danych

  // Proces się zakończył, więc można go posprzątać
  AProcess.Free;

  // Teraz, gdy wszystkie dane zostały odczytane, można z nich korzystać;
  // na przykład po to, by zapisać je do pliku na dysku
  with TFileStream.Create('output.txt', fmCreate) do
  begin
    OutputStream.Position := 0; // Wymagane, aby upewnić się, że wszystkie dane będą kopiowane od początku
    CopyFrom(OutputStream, OutputStream.Size);
    Free
  end;

  // Lub dane mogą być wyświetlane na ekranie
  with TStringList.Create do
  begin
    OutputStream.Position := 0; // Wymagane, aby upewnić się, że wszystkie dane będą kopiowane od początku
    LoadFromStream(OutputStream);
    writeln(Text);
    writeln('--- Liczba linii = ', Count, '----');
    Free
  end;

  // Sprzątanie
  OutputStream.Free;
end.

Zauważ, że powyższe można również osiągnąć za pomocą RunCommand:

var s: string;
...
RunCommand('c:\windows\system32\cmd.exe', ['/c', 'dir /s c:\windows'], s);

Korzystanie z wejścia i wyjścia TProcess

Zobacz przykładowe demo procesu w Lazarus-CCR SVN.

Wskazówki dotyczące korzystania z TProcess

Podczas tworzenia programu wieloplatformowego nazwę pliku wykonywalnego specyficzną dla systemu operacyjnego można ustawić za pomocą dyrektyw „{$IFDEF}” i „{$ENDIF}”. Przykład:

{...}
AProcess := TProcess.Create(nil)

{$IFDEF WIN32}
  AProcess.Executable := 'calc.exe'; 
{$ENDIF}

{$IFDEF LINUX}
  AProcess.Executable := FindDefaultExecutablePath('kcalc');
{$ENDIF}

AProcess.Execute;
{...}

macOS pokazuje pakiet aplikacji na pierwszym planie

Możesz uruchomić pakiet aplikacji przez TProcess, uruchamiając plik wykonywalny w pakiecie. Na przykład:

 AProcess.Executable:='/Applications/iCal.app/Contents/MacOS/iCal';

Spowoduje to uruchomienie Kalendarza, ale okno będzie znajdować się za bieżącą aplikacją. Aby uzyskać aplikację na pierwszym planie, możesz użyć narzędzia open z parametrem -n:

 AProcess.Executable:='/usr/bin/open';
 AProcess.Parameters.Add('-n');
 AProcess.Parameters.Add('-a'); // opcjonalny: określa aplikację do użycia; przeszukuje tylko katalogi aplikacji
 AProcess.Parameters.Add('-W'); // opcjonalnie: open czeka, aż aplikacje, które otwiera (lub były już otwarte) zostaną zamknięte
 AProcess.Parameters.Add('Pages.app'); // dołączenie .app jest opcjonalne

Jeśli Twoja aplikacja potrzebuje parametrów, możesz przekazać wraz z open parametr --args, po którym wszystkie dalsze parametry są przekazywane do aplikacji:

 AProcess.Parameters.Add('--args');
 AProcess.Parameters.Add('argument1');
 AProcess.Parameters.Add('argument2');

Zobacz także: macOS polecenie open.

Uruchom oddzielny program

Normalnie program uruchomiony przez twoją aplikację jest procesem potomnym i zostaje zabity, gdy twoja aplikacja zostanie zabita. Jeśli chcesz uruchomić samodzielny program, który nadal będzie działał, możesz użyć:

var
  Process: TProcess;
  I: Integer;
begin
  Process := TProcess.Create(nil);
  try
    Process.InheritHandles := False;
    Process.Options := [];
    Process.ShowWindow := swoShow;

    // Skopiuj domyślne zmienne środowiskowe, w tym zmienną DISPLAY, aby aplikacja GUI działała
    for I := 1 to GetEnvironmentVariableCount do
      Process.Environment.Add(GetEnvironmentString(I));

    Process.Executable := '/usr/bin/gedit';  
    Process.Execute;
  finally
    Process.Free;
  end;
end;

Przykład „rozmowy” z procesem aspell

Wewnątrz kodu źródłowego pasdoc można znaleźć dwa moduły, które sprawdzają pisownię poprzez „rozmawianie” przez potoki z uruchomionym procesem aspell:

  • Moduł PasDoc_ProcessLineTalk.pas implementuje klasę TProcessLineTalk, potomka TProcess, która może być z łatwością używana do komunikacji z dowolnym procesem linia po linii.
  • Moduł PasDoc_Aspell.pas implementuje klasę TAspellProcess, która sprawdza pisownię za pomocą bazowej instancji TProcessLineTalk do wykonania aspell i komunikacji z uruchomionym procesem aspell.

Obydwa moduły są raczej niezależne od pozostałych źródeł pasdoc, więc mogą służyć jako rzeczywiste przykłady użycia TProcess do uruchamiania i komunikacji przez potoki z innym programem.

Zastępowanie operatorów powłoki, takich jak „| < >”

Czasami chcesz uruchomić bardziej skomplikowane polecenie, które przesyła dane do innego polecenia lub do pliku. Coś podobnego do

ShellExecute('firstcommand.exe | secondcommand.exe');

lub

ShellExecute('dir > output.txt');

Wykonanie tego za pomocą TProcess nie zadziała. tj:

// to nie zadziała!
Process.CommandLine := 'firstcommand.exe | secondcommand.exe'; 
Process.Execute;

Dlaczego używanie specjalnych operatorów do przekierowywania wyjścia nie działa?

TProcess nie jest środowiskiem powłoki, tylko procesem. To nie dwa procesy, to tylko jeden. Możliwe jest jednak przekierowanie danych wyjściowych dokładnie tak, jak chcesz. Zobacz następną sekcję.

Jak przekierować wyjście za pomocą TProcess

Możesz przekierować dane wyjściowe polecenia do innego polecenia, używając osobnej instancji TProcess dla każdego polecenia.

Oto przykład, który wyjaśnia, jak przekierować dane wyjściowe jednego procesu do drugiego. Aby przekierować wyjście procesu do pliku/strumienia, zobacz przykład Czytanie dużych ilości danych wyjściowych.

Możesz nie tylko przekierować „normalne” wyjście (znane również jako stdout), ale możesz także przekierować wyjście błędu (stderr), jeśli określisz opcję poStderrToOutPut, jak to widać w opcjach drugiego procesu.

program Project1;
  
uses
  Classes, sysutils, process;
  
var
  FirstProcess,
  SecondProcess: TProcess;
  Buffer: array[0..127] of char;
  ReadCount: Integer;
  ReadSize: Integer;
begin
  FirstProcess  := TProcess.Create(nil);
  SecondProcess := TProcess.Create(nil);
 
  FirstProcess.Options     := [poUsePipes]; 
  FirstProcess.Executable  := 'pwd'; 
  
  SecondProcess.Options    := [poUsePipes,poStderrToOutPut];
  SecondProcess.Executable := 'grep'; 
  SecondProcess.Parameters.Add(DirectorySeparator+ ' -'); 
  // byłoby to to samo co "pwd | grep / -"
  
  FirstProcess.Execute;
  SecondProcess.Execute;
  
  while FirstProcess.Running or (FirstProcess.Output.NumBytesAvailable > 0) do
  begin
    if FirstProcess.Output.NumBytesAvailable > 0 then
    begin
      // upewnij się, że nie czytamy więcej danych niż przydzieliliśmy w buforze
      ReadSize := FirstProcess.Output.NumBytesAvailable;
      if ReadSize > SizeOf(Buffer) then
        ReadSize := SizeOf(Buffer);
      // teraz wczytaj dane wyjściowe do bufora
      ReadCount := FirstProcess.Output.Read(Buffer[0], ReadSize);
      // i zapisz bufor do drugiego procesu
      SecondProcess.Input.Write(Buffer[0], ReadCount);
  
      // jeśli SecondProcess zapisuje dużo danych do swojego wyjścia,
      // powinniśmy odczytać te dane tutaj, aby zapobiec zakleszczeniu,
      // patrz poprzedni przykład "Czytanie dużych ilości danych wyjściowych"
    end;
  end;
  // Zamknij dane wejściowe w SecondProcess,
  // aby zakończyć przetwarzanie swoich danych
  SecondProcess.CloseInput;
 
  // i poczekaj, aż się zakończy, lecz uważaj, jakie polecenie uruchamiasz,
  // ponieważ może się ono nie zakończyć, gdy jego dane wejściowe są zamknięte,
  // to następująca linia może zapętlić się w nieskończoność
  while SecondProcess.Running do
    Sleep(1);
  // Gotowe! Reszta programu jest tylko po to, aby przykład był bardziej 'użyteczny'

  // użyjemy ponownie Buffer, aby wyprowadzić wyjście SecondProcess do *tego* programu stdout
  WriteLn('Rozpoczęcie wyjścia grep:');
  ReadSize := SecondProcess.Output.NumBytesAvailable;
  if ReadSize > SizeOf(Buffer) then
    ReadSize := SizeOf(Buffer);
  if ReadSize > 0 then
  begin
    ReadCount := SecondProcess.Output.Read(Buffer, ReadSize);
    WriteLn(Copy(Buffer,0, ReadCount));
  end
  else
    WriteLn('grep nie znalazł tego, czego szukaliśmy. ', SecondProcess.ExitStatus);
  WriteLn('Zakończenie wyjścia grep:');
  
  // zwolnij obiekty naszych procesów
  FirstProcess.Free;
  SecondProcess.Free;
end.

W ten sposób możesz przekierować dane wyjściowe z jednego programu do drugiego.

Uwagi

Ten przykład może wydawać się przesadny, ponieważ możliwe jest uruchamianie „skomplikowanych” poleceń przy użyciu powłoki przy pomocy TProcess, takich jak:

Process.Commandline := 'sh -c "pwd | grep / -"';

Ale nasz przykład jest bardziej wieloplatformowy, ponieważ nie wymaga modyfikacji, aby działać w systemie Windows lub Linux itp. Polecenie „sh” może, ale nie musi istnieć na twojej platformie i jest ogólnie dostępny tylko na platformach *nix. W naszym przykładzie mamy również większą elastyczność, ponieważ możesz czytać i zapisywać z/do wejścia, wyjścia i stderr każdego procesu z osobna, co może być bardzo korzystne dla twojego projektu.

Przekierowywanie wejścia i wyjścia oraz działanie z rootem

Częstym problemem w systemach Unix (FreeBSD, macOS) i Linux jest to, że chcesz uruchomić jakiś program na koncie root (lub, bardziej ogólnie, na innym koncie użytkownika). Przykładem może być uruchomienie polecenia ping.

Jeśli możesz użyć do tego sudo, możesz dostosować następujący przykład zaadaptowany z postu opublikowanego przez andymana na forum ([1]). Ten przykład uruchamia polecenie ls w katalogu /root, ale oczywiście można go dostosować.

Lepszym sposobem na to jest użycie pakietu policykit, który powinien być dostępny we wszystkich najnowszych Linuksach. Zobacz wątek na forum, aby uzyskać szczegółowe informacje.

Duże części tego kodu są podobne do wcześniejszego przykładu, ale pokazuje również, jak przekierować stdout i stderr procesu wywoływanego oddzielnie na stdout i stderr naszego własnego kodu.

program rootls;

{ Demonstracja użycia TProcess, przekierowania stdout/stderr do naszego stdout/stderr,
 wywoływania sudo w systemach FreeBSD/Linux/macOS i dostarczania danych wejściowych na stdin}
{$mode objfpc}{$H+}

uses
  Classes,
  Math, {for min}
  Process;

  procedure RunsLsRoot;
  var
    Proc: TProcess;
    CharBuffer: array [0..511] of char;
    ReadCount: integer;
    ExitCode: integer;
    SudoPassword: string;
  begin
    WriteLn('Please enter the sudo password:');
    Readln(SudoPassword);
    ExitCode := -1; //Zacznij od porażki, zobaczmy później, czy to zadziała
    Proc := TProcess.Create(nil); //Utwórz nowy proces
    try
      Proc.Options := [poUsePipes, poStderrToOutPut]; //Użyj potoków, aby przekierować stdin,stdout,stderr
      Proc.CommandLine := 'sudo -S ls /root'; //Uruchom ls /root jako root za pomocą sudo
      // -S powoduje, że sudo odczytuje hasło z stdin.
      Proc.Execute; //Uruchom to. sudo prawdopodobnie poprosi teraz o hasło

      // wpisz hasło do stdin programu sudo:
      SudoPassword := SudoPassword + LineEnding;
      Proc.Input.Write(SudoPassword[1], Length(SudoPassword));
      SudoPassword := '%*'; // krótki string, mam nadzieję, że trochę pomiesza pamięć; uwaga: używanie PChars jest bardziej niezawodne
      SudoPassword := ''; // i sprawić, by program był nieco bezpieczniejszy przed węszeniem?!?

      // główna pętla do odczytu wyjścia z stdout i stderr dla sudo
      while Proc.Running or (Proc.Output.NumBytesAvailable > 0) or
        (Proc.Stderr.NumBytesAvailable > 0) do
      begin
        // odczytaj stdout i zapisz do naszego stdout
        while Proc.Output.NumBytesAvailable > 0 do
        begin
          ReadCount := Min(512, Proc.Output.NumBytesAvailable); //Czytaj do bufora, nie więcej
          Proc.Output.Read(CharBuffer, ReadCount);
          Write(StdOut, Copy(CharBuffer, 0, ReadCount));
        end;
        // odczytaj stderr i zapisz do naszego stderr
        while Proc.Stderr.NumBytesAvailable > 0 do
        begin
          ReadCount := Min(512, Proc.Stderr.NumBytesAvailable); //Czytaj do bufora, nie więcej
          Proc.Stderr.Read(CharBuffer, ReadCount);
          Write(StdErr, Copy(CharBuffer, 0, ReadCount));
        end;
      end;
      ExitCode := Proc.ExitStatus;
    finally
      Proc.Free;
      Halt(ExitCode);
    end;
  end;

begin
  RunsLsRoot;
end.

Inne przemyślenia: Bez wątpienia wskazane byłoby sprawdzenie, czy sudo rzeczywiście pyta o hasło. Można to konsekwentnie sprawdzać, ustawiając zmienną środowiskową SUDO_PROMPT na coś, na co zwracamy uwagę podczas czytania stdout TProcess, unikając problemu związanego z różnymi znakami zachęty dla różnych lokalizacji. Ustawienie zmiennej środowiskowej powoduje wyczyszczenie wartości domyślnych (odziedziczonych z naszego procesu), więc w razie potrzeby musimy skopiować środowisko z naszego programu.

Używanie fdisk z sudo w systemie Linux

Poniższy przykład pokazuje, jak uruchomić fdisk na komputerze z systemem Linux za pomocą polecenia sudo, aby uzyskać uprawnienia roota. Uwaga: jest to tylko przykład i nie obejmuje dużej ilości danych na wyjściu.

program getpartitioninfo;
{Pierwotnie napisany przez użytkownika forum Lazarus wjackon153. Prosimy o kontakt w przypadku pytań, uwag itp.
Zmodyfikowan z fragmentu kodu Lazarusa na program FPC dla ułatwienia zrozumienia/zwięzłości przez BigChimp}

Uses
  Classes, SysUtils, FileUtil, Process;

var
  hprocess: TProcess;
  sPass: String;
  OutputLines: TStringList;

begin  
  sPass := 'yoursudopasswordhere'; // Musisz to zmienić na własne hasło sudo
  OutputLines:=TStringList.Create; //dla pewności warto zamknąć to w bloku try...finally...end
  // OutputLines został utworzony ... To samo dla hProcess.
     
  // Poniższy przykład otworzy fdisk w tle i da nam informacje o partycjach
  // Ponieważ fdisk wymaga podwyższonych uprawnień, musimy przekazać
  // nasze hasło jako parametr do sudo za pomocą opcji -S, więc proces
  // poczeka, aż nasz program wyśle ​​nasze hasło do aplikacji sudo

  hProcess := TProcess.Create(nil);
  // W systemach Linux/Unix/FreeBSD/macOS musimy podać pełną ścieżkę do naszego pliku wykonywalnego:
  hProcess.Executable := '/bin/sh';
  // Teraz dodajemy wszystkie parametry w wierszu poleceń:
  hprocess.Parameters.Add('-c');
  // Tutaj przesyłamy potokiem hasło do polecenia sudo, które następnie wykonuje fdisk -l: 
  hprocess.Parameters.add('echo ' + sPass  + ' | sudo -S fdisk -l');
  // Uruchom asynchronicznie (poczekaj na zakończenie procesu) i użyj potoków, abyśmy mogli odczytać potok wyjściowy
  hProcess.Options := hProcess.Options + [poWaitOnExit, poUsePipes];
  // Teraz wykonaj:
  hProcess.Execute;

  // hProcess powinien teraz uruchomić zewnętrzny plik wykonywalny (ponieważ używamy poWaitOnExit).
  // Teraz możesz przetworzyć wyjście procesu (standardowe wyjście i standardowy błąd), np.:
  OutputLines.Add('stdout:');
  OutputLines.LoadFromStream(hprocess.Output);
  OutputLines.Add('stderr:');
  OutputLines.LoadFromStream(hProcess.Stderr);
  // Pokaż dane wyjściowe na ekranie:
  writeln(OutputLines.Text);

  // Wyczyść, aby uniknąć wycieków pamięci:
  hProcess.Free;
  OutputLines.Free;
  
  //Poniżej znajduje się kilka przykładów, jak widać, możemy przekazywać niedozwolone znaki, tak jak robimy to z terminala
  //Nawet jeśli przeczytałeś gdzie indziej, że nie możesz upewnić się tą metodą, to możesz :)

  //hprocess.Parameters.Add('ping -c 1 www.google.com');
  //hprocess.Parameters.Add('ifconfig wlan0 | grep ' +  QuotedStr('inet addr:') + ' | cut -d: -f2');

  //Użycie QuotedStr() nie jest wymagane, chociaż zapewnia czystszy kod;
  //możesz użyć podwójnego cudzysłowu i mieć ten sam efekt.

  //hprocess.Parameters.Add('glxinfo | grep direct');   

  // Ta metoda może być również używana do instalowania aplikacji z repozytorium:

  //hprocess.Parameters.add('echo ' + sPass  + ' | sudo -S apt-get install -y pkg-name'); 

 end.

Parametry zawierające spacje (Zastępowanie cudzysłowów powłoki)

W powłoce Linuksa możliwe jest pisanie cytowanych argumentów w następujący sposób:

gdb --batch --eval-command="info symbol 0x0000DDDD" myprogram

Polecenie GDB otrzyma 3 argumenty (oprócz pierwszego argumentu, który jest pełną ścieżką do pliku wykonywalnego):

  1. --batch
  2. --eval-command=info symbol 0x0000DDDD
  3. pełna ścieżka do myprogram

Najlepszym rozwiązaniem, aby uniknąć skomplikowanego cytowania, jest przejście na TProcess.Parameters.Add zamiast ustawiania wiersza poleceń dla nietrywialnych przypadków, np.

 AProcess.Executable:='/usr/bin/gdb';
 AProcess.Parameters.Add('--batch');
 AProcess.Parameters.Add('--eval-command=info symbol 0x0000DDDD'); // zwróć tutaj uwagę na brak cytowania
 AProcess.Parameters.Add('/home/me/myprogram');

Pamiętaj też, aby używać tylko pełne ścieżki.

TProcess.Commandline obsługuje jednak tylko podstawowe cytowanie parametrów z cudzysłowami. Ujmij w apostrofy cały parametr zawierający spacje z podwójnymi cudzysłowami. Jak poniżej:

AProcess.CommandLine := '/usr/bin/gdb --batch "--eval-command=info symbol 0x0000DDDD" /home/me/myprogram';

Właściwość .CommandLine jest przestarzała, a raporty o błędach do obsługi bardziej skomplikowanych przypadków cytowania nie będą akceptowane.

Alternatywy LCLIntf

Czasami nie trzeba jawnie wywoływać zewnętrznego programu, aby uzyskać potrzebną funkcjonalność. Zamiast otwierać aplikację i określać dokument, który ma być z nią powiązany, po prostu poproś system operacyjny o otwarcie dokumentu i pozwól mu użyć domyślnej aplikacji powiązanej z tym typem pliku. Poniżej kilka przykładów.

Otwieranie dokumentu w domyślnej aplikacji

W niektórych sytuacjach musisz otworzyć jakiś dokument/plik przy użyciu domyślnej aplikacji z nim powiązanej, zamiast uruchamiać konkretny program. To zależy od działającego systemu operacyjnego. Lazarus zapewnia niezależną od platformy procedurę OpenDocument, która zajmie się tym za Ciebie. Twoja aplikacja będzie nadal działać, nie czekając na zamknięcie procesu tworzenia dokumentu.

uses LCLIntf;
...
OpenDocument('manual.pdf');  
...

Otwieranie strony web w domyślnej przeglądarce internetowej

Aby to zrobić, po prostu przekaż wymagany adres URL. Przedrostek protokołu http:// wydaje się w pewnych okolicznościach opcjonalny. Ponadto przekazanie nazwy pliku wydaje się dawać takie same wyniki jak OpenDocument()

uses LCLIntf;
...
OpenURL('www.lazarus.freepascal.org/');

Zobacz także:

Lub możesz użyć TProcess w taki sposób:

uses Process;

procedure OpenWebPage(URL: string);
// Możliwe, że musisz podać swój adres URL w cudzysłowiu np. "www.lazarus.freepascal.org"
var
  Browser, Params: string;
begin
  FindDefaultBrowser(Browser, Params);
  with TProcess.Create(nil) do
  try
    Executable := Browser;
    Params:=Format(Params, [URL]);
    Params:=copy(Params,2,length(Params)-2); // usuń znaki "", nowa wersja TProcess.Parameters robi to sama
    Parameters.Add(Params);
    Options := [poNoConsole];
    Execute;
  finally
    Free;
  end;
end;

Zobacz także