to: zw84611
不要这样吧,c语言都拿来充数?
Method 1: LibC.System function call
function system(Command: PChar): Integer;
The Libc.System function call is the most convenient way to start an external
application. It simply executes the given argument command as shell command
.
system is a blocking function, which means it will not return until the child
process has terminated. The result value is -1 or 127 if an error occurred.
The value 127 has the special meaning that execve (used within the system
function - a more detailed description follows) failed.
On some shells you can add an ampersand (the "&" char) to the argument
command. This will force the system function call to return immediately:
procedure PlayWave;
begin
if Libc.system('playwav ~/test.wav') = -1 then
begin
ShowMessage('Can''t play wav file');
end;
end;
Example 1.1: Play a wav file using system
You can determine if a shell is available by using nil as argument. If the
result value is not zero, system was unable to invoke a shell.
procedure ShellAvail;
begin
if Libc.system(nil) <> 0 then
begin
ShowMessage('No shell available.');
end;
end;
Example 1.2: Determine if a shell is available
Method 2: popen
This method is extremely useful when you need to get the standard output of
the external application. The popen function is declared as
function popen(const Command: PChar;
Modes: PChar): PIOFile;
cdecl;
It invokes the shell, runs the command, and creates a pipe. This demo simply
displays the contents of the current directory within a memo.
var
Output: PIOFile;
line: PChar;
txt: string;
str: string;
StrLst: TStringList;
rb: integer;
const
BufferSize: Integer = 1000;
begin
SetLength(txt,0);
Output := popen('ls -l','r');
StrLst := TStringList.Create;
GetMem(Line,BufferSize);
StrLst.Add('Hallo');
if Assigned(output) then
begin
while FEOF(Output)=0do
begin
rb := libc.fread(line,1,BufferSize,output);
SetLength(Txt,length(txt)+rb);
MemCpy(@txt[length(txt)-(rb-1)],line,rb);
while pos(#10,txt) > 0do
begin
str := copy(txt,1,pos(#10,txt)-1);
StrLst.Add(str);
txt := copy(txt,pos(#10,txt)+1,length(txt));
end;
end;
end;
Memo1.Lines.AddStrings(StrLst);
StrLst.Free;
libc.pclose(output);
wait(nil);
FreeMem(Line,BufferSize);
end;
Method 3: Fork and Exec
Fork and Exec is how hardcore Linux and Unix programmers would create a child
process. In fact, the previous functions are internally implemented using fork
and exec.
function fork: __pid_t;
cdecl;
The fork function makes a copy the parent process and therefore creates a new
subprocess. Within the parent process the result value of fork is the Process
ID (PID) of the subprocess or an error code. In the newly created subprocess
the result value is zero.
Note: The function fork and the Kylix debuggerdo
not seem to be 100%
compatible currently. If you encounter problems and you are running the
application within the IDE it is recommended that you disable the debugger.
{$APPTYPE CONSOLE}
uses
Libc;
procedure Forking;
var
pid: PID_T;
begin
pid := fork;
if pid = 0 then
begin
writeln('Hello. I am your new sub-process.');
writeln('Bye.');
halt;
end;
writeln('Hello. I am the parent process. I am going to wait some time...');
__sleep(3);
end;
begin
Forking;
writeln('Close parent process.');
writeln('Bye.');
end.
Example 3.1: Fork
As you see, fork copies the parent process. The child process starts to
execute after the fork call. The differences between the parent process and
the child process are only:
The child has a different process identifier.
The child has a different parent process.
File locks are not inherited.
Pending signals are not inherited.
Exec
Since we have now a new child process it is necessary to replace that process
image with a new one:
function execve(PathName: PChar;
const argv: PPChar;
const envp: PPChar): Integer;
function execv(PathName: PChar;
const argv: PPChar): Integer;
The execve function loads and executes PathName. The arguments are stored in
argv and the environment is stored in envp.
execv performs the same actions as execve, except that the environment is taken
from the "environ" setting.
program ForkExecDemo;
{$APPTYPE CONSOLE}
uses
Libc;
procedure ForkAndExec;
var
pid: PID_T;
parg: array[1..3] of PChar;
filetoplay: string;
begin
pid := fork;
if pid = 0 then
begin
filetoplay := 'test.wav';
parg[1] := 'playwave';
parg[2] := PChar(FileToPlay);
parg[3] := nil;
if execv('/usr/bin/playwave',PPChar(@parg[1])) = -1 then
begin
writeln('Something is wrong in the sub process.');
Halt;
end;
end;
writeln('Hello. I am the parent process. I am going to wait some time...');
__sleep(3);
end;
begin
ForkAndExec;
writeln('Close parent process.');
writeln('Bye.');
end.
Example 3.2: Fork and Exec
While this works great in console applications you may get problems in X
applications because fork copies all the file descriptors. In such a case
you'll have to close them within the child process. Example 3.3 illustrates
this:
procedure ForkAndExec;
var
pid: PID_T;
Max: Integer;
I: Integer;
begin
pid := fork;
if pid = 0 then
begin
Max := sysconf(_SC_OPEN_MAX);
for i := (STDERR_FILENO+1) toMaxdo
begin
fcntl(i, F_SETFD, FD_CLOEXEC);
end;
[...]
end;
[...]
end;
Example 3.3: Closing the file descriptors
Sometimes you may want to let the parent process wait until the child process
has been terminated. This can bedo
ne using the function waitpid. It is
declared as:
function waitpid(__pid: pid_t;
__stat_loc: PInteger;
__options: Integer): pid_t;
cdecl;
__pid: The process to wait for.
__stat_loc: The status of the terminated app. Can be nil.
__options: Zero,a flag or an combination of the flags WNOHANG and WUNTRACED. WNOHANGdo
esn't let the parent wait until the child has finished while WUNTRACED delivers status information not only from terminated applications but also from stopped ones. None of the flags are of interest for our purposes.
Example 3.4 illustrates the usage of the function WaitPid. It uses fork and
execvp.
function StartApp(name: string;
arguments: array of string): Integer;
var
pid: PID_T;
Max: Integer;
I: Integer;
parg: PPCharArray;
argnum: Integer;
begin
Result := -1;
pid := fork;
if pid = 0 then
begin
Max := sysconf(_SC_OPEN_MAX);
for i := (STDERR_FILENO+1) to Maxdo
begin
fcntl(i, F_SETFD, FD_CLOEXEC);
end;
argnum := High(Arguments) + 1;
GetMem(parg,(2 + argnum) * sizeof(PChar));
parg[0] := PChar(Name);
i := 0;
while i <= high(arguments)do
begin
inc(i);
parg := PChar(arguments[i-1]);
end;
parg[i+1] := nil;
execvp(PChar(name),PPChar(@parg[0]));
halt;
end;
if pid > 0 then
begin
waitpid(pid,@Result,0);
end;
end;
Example 3.4: StartApp function
The examples are available from CodeCentral: ID 16329.
If you have comments or suggestions regarding this article please feel free to
contact me.