一 结构体/数组作为返回值
1. Called in C#
c++ codes:
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# source codes:
1. 因为返回的数组是变长的,所以DllImport函数声明不能用数组作返回类型。
必须使用IntPtr指针做返回值,然后用Marshal.PtrToStructure读取结构内容。
2. 如果你在C代码中用malloc申请的返回内存,那么记得提供一个释放该内存的C函数,否则会有内存泄漏。如果用的事WindowsAPI,可以再C#中用Marshal.FreeHGlobal释放。
下面是代码:
[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
}
}
二 结构体/数组作为参数
1 Python
Python调用DLL,如何使用结构体数组指针做参数
C++函数原型
typedef struct { unsigned long DeviceType; int Handle; int NumberOfClients; int SerialNumber; int MaxAllowedClients; }NeoDevice; int _stdcall icsneoFindNeoDevices(unsigned long DeviceTypes, NeoDevice *pNeoDevices, int *pNumberOfDevices);
Physon codes:
class NeoDevice(Structure):
_fields_ = [("DeviceType",c_ulong),
("Handle",c_int),
("NumberOfClients",c_int),
("SerialNumber",c_int),
("MaxAllowedClients",c_int)]
class cNeoVICan(CCanBase):
def __init__(self):
neoVi = windll.icsneo40
self.icsneoFindNeoDevices = neoVi.icsneoFindNeoDevices
if __name__ == "__main__":
canBus = cNeoVICan()
print canBus.icsneoGetDLLVersion()
iNumberOfDevices = (NeoDevice * 10)()
num = c_int()
iResult = canBus.icsneoFindNeoDevices(c_ulong(65535), cast(iNumberOfDevices, POINT(NeoDevice)), byref(num))
2 VB
数值型数组在VB中其数据是连续存放的,相当于一维的,而在C/C++中数组可以等价于指向数组第1个元素的指针。可以用引用的方式把VB中数组的第1个元素的地址传给VC编写的DLL,在DLL中用一个指针来接收,这样就实现了VB到Dll在中数组的传递。从DLL传递数组给VB方法相同,过程相反.
如果是二维数组,则把二维数组按照一维数组的方式传入传出,只是在使用的时候,顺便把二维数组的行和列数传递即可。
总体思想是这样的。下面看例子。
VC中:
double _stdcall OneDimensionArrayTest(double *inArr,int nCount,double *outArr,int* ioutArrCount) //一维数组的传入传出
{
int iNum=nCount;
double *dRes=new double[iNum];
int i;
for(i=0;i<nCount;i++)
{
dRes[i]=inArr[i]*2;
}
for(i=0;i<nCount;i++)
{
outArr[i]=dRes[i];
}
*ioutArrCount=iNum;
return dRes[0];
delete []dRes;
}
void _stdcall TwoDimensionArrayTest(double *inArr,int nRows,int nCols,double *outArr,int* outRows,int *outCols)//二维数组的传入传出
{
double *dRes=new double[nRows*nCols];
int i;
int j;
for(i=0;i<nRows;i++)
{
for(j=0;j<nCols;j++)
{
dRes[nCols*i+j]=inArr[nCols*i+j]*2;
}
}
for(i=0;i<nRows;i++)
{
for(j=0;j<nCols;j++)
{
outArr[nCols*i+j]=inArr[nCols*i+j]*2;
}
}
*outRows=nRows;
*outCols=nCols;
delete [] dRes;
}
LIBRARY "TestDll"
EXPORTS
; 此处可以是显式导出
Add @1
darray @2
OneDimensionArrayTest @3
TwoDimensionArrayTest @4
VB中
Declare Function OneDimensionArrayTest Lib "D:\在编程序\Dll\VBLoadDll\TestDll.dll" (ByRef inputer As Double, ByVal inLength As Long, ByRef output As Double, ByRef outLength As Long) As Double
Declare Function TwoDimensionArrayTest Lib "D:\在编程序\Dll\VBLoadDll\TestDll.dll" (ByRef inputer As Double, ByVal inRows As Long, ByVal inCols As Long, ByRef outputer As Double, ByRef outRows As Long, ByRef outCols As Long)
Private Sub cmdTest2_Click()
Dim inputer(8) As Double
Dim out(9) As Double
Dim res As Double
Dim m As Long
inputer(0) = 1.2
inputer(1) = 2.3
inputer(2) = 1
res = OneDimensionArrayTest(inputer(0), 9, out(0), m)
MsgBox CStr(m), vbOKOnly, "一维数组的元素个数"
'MsgBox CStr(res)
Dim str As String
Dim i As Integer
For i = 0 To UBound(out)
str = str + " " + CStr(out(i))
Next
MsgBox str, vbOKOnly, "一维数组的元素"
End Sub
Private Sub cmdTest3_Click()
Dim iRows As Integer
Dim iCols As Integer
iRows = 3
iCols = 4
Dim inputer() As Double
ReDim inputer(iRows, iCols)
Dim outputer() As Double
ReDim outputer(iRows, iCols)
Dim oRows As Long
Dim oCols As Long
Dim i, j As Integer
For i = 0 To UBound(inputer, 1)
For j = 0 To UBound(inputer, 2)
inputer(i, j) = (i + 1) * (j + 1)
Next
Next
Dim str As String
For i = 0 To UBound(inputer, 1)
For j = 0 To UBound(inputer, 2)
str = str + " " + CStr(inputer(i, j))
Next
str = str + vbCrLf
Next
MsgBox str, vbOKOnly, "inputer,二维数组的输入"
Call TwoDimensionArrayTest(inputer(0, 0), iRows + 1, iCols + 1, outputer(0, 0), oRows, oCols)
str = ""
'
For i = 0 To UBound(outputer, 1)
For j = 0 To UBound(outputer, 2)
str = str + " " + CStr(outputer(i, j))
Next
str = str + vbCrLf
Next
MsgBox str, vbOKOnly, "outputer,二维数组的输出"
End Sub