Saturday, January 12, 2008

WINVER Mayhem. The PROCESS_ALL_ACCESS problem.

If you are like me, you want to develop Windows applications that work on all the major versions of Windows. At the moment it is Windows Vista, Windows XP SP2 and maybe Windows 2000 SP4. You also want to use the new features introduced in the new versions.

Suppose that you want to use a something that is defined only when you have _WIN32_WINNT=0x600. What do you do?

There are 3 options:

1. Copy the definition manually to one of your file. This is not clean. I hope I won't have to do this.

2. Create one build per OS version. Each of them will define _WIN32_WINNT accordingly and you just have to be sure that the code specific to an OS version is inside an #IF _WIN32_WINNT >= VERSION. This makes sense, but this is a lot of troubles. Who wants to have multiple sets of exes and dlls and ship different binaries depending on the user configuration.

3. Define _WIN32_WINNT to the highest value (0x0600 for Vista in this case), and make sure that you don't use unsupported functions/structures on previous version. You need to use GetVersionEx to get the OS version and use GetProcAddress to get the address of the functions that are not defined in prior versions.

The third option is the one that I've seen most of the time. Unfortunately, changing the value of _WIN32_WINNT to 0x0600 is causing some unexpected problems.

One example is the define for PROCESS_ALL_ACCESS. It looks like this:

#if (NTDDI_VERSION >= NTDDI_LONGHORN)
#define PROCESS_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | \
0xFFFF)
#else
#define PROCESS_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | \
0xFFF)
#endif

They changed the mask on Vista to include 0xF000. 0xF000 contains 3 unused flags and the new PROCESS_QUERY_LIMITED_INFORMATION.

Why is this a problem?

Suppose that your code is doing this:


OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

This call is now going to fail on XP and 2000 because you are trying to open a process with 0xF000, and you don't have that access on the process.

You need to fix all PROCESS_ALL_ACCESS occurences in your code. The same problem exists with THREAD_ALL_ACCESS.

What would have been a better solution? I thought PROCESS_QUERY_LIMITED_INFORMATION was only a subset of PROCESS_QUERY_INFORMATION already present in the mask. Did they really need to add it? Could they have used GENERIC_ALL instead? As we saw in this earlier post, GENERIC_ALL can mean different things on different OS versions.

1 comment:

scottjg said...

man! i spent like a bajillion hours trying to figure out why my old binary worked but my new binary (compiled on a later version of visual studio) wouldn't.

I can't believe they'd just change the #define like that! AGGHH

thanks SO MUCH for pointing this out