C#有时需要调用C++等语言编写发布的DLL,这就涉及到一个问题,如何将将非托管内存指针中的数据复制到托管变量中来?微软提供了Marshal.Copy等方法,以实现类似的需求。
1)将非托管内存指针中的数据复制到托管 32 位有符号整数数组。
public static void Copy (
IntPtr source,
int[] destination,
int startIndex,
int length
)
2)
将数据从非托管内存块封送到新分配的指定类型的托管对象
C#调用C++ DLL(返回结构数组指针)
C++代码:
typedef struct TRANSACTION
{
char account[19];
int maxNum;
char startDate[16];
char endDate[16];
} TRANSACTION;
typedef struct QUERY
{
int id;
int itemAmount;
int gold;
int silver;
char itemName[30];
}QUERY;
extern "C " _declspec(dllexport) QUERY* QueryTransactionHistory(DWORD dwIP,int nPort,TRANSACTION * pTran, int* outNum,int* nErrorCode) {
cout << "dwIP = " << dwIP << ", nPort = " << nPort << ", pTran -> maxNum = " << pTran -> maxNum << endl;
cout << "pTran -> account = " << pTran -> account << ", pTran -> startDate = " << pTran -> startDate << ", pTran -> endDate = " << pTran -> endDate << endl;
QUERY* ret = (QUERY* )malloc(sizeof(QUERY) * 2);
ret[0].id = 0;
ret[0].gold = ret[0].silver = ret[0].itemAmount = 1;
strcpy(ret[0].itemName, "Item0 ");
ret[1].id = 1;
ret[1].gold = ret[1].silver = ret[1].itemAmount = 2;
strcpy(ret[1].itemName, "Item1 ");
(*outNum) = 2;
(*nErrorCode) = 0;
return ret;
}
C#代码:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public class TRANSACTION
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 19)]
public string account;
public int maxNum;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string startDate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string endDate;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class QUERY
{
public int id;
public int itemAmount;
public int gold;
public int silver;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 30)]
public string itemName;
}
class Program
{
[DllImport( "CPPDLL.DLL ", CallingConvention = CallingConvention.StdCall)]
public extern static IntPtr QueryTransactionHistory(uint swIP, int nPort, TRANSACTION pTran, out int cnt, out int nErrCode);
static void Main(string[] args)
{
int cnt, errCode;
TRANSACTION tran = new TRANSACTION();
tran.account = "account ";
tran.endDate = "endDate ";
tran.startDate = "startDate ";
tran.maxNum = 50;
//get retrun value as IntPtr
IntPtr ptr = QueryTransactionHistory(0, 0, tran, out cnt, out errCode);
Console.WriteLine( "============ CS =============== ");
Console.WriteLine( "Count = {0}, errCode = {1} ", cnt, errCode);
IntPtr head = ptr;
for (int i = 0; i < cnt; ++i)
{
//extract struct from pointer
QUERY q = (QUERY)Marshal.PtrToStructure(ptr, typeof(QUERY));
Console.WriteLine( "id = {0}, gold = {1}, silver = {2}, itemAmount = {3}, itemName = {4} ",
q.id, q.gold, q.silver, q.itemAmount, q.itemName);
//move pointer by sizeof(QUERY) to the next one
//WARNING: assume 32 bit machine!!!!!!
ptr = (IntPtr)(ptr.ToInt32() + Marshal.SizeOf(typeof(QUERY)));
}
//TODO: need to free memory
}
}
将非托管内存指针中的数据复制到托管 32 位有符号整数数组。
public static void Copy (
IntPtr source,
int[] destination,
int startIndex,
int length
)