现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

通过WMI获取网卡MAC地址、硬盘序列号、主板序列号、CPU ID、BIOS序列号

2013-01-26 22:23 工业·编程 ⁄ 共 8774字 ⁄ 字号 暂无评论

/* ----------------------------------------------------------
文件名称:WMI_DeviceQuery.h

作者:秦建辉

MSN:splashcn@msn.com

版本历史:
    V1.4    2010年05月17日
            修正了硬盘序列号处理中的错误。现在和EVEREST Ultimate Edition 5.5一致。

    V1.3    2010年05月11日
            增加了对网卡原生MAC地址的查询。

    V1.2    2010年05月05日
            增加对硬盘序列号的进一步处理。

    V1.1    2010年04月30日
            修正微软MSDN例子错误,并增加对虚拟机网卡的判断。
           
    V1.0    2010年04月27日
            完成正式版本。

功能描述:
    基于WMI获取设备属性:
        0:网卡原生MAC地址
        1:硬盘序列号
        2:主板序列号
        3:CPU ID
        4:BIOS序列号
        5:主板型号
        6:网卡当前MAC地址

接口函数:
    WMI_DeviceQuery
------------------------------------------------------------ */
#pragma once

#include <windows.h>

#ifndef MACRO_T_DEVICE_PROPERTY
    #define MACRO_T_DEVICE_PROPERTY

    #define PROPERTY_MAX_LEN    128    // 属性字段最大长度
    typedef struct _T_DEVICE_PROPERTY
    {
        TCHAR szProperty[PROPERTY_MAX_LEN];
    } T_DEVICE_PROPERTY;
#endif

#define WMI_QUERY_TYPENUM    7    // WMI查询支持的类型数

#ifdef __cplusplus
extern "C"
{
#endif

/*
功能:通过WMI获取设备属性
参数说明:
    iQueryType:需要查询的设备属性
            0:网卡原生MAC地址
            1:硬盘序列号
            2:主板序列号
            3:CPU ID
            4:BIOS序列号
            5:主板型号
            6:网卡当前MAC地址
    properties:存储设备属性值
    iSize:可存储的最大设备个数
返回值:
     -1:不支持的设备属性值
     -2:WMI连接失败
     -3:不正确的WQL查询语句
    >=0:获取的设备个数   
*/
INT WMI_DeviceQuery( INT iQueryType, T_DEVICE_PROPERTY *properties, INT iSize );

#ifdef __cplusplus
}
#endif
/////////////////////

实现文件:WMI_DeviceQuery.cpp

#include "WMI_DeviceQuery.h"
#include <comutil.h>
#include <Wbemidl.h>
#include <tchar.h>
#include <strsafe.h>
#include <algorithm>
#include <atlconv.h>
#include <ntddndis.h>

#pragma comment (lib, "comsuppw.lib")
#pragma comment (lib, "wbemuuid.lib")

typedef struct _T_WQL_QUERY
{
    CHAR*    szSelect;        // SELECT语句
    WCHAR*    szProperty;        // 属性字段
} T_WQL_QUERY;

// WQL查询语句
const T_WQL_QUERY szWQLQuery[] = {
    // 网卡原生MAC地址
    "SELECT * FROM Win32_NetworkAdapter WHERE (MACAddress IS NOT NULL) AND (NOT (PNPDeviceID LIKE 'ROOT%'))",
    L"PNPDeviceID",

    // 硬盘序列号
    "SELECT * FROM Win32_DiskDrive WHERE (SerialNumber IS NOT NULL) AND (MediaType LIKE 'Fixed hard disk%')",
    L"SerialNumber",

    // 主板序列号
    "SELECT * FROM Win32_BaseBoard WHERE (SerialNumber IS NOT NULL)",
    L"SerialNumber",   

    // 处理器ID
    "SELECT * FROM Win32_Processor WHERE (ProcessorId IS NOT NULL)",
    L"ProcessorId",

    // BIOS序列号
    "SELECT * FROM Win32_BIOS WHERE (SerialNumber IS NOT NULL)",
    L"SerialNumber",

    // 主板型号
    "SELECT * FROM Win32_BaseBoard WHERE (Product IS NOT NULL)",
    L"Product",

    // 网卡当前MAC地址
    "SELECT * FROM Win32_NetworkAdapter WHERE (MACAddress IS NOT NULL) AND (NOT (PNPDeviceID LIKE 'ROOT%'))",
    L"MACAddress",
};

// 通过“PNPDeviceID”获取网卡原生MAC地址
static BOOL WMI_DoWithPNPDeviceID( const TCHAR *PNPDeviceID, TCHAR *MacAddress, UINT uSize )
{
    TCHAR    DevicePath[MAX_PATH];
    HANDLE    hDeviceFile;   
    BOOL    isOK = FALSE;

    // 生成设备路径名
    StringCchCopy( DevicePath, MAX_PATH, TEXT("////.//") );
    StringCchCat( DevicePath, MAX_PATH, PNPDeviceID );
    StringCchCat( DevicePath, MAX_PATH, TEXT("#{ad498944-762f-11d0-8dcb-00c04fc3358c}") );

    // 将“PNPDeviceID”中的“/”替换成“#”,以获得真正的设备路径名
    std::replace( DevicePath + 4, DevicePath + 4 + _tcslen(PNPDeviceID), TEXT('//'), TEXT('#') );

    // 获取设备句柄
    hDeviceFile = CreateFile( DevicePath,
        0,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL,
        OPEN_EXISTING,
        0,
        NULL);

    if( hDeviceFile != INVALID_HANDLE_VALUE )
    {   
        ULONG    dwID;
        BYTE    ucData[8];
        DWORD    dwByteRet;       

        // 获取网卡原生MAC地址
        dwID = OID_802_3_PERMANENT_ADDRESS;
        isOK = DeviceIoControl( hDeviceFile, IOCTL_NDIS_QUERY_GLOBAL_STATS, &dwID, sizeof(dwID), ucData, sizeof(ucData), &dwByteRet, NULL );
        if( isOK )
        {    // 将字节数组转换成16进制字符串
            for( DWORD i = 0; i < dwByteRet; i++ )
            {
                StringCchPrintf( MacAddress + (i << 1), uSize - (i << 1), TEXT("%02X"), ucData[i] );
            }

            MacAddress[dwByteRet << 1] = TEXT('/0');    // 写入字符串结束标记
        }

        CloseHandle( hDeviceFile );
    }

    return isOK;
}

static BOOL WMI_DoWithHarddiskSerialNumber( TCHAR *SerialNumber, UINT uSize )
{
    UINT    iLen;
    UINT    i;

    iLen = _tcslen( SerialNumber );
    if( iLen == 40 )    // InterfaceType = "IDE"
    {    // 需要将16进制编码串转换为字符串
        TCHAR ch, szBuf[32];
        BYTE b;       

        for( i = 0; i < 20; i++ )
        {    // 将16进制字符转换为高4位
            ch = SerialNumber[i * 2];
            if( (ch >= '0') && (ch <= '9') )
            {
                b = ch - '0';
            }
            else if( (ch >= 'A') && (ch <= 'F') )
            {
                b = ch - 'A' + 10;
            }
            else if( (ch >= 'a') && (ch <= 'f') )
            {
                b = ch - 'a' + 10;
            }
            else
            {    // 非法字符
                break;
            }

            b <<= 4;

            // 将16进制字符转换为低4位
            ch = SerialNumber[i * 2 + 1];
            if( (ch >= '0') && (ch <= '9') )
            {
                b += ch - '0';
            }
            else if( (ch >= 'A') && (ch <= 'F') )
            {
                b += ch - 'A' + 10;
            }
            else if( (ch >= 'a') && (ch <= 'f') )
            {
                b += ch - 'a' + 10;
            }
            else
            {    // 非法字符
                break;
            }

            szBuf[i] = b;
        }

        if( i == 20 )
        {    // 转换成功
            szBuf[i] = L'/0';
            StringCchCopy( SerialNumber, uSize, szBuf );
            iLen = _tcslen( SerialNumber );
        }
    }

    // 每2个字符互换位置
    for( i = 0; i < iLen; i += 2 )
    {
        std::swap( SerialNumber[i], SerialNumber[i+1] );
    }

    // 去掉空格
    std::remove( SerialNumber, SerialNumber + _tcslen(SerialNumber) + 1, L' ' );

    return TRUE;
}

static BOOL WMI_DoWithProperty( INT iQueryType, TCHAR *szProperty, UINT uSize )
{
    BOOL isOK = TRUE;

    switch( iQueryType )
    {
    case 0:        // 网卡原生MAC地址       
        isOK = WMI_DoWithPNPDeviceID( szProperty, szProperty, uSize );
        break;

    case 1:        // 硬盘序列号
        isOK = WMI_DoWithHarddiskSerialNumber( szProperty, uSize );
        break;

    case 6:        // 网卡当前MAC地址
        // 去掉冒号
        std::remove( szProperty, szProperty + _tcslen(szProperty) + 1, L':' );
        break;

    default:
        // 去掉空格
        std::remove( szProperty, szProperty + _tcslen(szProperty) + 1, L' ' );
    }

    return isOK;
}

// 基于Windows Management Instrumentation(Windows管理规范)
INT WMI_DeviceQuery( INT iQueryType, T_DEVICE_PROPERTY *properties, INT iSize )
{
    HRESULT hres;
    INT    iTotal = 0;
   
    // 判断查询类型是否支持
    if( (iQueryType < 0) || (iQueryType >= sizeof(szWQLQuery)/sizeof(T_WQL_QUERY)) )
    {
        return -1;    // 查询类型不支持
    }

    // 初始化COM
    hres = CoInitializeEx( NULL, COINIT_MULTITHREADED );
    if( FAILED(hres) )
    {
        return -2;
    }

    // 设置COM的安全认证级别
    hres = CoInitializeSecurity(
        NULL,
        -1,
        NULL,
        NULL,
        RPC_C_AUTHN_LEVEL_DEFAULT,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        EOAC_NONE,
        NULL
        );
    if( FAILED(hres) )
    {
        CoUninitialize();
        return -2;
    }
   
    // 获得WMI连接COM接口
    IWbemLocator *pLoc = NULL;
    hres = CoCreateInstance(
        CLSID_WbemLocator,            
        NULL,
        CLSCTX_INPROC_SERVER,
        IID_IWbemLocator,
        reinterpret_cast<LPVOID*>(&pLoc)
        );
    if( FAILED(hres) )
    {
        CoUninitialize();
        return -2;
    }

    // 通过连接接口连接WMI的内核对象名"ROOT//CIMV2"
    IWbemServices *pSvc = NULL;
    hres = pLoc->ConnectServer(
         _bstr_t( L"ROOT//CIMV2" ),
         NULL,
         NULL,
         NULL,
         0,
         NULL,
         NULL,
         &pSvc
         );   
    if( FAILED(hres) )
    {
        pLoc->Release();
        CoUninitialize();
        return -2;
    }

    // 设置请求代理的安全级别
    hres = CoSetProxyBlanket(
        pSvc,
        RPC_C_AUTHN_WINNT,
        RPC_C_AUTHZ_NONE,
        NULL,
        RPC_C_AUTHN_LEVEL_CALL,
        RPC_C_IMP_LEVEL_IMPERSONATE,
        NULL,
        EOAC_NONE
        );
    if( FAILED(hres) )
    {
        pSvc->Release();
        pLoc->Release();    
        CoUninitialize();
        return -2;
    }

    // 通过请求代理来向WMI发送请求
    IEnumWbemClassObject *pEnumerator = NULL;
    hres = pSvc->ExecQuery(
        bstr_t("WQL"),
        bstr_t( szWQLQuery[iQueryType].szSelect ),
        WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
        NULL,
        &pEnumerator
        );
    if( FAILED(hres) )
    {
        pSvc->Release();
        pLoc->Release();
        CoUninitialize();
        return -3;
    }

    // 循环枚举所有的结果对象 
    while( pEnumerator )
    {
        IWbemClassObject *pclsObj = NULL;
        ULONG uReturn = 0;

        if( (properties != NULL) && (iTotal >= iSize) )
        {
            break;
        }

        pEnumerator->Next(
            WBEM_INFINITE,
            1,
            &pclsObj,
            &uReturn
            );

        if( uReturn == 0 )
        {
            break;
        }

        if( properties != NULL )
        {    // 获取属性值
            VARIANT vtProperty;
           
            VariantInit( &vtProperty );   
            pclsObj->Get( szWQLQuery[iQueryType].szProperty, 0, &vtProperty, NULL, NULL );
            StringCchCopy( properties[iTotal].szProperty, PROPERTY_MAX_LEN, W2T(vtProperty.bstrVal) );
            VariantClear( &vtProperty );

            // 对属性值做进一步的处理
            if( WMI_DoWithProperty( iQueryType, properties[iTotal].szProperty, PROPERTY_MAX_LEN ) )
            {
                iTotal++;
            }
        }
        else
        {
            iTotal++;
        }

        pclsObj->Release();
    } // End While

    // 释放资源
    pEnumerator->Release();
    pSvc->Release();
    pLoc->Release();   
    CoUninitialize();

    return iTotal;
}

给我留言

留言无头像?