Win32 code to get EDID in Windows XP/7

Jared Updike picture Jared Updike · Jun 15, 2011 · Viewed 13.9k times · Source

I found this page and was unable to get any useful information out of it (it searches the registry for something but never finds it and goes into an infinite loop).

As this question regarding getting the monitor serial UID ("EDID information") asks I would like the same information but from the point of view of Win32 C code (or C/C++ DDK code, or whatever) instead of Linux.

Answer

Jared Updike picture Jared Updike · Jun 16, 2011

First I got a C# version working using WMI Code Creator:

        try
        {
            ManagementObjectSearcher searcher =
                new ManagementObjectSearcher("root\\WMI",
                "SELECT * FROM WmiMonitorID");

            foreach (ManagementObject queryObj in searcher.Get())
            {
                Console.WriteLine("-----------------------------------");
                Console.WriteLine("WmiMonitorID instance");
                Console.WriteLine("-----------------------------------");
                //Console.WriteLine("Active: {0}", queryObj["Active"]);
                Console.WriteLine("InstanceName: {0}", queryObj["InstanceName"]);
                dynamic snid = queryObj["SerialNumberID"];

                Console.WriteLine("SerialNumberID: (length) {0}", snid.Length);
                Console.WriteLine("YearOfManufacture: {0}", queryObj["YearOfManufacture"]);
                dynamic code = queryObj["ProductCodeID"];
                string pcid = "";
                for (int i = 0; i < code.Length; i++)
                {
                    pcid = pcid + Char.ConvertFromUtf32(code[i]);
                    //pcid = pcid +code[i].ToString("X4");
                }
                Console.WriteLine("ProductCodeID: " +  pcid);
            }
        }
        catch (ManagementException e)
        {
            Console.WriteLine("An error occurred while querying for WMI data: " + e.Message);
        }

Below is the C++ code I found and tweaked to work with the InstanceName field in WmiMonitorID class (EDID structure) that I wanted to read. Don't forget to add setupapi.lib to your Linker > Additional Libraries build setting.

#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <wbemidl.h>
# pragma comment(lib, "wbemuuid.lib")

int EnumMonitorIDs()
{
    ret.clear();

    HRESULT hres =  CoInitializeEx(0, COINIT_MULTITHREADED); 
    if (FAILED(hres))
    {
        cout << "Failed to initialize COM library. Error code = 0x" 
            << hex << hres << endl;
        return 1;                  // Program has failed.
    }

    hres =  CoInitializeSecurity(
        NULL, 
        -1,                          // COM authentication
        NULL,                        // Authentication services
        NULL,                        // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
        RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation  
        NULL,                        // Authentication info
        EOAC_NONE,                   // Additional capabilities 
        NULL                         // Reserved
        );

    if (FAILED(hres))
    {
        cout << "Failed to initialize security. Error code = 0x" 
            << hex << hres << endl;
        CoUninitialize();
        return 1;
    }

    IWbemLocator *pLoc = NULL;
    hres = CoCreateInstance(
        CLSID_WbemLocator,             
        0, 
        CLSCTX_INPROC_SERVER, 
        IID_IWbemLocator, (LPVOID *) &pLoc);

    if (FAILED(hres))
    {
        cout << "Failed to create IWbemLocator object."
            << " Err code = 0x"
            << hex << hres << endl;
        CoUninitialize();
        return 1;                 // Program has failed.
    }

    IWbemServices *pSvc = NULL; 
    BSTR AbackB = SysAllocString(L"root\\WMI");
    // Connect to the root\cimv2 namespace with
    // the current user and obtain pointer pSvc
    // to make IWbemServices calls.
    hres = pLoc->ConnectServer(
         AbackB, // Object path of WMI namespace
         NULL,                    // User name. NULL = current user
         NULL,                    // User password. NULL = current
         0,                       // Locale. NULL indicates current
         NULL,                    // Security flags.
         0,                       // Authority (e.g. Kerberos)
         0,                       // Context object 
         &pSvc                    // pointer to IWbemServices proxy
         );
    SysFreeString(AbackB);

    if (FAILED(hres))
    {
        cout << "Could not connect. Error code = 0x" 
             << hex << hres << endl;
        pLoc->Release();     
        CoUninitialize();
        return 1;                // Program has failed.
    }

    hres = CoSetProxyBlanket(
       pSvc,                        // Indicates the proxy to set
       RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
       RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
       NULL,                        // Server principal name 
       RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
       RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
       NULL,                        // client identity
       EOAC_NONE                    // proxy capabilities 
    );

    if (FAILED(hres))
    {
        cout << "Could not set proxy blanket. Error code = 0x" 
            << hex << hres << endl;
        pSvc->Release();
        pLoc->Release();     
        CoUninitialize();
        return 1;               // Program has failed.
    }

    BSTR wql = SysAllocString(L"WQL");
    BSTR select = SysAllocString(L"SELECT * FROM WmiMonitorID");
    IEnumWbemClassObject* pEnumerator = NULL;
    hres = pSvc->ExecQuery(
        wql,
        select,
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &pEnumerator);
    SysFreeString(wql);
    SysFreeString(select);

    if (FAILED(hres))
    {
        cout << "Query for operating system name failed."
            << " Error code = 0x" 
            << hex << hres << endl;
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return 1;               // Program has failed.
    }

    IWbemClassObject *pclsObj = 0;
    ULONG uReturn = 0;   
    while (pEnumerator)
    {
        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, 
            &pclsObj, &uReturn);

        if (0 == uReturn)
        {
            break;
        }

        // ok, we have the EDID record, pull some fields out of it

        VARIANT vtProp;
        hr = pclsObj->Get(L"InstanceName", 0, &vtProp, 0, 0);
        wcout << "----------------" << endl << "InstanceName : " << vtProp.bstrVal << endl;
        VariantClear(&vtProp);



        pclsObj->Release();
    }

    pSvc->Release();
    pLoc->Release();
    pEnumerator->Release();
    CoUninitialize();

    return 0;
}