JumboWu 主页

版权声明:本文为Jumbo原创文章,采用[知识共享 署名-非商业性使用-禁止演绎 4.0 国际 许可协议],转载前请保证理解此协议 原文出处:https://www.jianshu.com/p/d3ac316104f8

C#与C++类型互转

一、C#调用DLL文件时参数对应表

Wtypes.h 中的非托管类型	非托管 C 语言类型	托管类名	说明
HANDLE	void*	System.IntPtr	32 位
BYTE	unsigned char	System.Byte	8 位
SHORT	short	System.Int16	16 位
WORD	unsigned short	System.UInt16	16 位
INT	int	System.Int32	32 位
UINT	unsigned int	System.UInt32	32 位
LONG	long	System.Int32	32 位
BOOL	long	System.Int32	32 位
DWORD	unsigned long	System.UInt32	32 位
ULONG	unsigned long	System.UInt32	32 位
CHAR	char	System.Char	用 ANSI 修饰。
LPSTR	char*	System.String 或 System.StringBuilder	用 ANSI 修饰。
LPCSTR	Const char*	System.String 或 System.StringBuilder	用 ANSI 修饰。
LPWSTR	wchar_t*	System.String 或 System.StringBuilder	用 Unicode 修饰。
LPCWSTR	Const wchar_t*	System.String 或 System.StringBuilder	用 Unicode 修饰。
FLOAT	Float	System.Single	32 位
DOUBLE	Double	System.Double	64 位

二、C#调用C++编写的DLL函数, 以及各种类型的参数传递

  1. 如果函数只有传入参数,比如:
1.	//C++中的输出函数 
2.	int __declspec(dllexport) test(const int N) 
3.	{ 
4.	return N+10; 
5.	} 
对应的C#代码为:
C# Code Copy Code To Clipboard
1.	[DllImport("test.dll", EntryPoint = "#1")] 
2.	public static extern int test(int m); 
3.	 
4.	private void button1_Click(object sender, EventArgs e) 
5.	{ 
6.	textBox1.Text= test(10).ToString(); 
7.	} 
2. 如果函数有传出参数,比如:

1.	//C++
2.	void __declspec(dllexport) test(const int N, int& Z) 
3.	{ 
4.	Z=N+10; 
5.	} 
对应的C#代码:
C# Code Copy Code To Clipboard
1.	[DllImport("test.dll", EntryPoint = "#1")] 
2.	public static extern double test(int m, ref int n); 
3.	 
4.	private void button1_Click(object sender, EventArgs e) 
5.	{ 
6.	int N = 0; 
7.	test1(10, ref N); 
8.	textBox1.Text= N.ToString(); 
9.	} 
3. 带传入数组:

1.	void __declspec(dllexport) test(const int N, const int n[], int& Z) 
2.	{ 
3.	for (int i=0; i<N; i++) 
4.	{ 
5.	Z+=n[i]; 
6.	} 
7.	} 
C#代码:

1.	[DllImport("test.dll", EntryPoint = "#1")] 
2.	public static extern double test(int N, int[] n, ref int Z); 
3.	 
4.	private void button1_Click(object sender, EventArgs e) 
5.	{ 
6.	int N = 0; 
7.	int[] n; 
8.	n = new int[10]; 
9.	for (int i = 0; i < 10; i++) 
10.	{ 
11.	n[i] = i; 
12.	} 
13.	test(n.Length, n, ref N); 
14.	textBox1.Text= N.ToString(); 
15.	} 
4. 带传出数组:
C++不能直接传出数组,只传出数组指针,

1.	void __declspec(dllexport) test(const int M, const int n[], int *N) 
2.	{ 
3.	for (int i=0; i<M; i++) 
4.	{ 
5.	N[i]=n[i]+10; 
6.	} 
7.	} 
对应的C#代码:

1.	[DllImport("test.dll", EntryPoint = "#1")] 
2.	public static extern void test(int N, int[] n, [MarshalAs(UnmanagedType.LPArray,SizeParamIndex=1)] int[] Z); 
3.	 
4.	private void button1_Click(object sender, EventArgs e) 
5.	{ 
6.	int N = 1000; 
7.	int[] n, Z; 
8.	n = new int[N];Z = new int[N]; 
9.	for (int i = 0; i < N; i++) 
10.	{ 
11.	n[i] = i; 
12.	} 
13.	test(n.Length, n, Z); 
14.	for (int i=0; i<Z.Length; i++) 
15.	{ 
16.	textBox1.AppendText(Z[i].ToString()+"n"); 
17.	} 
18.	} 

这里声明函数入口时,注意这句 [MarshalAs(UnmanagedType.LPArray,SizeParamIndex=1)] int[] Z 在C#中数组是直接使用的,而在C++中返回的是数组的指针,这句用来转化这两种不同的类型. 关于MarshalAs的参数用法以及数组的Marshaling,可以参见这篇转帖的文章: http://www.kycis.com/blog/read.php?21

  1. 传出字符数组:
C++定义:
1.	void __declspec(dllexport) test(int i, double &a, double &b, char t[5])  
C#对应声明:

1.	[DllImport("dll.dll", EntryPoint = "test")]  
2.	public static extern void test(int i, ref double a, ref double b, [Out, MarshalAs(UnmanagedType.LPArray)] char[] t);   
4.	            char[] t = new char[5];  
5.	            test(i, ref a, ref b, t);  

字符数组的传递基本与4相似,只是mashalAs 时前面加上Out。

三、 1.普通传值,如下面代码中MotionDetect的第4个参数; 2.传引用,MotionDetect的第3个参数,nNum传进动态库后赋值再传回来; 3.引用传一个结构体,MotionDetect的第2个参数,这里其实是传一个结构体的数组,具体像加ref的传参还真不会; 4.传一个有分配内存的变量,需要用到GCHandle,因为C#是可以自动回收内存的,而GCHandle在这里的作用就是把它的内存空间Pin住,传递给C++动态库后再手动回收资源。

using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;

public class D : MonoBehaviour 
{
	struct Color_32
	{
		public byte r;
		public byte g;
		public byte b;
		public byte a;
	}
	Color_32[]  imageDataResult;
	
	GCHandle pixelsHandle;
	
	[DllImport("MotionDetectionDll")]
	private static extern bool MotionDetect( System.IntPtr colors, Color_32[] imageDataResult, ref int nNum, int nChannels );
	
	void Start()
    {
		imageDataResult = new Color_32[128*128];
	}
	
	void Update () 
	{
		int nNum = 0;
		pixelsHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
		bool wfDf = MotionDetect( pixelsHandle.AddrOfPinnedObject(), imageDataResult, ref nNum, 4 );
		pixelsHandle.Free();
	}
}
C++中是这么写的
//头文件中多加个结构体定义
#include "stdafx.h"
struct Color_32
{
	byte r;
	byte g;
	byte b;
	byte a;
};
extern "C" _declspec(dllexport) bool MotionDetect ( char* imageData, Color_32 imageDataResult[], int *nNum, int nChannels );

// CPP文件中
extern "C" _declspec(dllexport) bool MotionDetect ( char* imageData, Color_32 imageDataResult[], int *nNum, int nChannels )
{
	IplImage* imgSrc = cvCreateImageHeader(cvSize(width, height), IPL_DEPTH_8U, 4);
	if(!imgSrc)
	{
		return false;
	}
	cvSetData( imgSrc, imageData, imgSrc->widthStep );
	
	......
	
	for ( int i=0; i< ......; i++ )
	{
		imageDataResult[i].r = ......;
		imageDataResult[i].g = ......;
		imageDataResult[i].b = ......;
		imageDataResult[i].a = ......;
	}
	
	......
	
	*nNum = 5;
	nChannels = 4;
}
Fork me on GitHub