Archives

Anticipation

  • No dates present

.NET 2.0 and 64-bit Windows

For those who don’t already know, my main desktop machine at the moment is running 64-bit Windows. There probably isn’t a particularly good reason for doing that — after all, I’ve only got 2GB of RAM in there at the moment, so I’m not even hitting the 3GB limit of 32-bit Windows yet. But when I bought my shiny new 64-bit processor I figured, why the heck not? And it has the added bonus of being able to make sure my own software works on a 64-bit platform, too. Of course, I could just be masochistic.

Having said that, for the most part I haven’t had any problems with native applications (there have been a few issues, but I don’t want to go into that now. Maybe I’ll save it for a later Rant post). Even games (and their evil DRM disc-detection software) run just fine.

Where I have hit some issues though is with .NET applications. “Wait!” I hear you say, “Aren’t .NET apps supposed to be platform-independent?” Well, they are (in a way), and it’s partly that which is their downfall :)

Starting with .NET 2.0, the JIT engine within the CLR can now emit 64-bit native code. And why not, after all — .NET apps are all just IL anyway, and if you translate that into whichever type of code is best for your processor (be that 32 or 64 bit), then naturally it’ll run faster and/or better, right?

And in fact that is the case, for the most part. Applications and libraries built in pure .NET 2.0 code can quite happily compile to and run as either 32 or 64 bit applications with no modification and almost no thought at all. Which is most likely why the default behaviour of Visual Studio 2005 is to compile .NET assemblies as “processor agnostic” — meaning they can swing both ways.

Where this starts to fall down is if your .NET app/library tries to interact with the outside world, which is still largely 32-bit. Calling into system DLLs (kernel32, etc) is ok, since those come in both 32 and 64 bit flavours after all, and you’ll end up calling whichever one is appropriate automatically. DirectX, though, only comes as 32-bit (at least the last time I looked at it; mind you that wasn’t DirectX itself [which is dual-bit], but the managed interface to it, which was written in .NET 1.1 and thus 32-bit only); and the same is most likely true of any other third-party libraries your application wants to interact with. And 64-bit code can’t call into 32-bit DLLs — at least not with the standard P/Invoke mechanisms.

But there’s an even more insidious trap. It’s been recommended for a while that applications store configuration and related settings in the Registry (although config files are starting to get favoured again in the .NET age, this time as hierarchical XML instead of flat INI). And sometimes even .NET apps need to access the Registry for something, whether it’s to spurn the config files and keep settings in there, or to read settings or paths for some third-party application you’re trying to interop with, or simply to update a file association — it’s all in there. And .NET contains some convenient routines to access the registry in the form of the Microsoft.Win32.Registry class.

But wait — which Registry are you actually talking to? You see, in 64-bit Windows there are effectively two Registries. One that 32-bit apps use, and one for 64-bit apps. It’s structured so that a 64-bit app accessing HKLM\Software\Foo will access the 64-bit Registry, and a 32-bit app using the exact same path will access the 32-bit Registry instead. Presumably this was done because of all the COM registrations and similar in there — after all, a 32-bit app doesn’t want to look up a COM component and discover a 64-bit component, since it won’t be able to talk to it. And the reverse is true as well. You’d think that they could just come up with new unique GUIDs for the 64-bit versions of the components, but again, this was probably an ease-of-porting decision. Still, it means care needs to be taken.

You see, your shiny new .NET 2.0 app will by default be running in 64-bit mode on 64-bit versions of Windows. This means that it will be accessing the 64-bit Registry, and will only be able to talk to 64-bit libraries. I encountered a case recently where an installer (native, 32-bit) wrote a path to the (32-bit) Registry, and then the application (.NET, 64-bit) tried to read it out and crashed when it couldn’t find it (since it looked in the 64-bit Registry).

There are a couple of different directions you can go from here, which really depend on the sorts of things you’re trying to do:

  • Stop using it
    Do you really need that component? If your app can live without it without any significant loss, then this might be the way to go. If you can get your app to pure IL then it won’t matter what bit-level the OS is running at.
  • Port to 64-bit
    If all you’re trying to do are simple things like reading/writing paths from the Registry, or using P/Invoke on system DLLs (where 64-bit versions are already available), then it’s fairly straightforward to get your code working in 64-bit again. Just change your Registry code (that wants to get to the 32-bit Registry) to access HKLM\Software\Wow6432Node\Foo instead of HKLM\Software\Foo, and make sure you’re using IntPtr in your P/Invoke calls wherever pointers are being passed around, and not a regular integer. (And how do you know whether you’re running with 32 or 64 bits? Check IntPtr.Size.)
  • Force 32-bit
    If you’re talking to some external third-party libraries that you really can’t get in 64-bit mode, then it’s probably easier to force your app to run in 32-bit mode even on 64-bit Windows. This is actually pretty simple. If you go to Build -> Configuration Manager, you’ll see that the Active Solution Platform will be “Any CPU”. Click on the dropdown and go to New, then select x86 as your new platform, and copy settings from “Any CPU”. This should automatically create and assign all the project configurations too. You can now either delete the old “Any CPU” configurations (which you’ll need to do individually on each project as well as on the solution itself), or just leave them there but don’t use them. Either way, after a Rebuild your application should now be ready to go — it’ll be a permanent 32-bit app.

If you’ve gotten yourself the other end of the stick — you’re running 64-bit Windows and have encountered an app that’s being promoted to 64 bits when it wasn’t expecting it — then you can use the corflags.exe tool (part of the .NET 2.0 SDK) to force the app into 32-bit mode by using the /32BITS+ flag. But it’d be nice if application vendors can sort this out beforehand 😉

If you want more detailed information, have a look at MSDN here and here.

9 comments to .NET 2.0 and 64-bit Windows

  • Thanks for the explanation, you helped me a lot with this article!

  • Great post, thanks! I spent 20 minutes saying to myself, “But I *didn’t write* a 64-bit application!?”

  • […] NET 2.0 and 64-bit Windows x64 XNA issue, or DssProxy issue, or System.Reflection issue…? Run 32-bit applications on x64 Windows servers way to force 32bit execution of .NET 2.0 in a x64 environment? How to run a ssis package from .net code in 32-bit mode on a 64bit […]

  • jowin

    This article helps. My team and I came across a problem of having to deploy our application in Windows 7 x64. However, a module needs to access an unmanaged DLL that creates a bar code. We used P/invoke to do the job. It works on x86 PCs. But the new x64, we are having problems because the dll does not create the bar code.

    Could you help me on this? thanks.

  • If it’s a 32-bit native DLL (as seems likely), then you only really have two choices:

    1. Switch your application to 32-bit (platform to x86, as mentioned in the article).
    2. Call a separate “helper” process that has been compiled as 32-bit (whether native or managed).

    For option 2, you can either make an .exe file and pass command line parameters to it and have it write files (or use IPC mechanisms if your needs are more complicated), or you can make a COM .dll and register it for out-of-process calls. (I haven’t actually tried that last one; it might be tricky to make the call since the 64-bit app won’t be able to see a 32-bit COM registration. But I have heard that it’s possible.)

    Well, another option is to write (or find) a pure managed code implementation that does something equivalent (which should be fairly easy if all the component is doing is creating that barcode… might be more complicated if it has some business rules or is generating the barcode content rather than the barcode image itself).

    I should probably update this post sometime; some of the things it’s saying are getting a bit dated.

  • BumpaRoy

    Great article! Do you know if .Net 3.5 has these same issues?

    How do I know in my code whether to call HKLMSoftwareFoo or HKLMSoftwareWow6432NodeFoo?

  • The default is “Any CPU”, and “Any CPU” means “either x86 or x64, depending on processor type”, so yes it does.

    It’s only an issue if you need to interop with unmanaged stuff. If you’re writing a pure managed application then ignore this whole issue and you’ll just get a happily-executing dual-32/64-bit app.

    If you do specifically need to access the 32-bit registry for some reason, then either force your app to 32-bit (by switching from “Any CPU” to “x86”) or use the Wow6432Node if you’re running 64-bit (which you can determine via IntPtr.Size).

    Apparently, coming up in .NET 4.0 will be some better support for cross-bit registry access.

  • Hello, I believe your website could be having browser compatibility issues.

    Whenever I take a look at your blog in Safari, it looks fine but
    when opening in IE, it’s got some overlapping issues. I just wanted to provide you with a quick heads up! Aside from that, great website!

  • Favatar
    cpu

    Hi, after reading this amazing piece of writing i am also cheerful to share my knowledge here with mates.

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>