WOW64 and Your Profiler

Has this ever happened to you?

My profiler loads and runs great on my 32 bit box. But when I try to run it on a 64 bit box, it never loads. Shouldn't the WOW just make this all work?!

When it comes to running your profilers on 64 bit boxes, there aren't any special rules dictated by the CLR that you must follow to get it to work. But if you're not careful, or you're unaware of how WOW64 works, it's easy to misconfigure your box.

OS Redirectors

First off, get familiar with WOW64. MSDN has essential information here and in child topics. I'm not going to repeat all that information here, but I will mention a couple things to take note of.

The registry redirector and file system redirector create a logical view under which a 32 bit app executes, so that the 32 bit app feels like it is executing on a 32 bit machine. The registry redirector will show a subset of the registry to a 32 bit app, with some automatic remapping. An important example (there are more!):

32 bit app asks for "HKEY_CLASSES_ROOT\CLSID".
32 bit app gets "HKEY_CLASSES_ROOT\Wow6432Node\CLSID"

This is how the system keeps 32-bit COM objects separate from 64-bit COM objects.

Similarly, file system paths get redirected to make the 32 bit app feel more at home. Example:

32 bit app asks for "%windir%\system32\Blah".
32 bit app gets "%windir%\SysWow64\Blah"

There are exceptions to this, but that's the general rule. For those learning this for the first time, it's worthwhile to emphasize this point.

The 64 bit system tools and DLLs are in system32.
The 32 bit system tools and DLLs are in SysWow64.

At first, this might seem a little reversed from what you'd expect, but you get used to it after a while.

When a 32 bit app starts up, its environment variables (particularly its PATH) are adjusted in a natural way so that the DLLs and other executables it wants are generally in the places it expects to find them.

Register this

Let's put this knowledge into practice. What happens when you run regedit.exe? The answer is, which regedit.exe are you running? Ok, that's just another question. To answer that, from what environment are you running regedit.exe? Darn it, did it again, another question. Let's say you spawn cmd.exe from Start.Run. That gives you a 64 bit command prompt, from the real %windir%\system32\cmd.exe (unless you do some wacky things to your path). That will give you the real 64 bit environment and paths. So if you type regedit.exe from that command prompt, you'll get the real 64 bit %windir%\system32\regedit.exe. That guy will show you the whole registry.

But you can also run a 32 bit command prompt from Start.Run via %windir%\SysWow64\cmd.exe. When you do that and type "regedit.exe" you're gonna get the 32 bit %windir%\SysWow64\regedit.exe, which will show you the 32-bit logical view of the registry, with Registry Redirection in place.

Now what if, instead of running regedit.exe, you run regsvr32.exe? Same deal: depending on your environment, you'll either get the 32-bit or the 64-bit version of regsvr32.exe. And let's say you were running regsvr32.exe to register your profiler DLL. Well, you'll need to make sure the regsvr32 you run corresponds to the "bitness" of your profiler DLL. One of the WOW64 rules says that a 64 bit process cannot load 32 bit DLLs, and a 32 bit process cannot load 64 bit DLLs. So if the bitness of regsvr32 doesn't match the bitness of your profiler DLL, then regsvr32 will fail to LoadLibrary your DLL.

Profilers: The magic ends with you

One of the nice features of the CLR is the option to go for platform independence. You can write a C# app, compile with csc.exe /platform:anycpu, and you get an executable with a pretty cool property. Slap that executable onto an x64 or ia64 machine (with the appropriate CLR installed), and it will run as a native 64-bit app in that machine's 64-bit architecture. Slap that same executable onto an x86 machine, and it will run as a native 32-bit app. For managed applications that don't make assumptions about bitness (e.g., that don't rely on the size of pointers or on native COM objects of a particular bitness being used), this is pretty nifty. See Josh William's informative blog entry for more information.

But profilers don't get to live in this magical world of chocolate fountains, lollipop skyscrapers, and dancing leprechauns. Profilers are native DLLs, and you have to explicitly choose whether to compile your profiler DLL for x86, x64, or ia64. And as you probably know, it's not a simple matter of swapping in a new compiler and saying, "go!". There's a fair amount of platform-specific code you need to write, depending on which features of the CLR Profiling API you use. Using the enter / leave / tailcall hooks is but one example. (See Jonathan Keljo's blog entry on that to get an idea.) And of course there are the little things, too, like several CLR Profiling API types (e.g., FunctionIDs) becoming 64 bits long on 64 bit architectures. Anyway, it's out of the scope of this topic to talk about all the pitfalls you can fall into. But when you embark on compiling for multiple architectures, it should hopefully become pretty clear what changes you'll need to make.

Once you've decided all the architectures you wish to support, you need to provide a version of your DLL for each architecture, and register it appropriately. As a quick recap, here's an easy way to do the registration:

x86 Machine
Just run regsvr32 like you usually do.

x64 or ia64 Machine

If you want to be able to profile 64 bit apps, run 64 bit regsvr32 against your 64 bit Profiler DLL
If you want to be able to profile 32 bit apps (WOW),   run 32 bit regsvr32 against your 32 bit Profiler DLL.
If you want to support both, do both!

Note that, although you may not mix an EXE of one bitness with a DLL of another bitness, you may still spawn child EXEs of any bitness you like from any parent EXE. So you can still author one installation program that copies and registers both 32 bit and 64 bit copies of your profiler DLL.

COR_PROFILER

Ok, so your profiler DLL is registered, but what about those pesky environment variables you're supposed to set in order to get your profiler DLL actually loaded when the managed app runs? This part is easy. Microsoft generally recommends that, if you're creating a COM object and compiling for both 32 and 64 bits, just use the same CLSID in both versions. If you register both versions of the COM object on the same machine, then that same CLSID will show up in both branches of the registry:

under HKEY_CLASSES_ROOT\CLSID for 64 bit apps
under HKEY_CLASSES_ROOT\Wow6432Node\CLSID for 32 bit apps

So if you just set COR_PROFILER to your one and only CLSID, then you don't even need to know beforehand the bitness of the managed application that will be spawned from that environment. 64 bit apps will know to look for your CLSID under the 64 bit version of the CLSID hive, and will find the path to the 64 bit version of your profiler DLL. 32 bit apps running under the WOW will get redirected to the 32 bit version of the CLSID hive, and will find the path to the 32 bit version of your profiler DLL.

Double your bits, double your pleasure

Hopefully this clears things up. Really, there's only one key thing to remember, which is to be aware of the various WOW64 redirectors. Once you understand how they work, it becomes clear how to register your profiler DLL, and where the managed app expects to find what you registered.

Comments

  • Anonymous
    January 17, 2007
    I'm looking to get the Adobe IFilter to run on Server 2003 x64.  It looks like this article is what I need to do, but for the life of me, I can't get it to work right. Do you have any suggestions that will help me to get this working?

  • Anonymous
    January 25, 2007
    I'm afraid I'm not familiar with Adobe IFilter, though I do not believe it is a CLR profiler.  So any tips from this article would only be useful in the most general way.  A more complete discussion of WOW64 can be found on MSDN at http://msdn.microsoft.com/library/en-us/winprog64/winprog64/running_32_bit_applications.asp.

  • Anonymous
    May 16, 2007
    The comment has been removed

  • Anonymous
    May 16, 2007
    Hi, Divya.  If you're concerned that registration didn't do what you expect, why not open up the registry and take a look?  You can also use a debugger to step through your DLLs DllRegisterServer while debugging regsvr32.exe. If a DLL is successfully loaded by a 64-bit process (including the 64-bit regsvr32.exe), then the DLL is a 64-bit DLL.  If a DLL is successfully loaded by a 32-bit process (including the 32-bit regsvr32.exe), then the DLL is a 32-bit DLL. Check the Windows API for ways to detect the bitness of the OS.  Some functions to start with: IsWow64Process & GetNativeSystemInfo.  Also, if you're writing a batch script, you can probably rely on environment variables like PROCESSOR_ARCHITECTURE and PROCESSOR_ARCHITEW6432

  • Anonymous
    May 23, 2007
    hi David Thanks for replying and sorry in responding lately I tried debugging, the registration was successful but no entry in regedit for 64bit dll. Am using c# coding to check the OS and i used System.IntPtr.size value to see the type of OS but its not functioning properly. Thanks, Divya

  • Anonymous
    May 31, 2007
    Hi, Divya.  I'm not sure what you mean by "registration was successful", if you don't actually end up with the entry in the registry.  Sounds like you'll want to step through your DllRegisterServer export to find out what went wrong with registration. You mentioned using C# code--if you're talking about using C# code for doing the actual registration, that's worrisome to me.  Your profiler should be coded in native C/C++, not in managed code.  And you should have two physical DLLs, one for 64-bit profiling and one for 32-bit profiling.

  • Anonymous
    July 19, 2007
    The comment has been removed

  • Anonymous
    July 19, 2007
    Hi, James, note that this blog is dedicated to the CLR Profiling API.  If I understand your question properly, it is unrelated to the CLR Profiling API, which would put it outside the scope of this blog (forgive me if I'm misinterpreting). Although I'm probably unable to solve your problem, I can try to push you in the right direction.  Focus on exactly which DLL is failing to load (your managed DLL or the MS 64 bit DLL), and use a debugger like windbg to catch the exception that prevents the load.  The problem might be as simple as a bitness mismatch or as complex as a domain-specific problem with the particular application you're working on.  The key is to narrow down the problem so you find the exact cause of the load failure. Also, you're more likely to get a helpful response if you try one of the public forums dedicated to this kind of thing, like http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=42&SiteID=1 Good luck!

  • Anonymous
    December 11, 2007
    This is the first of some tips to help you debug your profiler.  Note that these tips assume you're