Search code examples
c#c++wmiwqlcpuid

Getting CPU ID code from C# to be in C++


I have this C# code to get Processor ID but I'm not able to pass it to C++, I tried a lot but I really can't, I just started in C++ and I would like to be able to get the CPU ID with C++ like I used to get with C#

This is the code I have in C#:

public static string GetProcessorID()
{
  string sProcessorID = "";
  string sQuery = "SELECT ProcessorId FROM Win32_Processor";
  ManagementObjectSearcher oManagementObjectSearcher = new ManagementObjectSearcher(sQuery);
  ManagementObjectCollection oCollection = oManagementObjectSearcher.Get();
  foreach (ManagementObject oManagementObject in oCollection)
  {
    sProcessorID = (string)oManagementObject["ProcessorId"];
  }

  return (sProcessorID);
}

Solution

  • It's a little bit longer in C++! This is a full working example, note that if you change the query from

    SELECT ProcessorId FROM Win32_Processor
    

    to

    SELECT * FROM Win32_Processor
    

    Then you can use the QueryValue function to query any property value.

    HRESULT GetCpuId(char* cpuId, int bufferLength)
    {
        HRESULT result = InitializeCom();
        if (FAILED(result))
            return result;
    
        IWbemLocator* pLocator = NULL;
        IWbemServices* pService = NULL;
        result = GetWbemService(&pLocator, &pService);
        if (FAILED(result))
        {
            CoUninitialize();
            return result;
        }
    
        memset(cpuId, 0, bufferLength);
        result = QueryValue(pService, 
                L"SELECT ProcessorId FROM Win32_Processor", L"ProcessorId",
                cpuId, bufferLength);
    
        if (FAILED(result))
        {
            pService->Release();
            pLocator->Release();
            CoUninitialize();
    
            return result;
        }
    
        pService->Release();
        pLocator->Release();
        CoUninitialize();
    
        return NOERROR;
    }
    

    First you have to do all the initialization stuffs, they're packed into these two functions:

    HRESULT InitializeCom()
    {
        HRESULT result = CoInitializeEx(0, COINIT_APARTMENTTHREADED); 
        if (FAILED(result))
            return result;
    
        result = CoInitializeSecurity(
            NULL,                           // pSecDesc
            -1,                             // cAuthSvc (COM authentication)
            NULL,                           // asAuthSvc
            NULL,                           // pReserved1
            RPC_C_AUTHN_LEVEL_DEFAULT,      // dwAuthnLevel
            RPC_C_IMP_LEVEL_IMPERSONATE,    // dwImpLevel
            NULL,                           // pAuthList
            EOAC_NONE,                      // dwCapabilities
            NULL                            // Reserved
            );
    
        if (FAILED(result) && result != RPC_E_TOO_LATE)
        {
            CoUninitialize();
    
            return result;
        }
    
        return NOERROR;
    }
    
    HRESULT GetWbemService(IWbemLocator** pLocator, IWbemServices** pService)
    {
        HRESULT result = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, 
            IID_IWbemLocator, reinterpret_cast<LPVOID*>(pLocator));
    
        if (FAILED(result))
        {
            return result;
        }
    
        result = (*pLocator)->ConnectServer(
            _bstr_t(L"ROOT\\CIMV2"),    // strNetworkResource
            NULL,                       // strUser  
            NULL,                       // strPassword
            NULL,                       // strLocale
            0,                          // lSecurityFlags
            NULL,                       // strAuthority
            NULL,                       // pCtx
            pService                    // ppNamespace
            );
    
        if (FAILED(result))
        {
            (*pLocator)->Release();     
    
            return result;
        }
    
        result = CoSetProxyBlanket(
            *pService,                      // pProxy
            RPC_C_AUTHN_WINNT,              // dwAuthnSvc
            RPC_C_AUTHZ_NONE,               // dwAuthzSvc
            NULL,                           // pServerPrincName
            RPC_C_AUTHN_LEVEL_CALL,         // dwAuthnLevel
            RPC_C_IMP_LEVEL_IMPERSONATE,    // dwImpLevel
            NULL,                           // pAuthInfo
            EOAC_NONE                       // dwCapabilities
            );
    
        if (FAILED(result))
        {
            (*pService)->Release();
            (*pLocator)->Release();     
    
            return result;
        }
    
        return NOERROR;
    }
    

    That done you can run your WQL query then you have to enumerate returned properties to find the one you need (you can make it simpler but in this way you can query multiple values). Note that if you query ALL values from Win32_Processor you'll get a lot of data. On some systems I saw it takes even 2 seconds to complete the query and to enumerate properties (so filter your query to include only data you need).

    HRESULT QueryValue(IWbemServices* pService, const wchar_t* query, const wchar_t* propertyName, char* propertyValue, int maximumPropertyValueLength)
    {
        USES_CONVERSION;
    
        IEnumWbemClassObject* pEnumerator = NULL;
        HRESULT result = pService->ExecQuery(
            bstr_t(L"WQL"),                                         // strQueryLanguage
            bstr_t(query),                                          // strQuery
            WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,  // lFlags
            NULL,                                                   // pCtx
            &pEnumerator                                            // ppEnum
            );
    
        if (FAILED(result))
            return result;
    
        IWbemClassObject *pQueryObject = NULL;
        while (pEnumerator)
        {
            try
            {
                ULONG returnedObjectCount = 0;
                result = pEnumerator->Next(WBEM_INFINITE, 1, &pQueryObject, &returnedObjectCount);
    
                if (returnedObjectCount == 0)
                    break;
    
                VARIANT objectProperty;
                result = pQueryObject->Get(propertyName, 0, &objectProperty, 0, 0);
                if (FAILED(result))
                {
                    if (pEnumerator != NULL)
                        pEnumerator->Release();
    
                    if (pQueryObject != NULL)
                        pQueryObject->Release();
    
                    return result;
                }
    
                if ((objectProperty.vt & VT_BSTR) == VT_BSTR)
                {
                    strcpy_s(propertyValue, maximumPropertyValueLength, OLE2A(objectProperty.bstrVal));
                    break;
                }
    
                VariantClear(&objectProperty);
            }
            catch (...)
            {
                if (pEnumerator != NULL)
                    pEnumerator->Release();
    
                if (pQueryObject != NULL)
                    pQueryObject->Release();
    
                return NOERROR;
            }
        } 
    
        if (pEnumerator != NULL)
            pEnumerator->Release();
    
        if (pQueryObject != NULL)
            pQueryObject->Release();
    
        return NOERROR;
    }
    

    NOTE: this code is useful if you need to gather more informations than the simple ID of the CPU. It's a generic example to get one (or more) properties from a WQL query (as you did in C#). If you do not need to get any other information (and you do not think to use WMI in your C++ programs) then you may use the __cpuid() intrinsic as posted in a comment.

    __cpuid()

    The ProcessorId property from WMI has following description:

    Processor information that describes the processor features. For an x86 class CPU, the field format depends on the processor support of the CPUID instruction. If the instruction is supported, the property contains 2 (two) DWORD formatted values. The first is an offset of 08h-0Bh, which is the EAX value that a CPUID instruction returns with input EAX set to 1. The second is an offset of 0Ch-0Fh, which is the EDX value that the instruction returns. Only the first two bytes of the property are significant and contain the contents of the DX register at CPU reset—all others are set to 0 (zero), and the contents are in DWORD format.

    A good implementation should check more about strange cases but a naive implementation may be:

    std::string GetProcessorId()
    {
     int info[4] = { -1 };
    
     __cpuid(info, 0);
     if (info[0] < 1)
      return ""; // Not supported?!
    
     // Up to you...you do not need to mask results and you may use
     // features bits "as is".    
     __cpuid(info, 1);
     int family = info[0];
     int features = info[3];
    
     std::stringstream id;
     id << std::hex << std::setw(4) << std::setfill('0') << family << features;
    
     return id.str();
    }
    

    See also this post for a better C++-ish implementation.