19 February 2010

Unicode environment blocks and CreateProcess

I've just been struggling with a Unicode conversion of some code that passes a custom environment block to a child process.

On Unicode Delphi compilers the code produces a Unicode environment block, and I'd done something like this...

procedure ExecProgWithUnicodeEnv(const ProgName: string; EnvBlock: Pointer);
var
  SI: TStartupInfo;
  PI: TProcessInformation;
  SafeProgName: string;
begin
  SafeProgName := ProgName;    // workaround for read-only lpCommandLine
  UniqueString(SafeProgName);  // param to CreateProcessW
  FillChar(SI, SizeOf(SI), 0);
  SI.cb := SizeOf(SI);
  CreateProcess(
    nil, PChar(ProgName), nil, nil, True,
    0, EnvBlock, nil, SI, PI
  );
end;

If you're wandering about that UniqueString stuff, check out this post.

The assumption was that CreateProcessW would expect a Unicode environment block. Wrong! It actually still expects an ANSI block by default.

A dig around in the API docs revealed the answer: If you pass a Unicode environment block to CreateProcess in the lpEnvironment parameter you must also include CREATE_UNICODE_ENVIRONMENT in the dwCreationFlags parameter. So it's just a matter of changing

  ...
  CreateProcess(
    nil, PChar(ProgName), nil, nil, True,
    0, EnvBlock, nil, SI, PI
  );
  ...

to

  ...
  CreateProcess(
    nil, PChar(ProgName), nil, nil, True,
    CREATE_UNICODE_ENVIRONMENT, EnvBlock, nil, SI, PI
  );
  ...

In the end, because my project guarantees a Unicode environment block if and only if it is compiled on a Unicode Delphi, I went for code similar to the following:

  ...
  {$IFDEF UNICODE}
  CreateFlags := CREATE_UNICODE_ENVIRONMENT;
  {$ELSE}
  CreateFlags := 0;
  {$ENDIF}
  CreateProcess(
    nil, PChar(SafeProgName), nil, nil, True,
    CreateFlags, EnvBlock, nil, SI, PI
  );
  ...

You definately shouldn't use conditional compilation like this if there's a chance you'll be handling an ANSI environment block with a Unicode compile, or vice-versa.

There we have it, just one more thing to consider when porting code to Unicode.

And the code in question? It's part of the demo for my article "How to access environment variables".

No comments: