Detecting the CPU architecture in F#
If you google for methods of detecting the cpu architecture, you’ll find lots of pages about how to use WMI and ManagementObjectSearcher. This is an immensely slow operation which takes more than a second to complete (at least when I’ve timed it on a 2.8 GHz 4 core machine), so don’t use it. A quicker method is to use PInvoke to call the GetNativeSystemInfo function of the Windows API directly.
The PInvoke members reside in the System.Runtime.InteropServices namespace, so we’ll start by opening it. For some reason, Microsoft has never bothered to provide us with .NET definitions for Windows API types and functions, so they are wheels that we all have to reinvent. In this case, SYSTEM_INFO is the type that we’ll have to redefine in .NET code:
open System open System.Runtime.InteropServices #nowarn "9" "51" [<Struct; StructLayout(LayoutKind.Sequential)>] type SYSTEM_INFO = val mutable wProcessorArchitecture : uint16 val mutable wReserved : uint16 val mutable dwPageSize : uint32 val mutable lpMinimumApplicationAddress : IntPtr val mutable lpMaximumApplicationAddress : IntPtr val mutable dwActiveProcessorMask : UIntPtr val mutable dwNumberOfProcessors : uint32 val mutable dwProcessorType : uint32 val mutable dwAllocationGranularity : uint32 val mutable wProcessorLevel : uint16 val mutable wProcessorRevision : uint16
Unless we switch off warnings 9 and 51, the compiler will complain about not being able to generate verifiable IL code.
Next, we’ll need to import the Windows API GetNativeSystemInfo function:
[<DllImport("kernel32.dll")>] extern void private GetNativeSystemInfo(SYSTEM_INFO *lpSystemInfo)
Finally, we’ll define an F# type for the CPU architecture, duplicate the PROCESSOR_ARCHITECTURE constants from Windows API and call GetNativeSystemInfo to retrieve the architecture of the CPU that our program is running on:
type CpuArchitecture = | X86 | X64 | ARM | IA64 | Unknown let getCpuArchitecture () = let PROCESSOR_ARCHITECTURE_INTEL = 0us; let PROCESSOR_ARCHITECTURE_ARM = 5us; let PROCESSOR_ARCHITECTURE_IA64 = 6us; let PROCESSOR_ARCHITECTURE_AMD64 = 9us; let PROCESSOR_ARCHITECTURE_UNKNOWN = 0xFFFFus; let mutable sysInfo = SYSTEM_INFO() GetNativeSystemInfo(&&sysInfo) match sysInfo.wProcessorArchitecture with | p when p = PROCESSOR_ARCHITECTURE_INTEL -> X86 | p when p = PROCESSOR_ARCHITECTURE_ARM -> ARM | p when p = PROCESSOR_ARCHITECTURE_IA64 -> IA64 | p when p = PROCESSOR_ARCHITECTURE_AMD64 -> X64 | _ -> CpuArchitecture.Unknown
Don’t make the all too common mistake of confusing your program’s bitness with your cpu’s architecture. The former is determined simply by (IntPtr.Size*8). To illustrate it, here’s a program that uses our getCpuArchitecture function:
let printPlatform () = printfn "Running on an %A in %d bit mode." (getCpuArchitecture ()) (IntPtr.Size*8)
If you run this in F# Interactive on a 64-bit OS, you’ll get a result like
Running on an X64 in 32 bit mode.
This is because F# interactive runs in 32-bit mode (at least in Visual Studio 2010), but GetNativeSystemInfo still correctly detects that the CPU is an x64. On a 32 bit OS running on an x64, the reported CPU type will be x86 because that’s what the OS thinks it’s running on.
And that, ladies and gentlemen, is how we do that.