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.

  1. No comments yet.

  1. No trackbacks yet.