System Delphi

Title: Custom Environment Variable Blocks
Question: This article looks at how environment variables are passed from one process to another.
Answer:
When a program spawns a child process, by default the child process gets a copy of the parent's environment block. In my previous article I showed that additional environment variables can be passed to a child process by creating them in the parent process before executing the child process. The child then gets a copy of the new variables along with all the parent's other environment variables.
What if you don't want to modify the parent process environment variables, or only want to pass certain variables to the child process? To do this you need to create a new environment block and pass that to the new process.
An environment variable block is in the form 'VAR1=Value1'#0'VAR2=Value2'#0#0 - i.e. a sequence of zero terminated strings of environment variables / values terminated by an additional zero character.
To create a new process with a custom environment block we need to:
1) create the new environment block containing the environment strings
2) create the new process (using the Windows API CreateProcess), passing the address of the environment block.
The following procedure executes the given program and with an environment block containing the environment variables stored in the given string list (the string list stores environment variables in the form 'NAME=VALUE').
procedure ExecWithEnv(const ProgName: string;
const Env: TStringList);
var
EnvBlock: Pointer; // environment block
EnvStart: PChar; // location of vars in block
BlockSize: Integer; // size of block
Idx: Integer; // scans env strings
SI: TStartupInfo; // process start up info
PI: TProcessInformation; // process info
begin
// Work out size of environment block
BlockSize := 0;
for Idx := 0 to Pred(Env.Count) do
// add space for #0 terminated string
Inc(BlockSize, Length(Env[Idx]) + 1);
// add space for final #0
Inc(BlockSize);
// Create the environment block
GetMem(EnvBlock, BlockSize);
try
EnvStart := EnvBlock;
for Idx := 0 to Pred(Env.Count) do
begin
StrPCopy(EnvStart, Env[Idx]);
Inc(EnvStart, Length(Env[Idx]) + 1);
end;
EnvStart^ := #0;
// Execute the program
FillChar(SI, SizeOf(SI), 0);
SI.cb := SizeOf(SI);
CreateProcess(nil, PChar(ProgName), nil, nil,
True, 0, EnvBlock, nil, SI, PI);
finally
// Finished with env block
FreeMem(EnvBlock, BlockSize);
end;
end;
To create a process named 'MyProg.exe' with environment variables FOO=BAR and DELPHI=3000 when a button is clicked do the following:
procedure TForm1.Button1Click(Sender: TObject);
var
EnvVars: TStringList;
begin
EnvVars := TStringList.Create;
try
EnvVars.Add('FOO=BAR');
EnvVars.Add('DELPHI=3000');
ExecWithEnv('MyProg.exe', EnvVars);
finally
EnvVars.Free;
end;
end;
A demo program that demonstrates this and other environment variable techniques is available for download here.