C#部份
namespace testInteroperability { class MsgBoxTest { [DllImport("user32.dll")] static extern int MessageBox(IntPtr hWnd,string text, string caption,int type); public static void testMB() { MessageBox(IntPtr.Zero, "Please do not press this again.", "Attention", 0); } } }
using System; using System.Runtime.InteropServices; using System.Text; namespace testInteroperability { class testMyDLL { /* * 内容:C#调用C++写的Native DLL的示例 * 作者:kagula * 日期:20120920 * 测试环境:VS2008SP1 .NET FRAMEWORK 3.5 * * 注意: * [1]你需要使用exeScope工具查看C++的函数导出后在DLL中的入口名称 * 下面语句EntryPoint后跟的是C++函数导出后,在DLL中的入口名称 * 因为c++的重载机制,同个函数名可以有不同的参数和返回值等等,为了区别开来,所以加了@@之类 * * [2]如果你经常修改C++DLL中的函数参数列表,VC会自动生成新的引用名称很不方便 * 这时,你可以在C++项目中,[Add]->[New Item]->[Visual C++]->[Code]->[Module-Definition File(.def)] * 添加一个模块定义文件(.def),定义函数外部引用的名称。 * * [3]你必须修改你的DLL项目的DLL输出路径到C#程序的输出路径中 * 例"<Solution所在路径>\<Solution名称>\<项目名称>\bin\Debug" * * [4]C++的DLL项目同C#测试DLL的项目要放在同一个Solution中 * 勾选C#项目属性[Debug]->[Enable Debuggers]->[Enable unmanaged code debugging]选项 * 现在调试你的C#项目自动会进入到你在C++项目中下的断点。 * * * 参考资料 * [1]《Type Library Importer in Managed Code》 * http://clrinterop.codeplex.com/releases/view/17579 * [2]《How to copy a String into a struct using C#》 * http://www.codeproject.com/Articles/7357/How-to-copy-a-String-into-a-struct-using-C * [3]《C#中具体如何调用Win32函数》 * http://www.pinvoke.net * [4]《C++字符集问题终极分析(可解决乱码问题)》 * http://hi.baidu.com/xddipuauedhkpqe/item/b59ee29082aaff35336eebcc */ //测试基本调用,引用名称,为VC自动生成 [DllImport(@"MyDLL.dll", EntryPoint = "?fnMyDLL@@YAHXZ")] static extern int fnMyDLL(); public static void test_fnMyDLL() { try { int nR = fnMyDLL(); Console.WriteLine("test fnMyDLL()=" + nR); } catch (Exception e) { //找不到动态链接库会抛出异常 Console.WriteLine(e.Message); } } //测试Primitive类型参数传入,引用名称在VC++项目的DEF文件中定义 [DllImport(@"MyDLL.dll")] static extern void fnHaveMorePara(byte c,byte uc, short s,ushort us, int n,uint un, int l,uint ul, Int64 ll,Int64 ull, float f,double d, [MarshalAs(UnmanagedType.LPTStr)] string ss, [MarshalAs(UnmanagedType.LPWStr)] string ws); public static void test_fnHaveMorePara() { try { fnHaveMorePara(1,2,3,4,5,6,7,8, 9L,10L,11.0f,12.0, "ANSI String", "test_fnHaveMorePara[来自C#的字符串]"); } catch (Exception e) { //找不到动态链接库会抛出异常 Console.WriteLine(e.Message); } } //测试Primitive类型参数传出,引用名称在VC++项目的DEF文件中定义 /* * [1]测试传入字节数组 * [2]测试取字符串结果 * [3]测试取int结果(其它原始数据类型的传出同int) */ [DllImport(@"MyDLL.dll")] static extern void fnHaveMorePara2(IntPtr pData, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pWStr, ref int n); unsafe public static void test_fnHaveMorePara2() { byte[] data = new byte[2]; data[0] = 1; data[1] = 2; fixed (byte* p = data) { StringBuilder sb = new StringBuilder(255); int n = 0; fnHaveMorePara2((IntPtr)p, sb, ref n); Console.WriteLine("1+2=" + n +" sb="+sb.ToString()); } } //测试结构数据的传入传出 //C#负责分配内存,C++(DLL)负责往内存里填数据 /* * Struct中的字符串成员转递给C++DLL用String类型, * [1]从C++DLL返回得用StringBuilder类型,问题是Struct里不能有StringBuilder类型的字符串类型。 * 所要要返回struct里的String类型的东东,struct里必须是String类型,[2]而且外部函数声明时 * 必须用ref。如果用out语法,传到C++里的struct里的String对象成员为空指针。 * out 后面跟的结构里的成员只能是原始数据类型。 */ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct StudentInfo { public int id; [MarshalAs(UnmanagedType.LPWStr,SizeConst=256)] public String name; public int age; } [DllImport(@"MyDLL.dll")] static extern void fnHaveMorePara3(StudentInfo siIn,ref StudentInfo siOut, ref StudentInfo siOut2); public static void test_fnHaveMorePara3() { StudentInfo si = new StudentInfo(); StudentInfo siOut2 = new StudentInfo(); si.id = 12; si.name = "来自C#的字符串!"; si.age = 13; //分配足够长度的内存,让C++程序能够往里填要返回的字符串。 //若C++程序地址越界(超出C#所分配实际内存长度),C#程序会抛出OutOfMemory异常。 siOut2.name = new String('\0',256); fnHaveMorePara3(si, ref si, ref siOut2); Console.WriteLine("id="+si.id); Console.WriteLine("name=" + si.name); Console.WriteLine("age=" + si.age); Console.WriteLine("siOut2 id=" + siOut2.id); Console.WriteLine("siOut2 name=" + siOut2.name); Console.WriteLine("siOut2 age=" + siOut2.age); } //测试union结构参数的传入传出 //暂时不会用到,故省略 //测试2D数组的传递 [DllImport(@"MyDLL.dll")] static extern void AddMatrix4x4(int [,] s1,int [,] s2,ref int[,] d); public static void test_fnHaveMorePara4() { int[,] s1 = new int[4,4]; int[,] s2 = new int[4,4]; int[,] d = new int[4, 4]; for(int i=0;i<4;i++) for (int j = 0; j < 4; j++) { s1[i,j] = i; s2[i, j] = j; d[i, j] = i * j; } AddMatrix4x4(s1, s2, ref d); Console.WriteLine("AddMatrix4x4 D[3][3]= >" + d[3,3]); } //测试C++对C#的回调 delegate bool FromDLLCallBack(int r); [DllImport(@"MyDLL.dll")] static extern void Add(FromDLLCallBack func, int s1,int s2); static bool _FromDLLCallBack(int r) { Console.WriteLine("_FromDLLCallBack =>" + r); return false; } public static void test_fnHaveMorePara5() { Add(_FromDLLCallBack, 1, 2); } } }
C++部份,模块定义文件
LIBRARY "MyDLL" EXPORTS fnHaveMorePara @2 fnHaveMorePara2 @3 fnHaveMorePara3 @4 AddMatrix4x4 @5 Add @6
C++部份,头文件
#ifdef MYDLL_EXPORTS #define MYDLL_API __declspec(dllexport) #else #define MYDLL_API __declspec(dllimport) #endif //测试简单函数的调用 MYDLL_API int fnMyDLL(void); //测试原始数据类型参数列表的传入 MYDLL_API void fnHaveMorePara(char c,unsigned char uc, short s,unsigned short us, int n,unsigned int un, long l,unsigned long ul, long long ll,unsigned long long ull, float f,double d, char* pStr,wchar_t* pWStr); //测试原始数据类型参数的返回 /* [in] pData 字节型数组指针 pWStr 要返回字符串指针 [out] pWStr,n n pData数组第一个单元的值 + pData数组第二个单元的值 */ MYDLL_API void fnHaveMorePara2(void* pData,wchar_t* pWStr,int* n); //测试数据结构对象的传入传出 typedef struct _StudentInfo { int id; wchar_t* name; int age; } StudentInfo; MYDLL_API void fnHaveMorePara3(StudentInfo siIn,StudentInfo* siOut, StudentInfo* siOut2); /* 功能:四阶矩阵加法 目的:二维数组传递 备注:int s1[4][4]可以声明为int *s1, 这时对s1二维数组进行引用可以采用s1[i*4+j]形式,i、j的定义域为[0,3] */ MYDLL_API void AddMatrix4x4(int s1[4][4],int s2[4][4],int **_d); //测试回调功能 typedef BOOL (CALLBACK *FromDLLCallBack) (int r); MYDLL_API void Add(FromDLLCallBack func,int a,int b);
C++部份,源文件
// MyDLL.cpp : Defines the exported functions for the DLL application. // #include "stdafx.h" #include "MyDLL.h" #include "stdio.h" #include <string.h> #include <iostream> /* 环境:VS2008SP1 项目设置:新建DLL项目时,勾选[Additional options]的[Export Symbols]选项 */ MYDLL_API int fnMyDLL(void) { return 42; } MYDLL_API void fnHaveMorePara(char c,unsigned char uc, short s,unsigned short us, int n,unsigned int un, long l,unsigned long ul, long long ll,unsigned long long ull, float f,double d, char* pStr,wchar_t* pWStr) { printf("[char,unsigned char]=>%c,%c\n",c,uc); printf("[short,unsigned short]=>%d,%d\n",s,us); printf("[int,unsigned int]=>%d,%d\n",n,un); printf("[long,unsigned long]=>%d,%d\n",l,ul); printf("[long long,unsigned long long]=>%I64d,%I64d\n",ll,ull); printf("[float,double]=>%d,%d\n",c,uc); printf("[char *]=>%s\n",pStr); //打印中文会乱码,还需要做字符集编码转换,但这不是本文的重点,故忽视。 wprintf(L"[wchar_t *]=>%s\n",pWStr); } MYDLL_API void fnHaveMorePara2(void* pData,wchar_t* pWStr,int* n) { if(pData!=NULL) { char *pC = (char *)pData; std::wstring ws=L"Unicode String[来自DLL的字符串]"; *n = (int)(pC[0] + pC[1]); wcsncpy(pWStr,ws.c_str(),255); } } /* 功能: 目的:结构对象的传递 备注: */ MYDLL_API void fnHaveMorePara3(StudentInfo siIn,StudentInfo* siOut,StudentInfo* siOut2) { printf("SI(1)->id=%d\n",siIn.id); wprintf(L"SI(1)->name=%s\n",siIn.name); printf("SI(1)->age=%d\n",siIn.age); siOut->id=21; //不能对siOut->name操作,因为没有分配足够长度的内存 siOut->age=22; siOut2->id = 31; //C#对应的是String类型所以不能给wchar_t*类型对象赋值, //否则在C#里引用这个对象会抛出OutOfMemeoryException。 if(siOut2->name!=NULL) wcsncpy(siOut2->name,L"来自C++DLL的名字",255); siOut2->age = 32; } MYDLL_API void AddMatrix4x4(int s1[4][4],int s2[4][4],int **d) { for(int i=0;i<4;i++) { for(int j=0;j<4;j++) { (*d)[i*4+j] = s1[i][j] + s2[i][j]; } } printf("d[3][3]=%d\n",(*d)[15]); } MYDLL_API void Add(FromDLLCallBack func,int a,int b) { BOOL bR = (*func)(a+b); if(bR==1) printf("C#返回True!\n"); else if(bR==0) printf("C#返回False!\n"); }