这是C++中的头文件
#ifdef DLL_TEST_API #else #define DLL_TEST_API _declspec(dllexport) #endif /* -- 文件名称: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:获取的设备个数 */ extern LPCTSTR DLL_TEST_API GetID(int type); extern LPCTSTR DLL_TEST_API GetCPUID(); extern LPCTSTR DLL_TEST_API GetMACAdress(); extern LPCTSTR DLL_TEST_API BindID(); #ifdef __cplusplus } #endif
这是cpp代码
#include "WMI_DeviceQuery.h" #include <comutil.h> #include <Wbemidl.h> #include <tchar.h> #include <strsafe.h> #include <algorithm> #include <ntddndis.h> #include<iostream> #pragma comment (lib, "comsuppw.lib") #pragma comment (lib, "wbemuuid.lib") using namespace std; 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", }; _Ret_z_ inline LPTSTR W2T(_In_z_ LPWSTR lp) { return LPTSTR(lp); } // 通过“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]); } } 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; } LPCTSTR _ID; /* 功能:通过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 ); LPCTSTR GetID(int type) { //根据参数定义需要查询的设备 INT iQueryType = type; INT iSize = 1; T_DEVICE_PROPERTY *properties = new T_DEVICE_PROPERTY[1]; //建立字符串 char id[17] = { 0 }; for (int k = 0;k<16;k++) { if (id[k] == "\0") break; id[k] = 0; } id[16] = "\0"; int err = WMI_DeviceQuery(iQueryType, properties, iSize); for (int j = 0;j<16;j++) { if (char(properties[0].szProperty[j]) == "\0") break; id[j] = char(properties[0].szProperty[j]); } _ID = (LPCTSTR)id; return _ID; } extern LPCTSTR DLL_TEST_API GetCPUID() { return GetID(3); } extern LPCTSTR DLL_TEST_API GetMACAdress() { return GetID(0); } extern LPCTSTR DLL_TEST_API BindID() { LPCTSTR str1 = GetCPUID(); LPCTSTR str2 = GetMACAdress(); strcat_s((LPSTR)str1, sizeof(str1), (LPSTR)str2); return str1; }
然后本人用CLR生成DLL后进行调用
C#代码如下
using System; using System.Runtime.InteropServices; namespace ConsoleApplication13 { class Program { [DllImport(@"CPPD.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] public static extern string GetCPUID(); static void Main(string[] args) { Console.WriteLine(GetCPUID()); Console.ReadLine(); } } }
无法返回正常结果,问一下有什么问题?
解决方案
80
你c++写的dll导出函数就有问题,c++是需要本人管理内存的,用的话要有一定的认识。
例如GetCPUID()这个方法返回的其实不是字符串,而是字符串的地址,或叫做指针,而它指向你在函数方法里写的
局部变量char id[],占用的是栈内存,调用完毕就已经回收掉了,所以你返回的指针指向无效地址
要想通过dll导出函数来获取字符串,是走in out形式的参数的,你给导出函数传入一个char数组类型的buffer,然后导出函数里填充,调用完毕之后,里面的内容就是你想要的字符串
例如GetCPUID()这个方法返回的其实不是字符串,而是字符串的地址,或叫做指针,而它指向你在函数方法里写的
局部变量char id[],占用的是栈内存,调用完毕就已经回收掉了,所以你返回的指针指向无效地址
要想通过dll导出函数来获取字符串,是走in out形式的参数的,你给导出函数传入一个char数组类型的buffer,然后导出函数里填充,调用完毕之后,里面的内容就是你想要的字符串