Code Bye

C#调用C++的DLL无法返回正常结果

这是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,然后导出函数里填充,调用完毕之后,里面的内容就是你想要的字符串

CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明C#调用C++的DLL无法返回正常结果