Introduction

Here I show how to use the IsWow64Process2 API to determine whether a Windows process is running under WOW64, and whether Windows and/or the process are 32 bit or 64 bit.

TL; DR;

Let’s dive right in. Below is the completed code.

Code: Here is the Function

BOOL getBits(BOOL& windowsIs32Bit, BOOL& isWOW64, BOOL& processIs32Bit)
{
  USHORT ProcessMachine;
  USHORT NativeMachine;

  if (!IsWow64Process2(GetCurrentProcess(), &ProcessMachine, &NativeMachine)) {
    std::cerr << "IsWOW64Process2 returned FALSE (failed). GetLastError returned: " << GetLastError() << std::endl;
    return FALSE;
  }

  if (ProcessMachine == IMAGE_FILE_MACHINE_UNKNOWN) {
    isWOW64 = FALSE;

    if (NativeMachine == IMAGE_FILE_MACHINE_IA64 || NativeMachine == IMAGE_FILE_MACHINE_AMD64 || NativeMachine == IMAGE_FILE_MACHINE_ARM64) {
      windowsIs32Bit = FALSE;
      processIs32Bit = FALSE;

      return TRUE;
    }

    if (NativeMachine == IMAGE_FILE_MACHINE_I386 || NativeMachine == IMAGE_FILE_MACHINE_ARM) {
      windowsIs32Bit = TRUE;
      processIs32Bit = TRUE;

      return TRUE;
    }

    std::cerr << "Unknown Windows Architecture." << std::endl;
    return FALSE;
  }

  windowsIs32Bit = FALSE;
  isWOW64 = TRUE;
  processIs32Bit = TRUE;

  return TRUE;
}

Code: Example Usage of the Function

int main() {

  BOOL windowsIs32Bit;
  BOOL isWOW64;
  BOOL processIs32Bit;

  if (!getBits(windowsIs32Bit, isWOW64, processIs32Bit)) {
    return -1;
  }

  std::cout << (windowsIs32Bit ? "Windows is 32 bit" : "Windows is 64 bit") << std::endl;
  std::cout << (isWOW64 ? "This process *is* running under WOW64" : "This process is *not* running under WOW64") << std::endl;
  std::cout << (processIs32Bit ? "This process is 32 bit" : "This process is 64 bit") << std::endl;

  return 0;
}

Note: I have verified that the above code works with a 32 bit process running under WOW64, and with a 64 bit process running natively, both running under 64 bit Windows 10 version 1903 on a 64 bit Intel AMD64 machine. IsWow64Process2 requirements say IsWow64Process2 minimum support is Windows 10 version 1511 (client), and Windows Server 2016 (server).

I do not have a 32 bit machine or 32 bit version of Windows to test, nor an ARM or ARM64 machine. If someone has those capabilities, please consider testing my code and reporting back to me your results. Here is my contact form to get in touch with me.

Discussion

I hope the code is mostly self-explanatory. However, the below discussion breaks it down well to clarify the code.

Function Return Value and Parameters

BOOL getBits(BOOL& windowsIs32Bit, BOOL& isWOW64, BOOL& processIs32Bit)
{

The three parameters in the function getBits are reference types. The passed arguments will be set toTRUE or FALSE values after the appropriate information is extracted in the function. Since the parameters are all reference types, changing them to the appropriate values in the functions changes the values in the original arguments passed in by the caller so the caller can use them. The function returns a BOOL which is TRUE if the function completed successfully, FALSE otherwise.

Local Variables and Call to IsWow64Process2

  USHORT ProcessMachine;
  USHORT NativeMachine;

  if (!IsWow64Process2(GetCurrentProcess(), &ProcessMachine, &NativeMachine)) {
    std::cerr << "IsWOW64Process2 returned FALSE (failed). GetLastError returned: " << GetLastError() << std::endl;
    return FALSE;
  }

Above, we note that two local variables, ProcessMachine and NativeMachine, are defined. Their addresses are passed in to IsWow64Process2 and receive the function’s outputs via the pointer parameters pProcessMachine and pNativeMachine, respectively (see here).

Their names are a bit confusing because of the word Machine in both: machine is not the physical machine, but the virtual machine architecture within which the process runs under WOW64 (ProcessMachine) and the architecture of the host Windows operating system (NativeMachine).

ProcessMachine

If ProcessMachine is IMAGE_FILE_MACHINE_UNKNOWN (at this time, 0), the process is not running under WOW64; it is either 32 bit running natively under 32 bit Windows, or 64 bit running natively underneath the 64 bit Windows (see here).

If it is not IMAGE_FILE_MACHINE_UNKNOWN, it will be set to a value that reveals the type of the WOW64 process: see here. There are many different machine type constants, some referring to 64 bit architectures, and some architectures that are no longer supported by Windows versions that have IsWow64Process2. Note this qoute from these docs:

WOW64 is the x86 emulator that allows 32-bit Windows-based applications to run seamlessly on 64-bit Windows. This allows for 32-bit (x86) Windows applications to run seamlessly in 64-bit (x64) Windows, as well as for 32-bit (x86) and 32-bit (ARM) Windows applications to run seamlessly in 64-bit (ARM64) Windows.

It would appear that the only two possible image file machine constants for a process running under WOW64 returnable via pProcessMachine are:

  • IMAGE_FILE_MACHINE_I386
  • IMAGE_FILE_MACHINE_ARM

These constants would indicate a 32 bit x86 process or a 32 bit ARM process, respectively. Note that an x86 process can run under either an AMD64 or ARM64 WOW64 emulator, but a 32 bit ARM process can only run under an ARM64 WOW64 emulator.

NativeMachine

NativeMachine will return a value indicating the architecture type of the host Windows operating system That value is also an image file machine constant. As best I can tell from research, the possible values, which would be for Windows architectures supporting the IsWow64Process2 API, are:

  • IMAGE_FILE_MACHINE_I386 (32 bit x86)
  • IMAGE_FILE_MACHINE_ARM (32 bit ARM)
  • IMAGE_FILE_MACHINE_IA64 (64 bit Intel Itanium)
  • IMAGE_FILE_MACHINE_AMD64 (64 bit Intel or AMD)
  • IMAGE_FILE_MACHINE_ARM64 (64 bit ARM)

I am not sure if the above list is indeed exhaustive. If you know differently, please let me know.

GetCurrentProcess (see here) returns a pseudo handle to the current process, so the ProcessMachine information in my example will reflect the architecture of the current process.

IsWowProcess2 Function Return Value

IsWow64Process2 will return a nonzero value if it succeeds, otherwise, returning zero. If it fails, I display an error indication that includes the last-error code returned by GetLastError giving the reason for the failure (see here and here). I then return FALSE to indicate failure to the caller, in which case the four returned booleans have undefined values.

Not WOW64 Path

  if (ProcessMachine == IMAGE_FILE_MACHINE_UNKNOWN) {
    isWOW64 = FALSE;
 

The above code fragment notices the process is not running under WOW64 since IMAGE_FILE_MACHINE_UNKNOWN is returned (see above). It immediately sets isWOW64 to FALSE. The following three paths run under this conditional block:

64 Bit Windows Host Path

    if (NativeMachine == IMAGE_FILE_MACHINE_IA64 || NativeMachine == IMAGE_FILE_MACHINE_AMD64 || NativeMachine == IMAGE_FILE_MACHINE_ARM64) {
      windowsIs32Bit = FALSE;
      processIs32Bit = FALSE;

      return TRUE;
    }

If NativeMachine is any of the possible 64 bit values, indicating the host Windows architecture is 64 bit, I set windowsIs32Bit and processIs32Bit to FALSE and return success. Since we know Windows is 64 bit, and the process is not running under WOW64, we know the process is running 64 bit natively on the host OS.

32 Bit Windows Host Path

    if (NativeMachine == IMAGE_FILE_MACHINE_I386 || NativeMachine == IMAGE_FILE_MACHINE_ARM) {
      windowsIs32Bit = TRUE;
      processIs32Bit = TRUE;

      return TRUE;
    }

If NativeMachine is any of the possible 32 bit values, indicating the host Windows architecture is 32 bit, I set windowsIs32Bit and processIs32Bit to TRUE and return success. Since we know Windows is 32 bit, we know the process is running 32 bit natively on the host OS. A 64 bit process cannot run under 32 bit Windows.

Catch All Error Path

    std::cerr << "Unknown Windows Architecture." << std::endl;
    return FALSE;
  }

If the returned value is not one known by getBits, I print out an error statement and return failed.

The WOW64 Path

  windowsIs32Bit = FALSE;
  isWOW64 = TRUE;
  processIs32Bit = TRUE;

  return TRUE;
}

Finally, if ProcessMachine did not return IMAGE_FILE_MACHINE_UNKNOWN, the process is running under WOW64. windowsIs32Bit is set to FALSE because WOW64 is only possible under 64 bit Windows, isWow64 is set TRUE, and processIs32Bit is set TRUE because only a 32 bit process can run under WOW64.

Example Usage

int main() {

  BOOL windowsIs32Bit;
  BOOL isWOW64;
  BOOL processIs32Bit;

  if (!getBits(windowsIs32Bit, isWOW64, processIs32Bit)) {
    return -1;
  }

  std::cout << (windowsIs32Bit ? "Windows is 32 bit" : "Windows is 64 bit") << std::endl;
  std::cout << (isWOW64 ? "This process *is* running under WOW64" : "This process is *not* running under WOW64") << std::endl;
  std::cout << (processIs32Bit ? "This process is 32 bit" : "This process is 64 bit") << std::endl;

  return 0;
}

The usage is quite straightforward. In the example, we define the three BOOL variables to pass as arguments then call the function getBits to fill them with the appropriate values. If getBits returns FALSE, we exit the process with a return from main with the value -1 to indicate failure.

Otherwise, we print an appropriate output for each returned BOOL argument, then return 0 to indicate nominal operation of the process.

Conclusion

IsWow64Process2 provides a nice, new API for its supported Windows versions that returns more information than the old IsWow64Process (see here). There is no need to fuss about old architectures no longer supported in the latest versions of Windows, and it is a great way to figure out what the Windows host machine architecture is.

I have written this to provide hopefully robust, working example code of its usage for the community, which I could not find elsewhere. Even though I researched heavily, I am still unsure of some of the details, which I note above, and could not exercise all code paths. Help and correction are welcome via my contact form. I hope this article serves the community well.

Thanks to Pexels for the free header image.