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
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\Foo, and make sure you’re using
IntPtrin 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
- 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 😉