CreateProcess of 64-bit process from 32-bit process inherits the 32-bit environment block

My customer-facing installer program for our kernel-mode software
drivers (not hardware device drivers) is a 32-bit SETUP.EXE so that
everyone launches our install the same way. That 32-bit SETUP.EXE
does everything it can running AsInvoker, then when the time is right
it respawns itself elevated with the ShellExecute RunAs verb.

The elevated copy of this 32-bit SETUP.EXE then does the heavy
lifting, including spawning a 32-bit or 64-bit child process depending
on which CPU flavor of Windows we’re running on. (From our own
installation set’s .\i386\ versus .\amd64\ directory; not SYSTEM32 or
anything that is subject to WOW64 folder redirection.) This helper
child process invokes the actual driver installation(s) via SETUPAPI,
calls some needed INetCfg APIs, etc.

What I never noticed before is that when I CreateProcess from the
32-bit SETUP.EXE to create a 64-bit child process, passing NULL for
the environment block parameter does /literally/ what the
documentation has always said it does, which is that the child process
gets a copy of the parent process’ environment block.

Even though the environment variables are all WRONG for a 64-bit
process. i.e. “CommonProgramFiles”, “CommonProgramW6432”,
“ProgramFiles”, “ProgramW6432”, “PROCESSOR_ARCHITECTURE” and
“PROCESSOR_ARCHITEW6432” are all swapped / backwards from what they
should be on a 64-bit process.

I don’t see any discussion of this here or on MSDN. Definitely I can
“brute force” create a custom environment block when spawning the
process, but I guess I was more expecting that there would be a new
process create flag or something that says “generate CPU-appropriate
environment block” when calling CreateProcess. Since in theory the
caller doesn’t necessarily /know/ whether the created process will
ultimately be 32-bit or 64-bit, since it depends on what executable
image gets found first in the PATH, etc.

Anyone have experience tackling this before, or something I’m missing
that would create the more appropriate environment when calling
CreateProcess?

For what it’s worth, the installer code has been working fine for
years even with “the wrong environment block” on 64-bit Windows. But
this week we learned of “that one customer” having an issue, and a
Process Monitor log of their failing install shows that my otherwise
64-bit helper .EXE which calls SETUPAPI to process some sections of
our .INF is ultimately writing “C:\Program Files (x86)”-based paths in
response to the .INF processing, when he should have been writing
“C:\Program Files” because he’s a 64-bit process / a 64-bit SETUPAPI
being invoked.

Alan Adams
Novell Client CPR Group
xxxxx@novell.com

Novell, Inc.
www.novell.com

Alan Adams wrote:

Even though the environment variables are all WRONG for a 64-bit
process. i.e. “CommonProgramFiles”, “CommonProgramW6432”,
“ProgramFiles”, “ProgramW6432”, “PROCESSOR_ARCHITECTURE” and
“PROCESSOR_ARCHITEW6432” are all swapped / backwards from what they
should be on a 64-bit process.

CommonProgramW6432 and ProgramW6432 are supposed to be the same. They
always point to the native (64-bit) folders. CommonProgramFiles and
ProgramFiles should change.

However, I can duplicate your results pretty easily. If I start a
32-bit Python shell, then do os.system(‘cmd’), it’s clear that it
inherited the 32-bit environment. Very interesting.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Tim Roberts wrote:

> Alan Adams wrote:
> > Even though the environment variables are all WRONG for a 64-bit
> > process. i.e. “CommonProgramFiles”, “CommonProgramW6432”,
> > “ProgramFiles”, “ProgramW6432”, “PROCESSOR_ARCHITECTURE” and
> > “PROCESSOR_ARCHITEW6432” are all swapped / backwards from what they
> > should be on a 64-bit process.
>
> CommonProgramW6432 and ProgramW6432 are supposed to be the same. They
> always point to the native (64-bit) folders. CommonProgramFiles and
> ProgramFiles should change.
>
> However, I can duplicate your results pretty easily. If I start a
> 32-bit Python shell, then do os.system(‘cmd’), it’s clear that it
> inherited the 32-bit environment. Very interesting.

Thanks, you are correct of course, and I spammed all the variables I
was looking at rather than the ones that were actually different. The
results I saw in my “64-bit process spawned by a 32-bit process” had
the environment of:

CommonProgramFiles=C:\Program Files (x86)\Common Files
CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files
CommonProgramW6432=C:\Program Files\Common Files
PROCESSOR_ARCHITECTURE=x86
PROCESSOR_ARCHITEW6432=AMD64
ProgramFiles=C:\Program Files (x86)
ProgramFiles(x86)=C:\Program Files (x86)
ProgramW6432=C:\Program Files

Whereas on a “64-bit process” I see the environment of:

CommonProgramFiles=C:\Program Files\Common Files
CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files
CommonProgramW6432=C:\Program Files\Common Files
PROCESSOR_ARCHITECTURE=AMD64
ProgramFiles=C:\Program Files
ProgramFiles(x86)=C:\Program Files (x86)
ProgramW6432=C:\Program Files

I’m still in the process of proving whether the environment “really is
the issue” that is directly or indirectly causing SETUPAPI to process
the .INF as though he’s 32-bit “for this one customer.”

Apparently the environment does not normally affect the SETUPAPI
behavior, since installation on Windows 64-bit has been successful for
years despite the 64-bit child process having always been spawned by
the 32-bit SETUP.EXE.

Alan Adams
Novell Client CPR Group
xxxxx@novell.com

Novell, Inc.
www.novell.com