Net Core教程

C#调用C++写的dll

本文主要是介绍C#调用C++写的dll,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
C#调用C++的DLL搜集整理的所有数据类型转换方式(转)   2011-04-01 13:49:13|  分类: C++,C#|举报|字号 订阅         //C++中的DLL函数原型为         //extern "C" __declspec(dllexport) bool 方法名一(const char* 变量名1, unsigned char* 变量名2)         //extern "C" __declspec(dllexport) bool 方法名二(const unsigned char* 变量名1, char* 变量名2)         //C#调用C++的DLL搜集整理的所有数据类型转换方式,可能会有重复或者多种方案,自己多测试         //c++:HANDLE(void   *)          ----    c#:System.IntPtr          //c++:Byte(unsigned   char)     ----    c#:System.Byte          //c++:SHORT(short)              ----    c#:System.Int16          //c++:WORD(unsigned   short)    ----    c#:System.UInt16          //c++:INT(int)                  ----    c#:System.Int16         //c++:INT(int)                  ----    c#:System.Int32          //c++:UINT(unsigned   int)      ----    c#:System.UInt16         //c++:UINT(unsigned   int)      ----    c#:System.UInt32         //c++:LONG(long)                ----    c#:System.Int32          //c++:ULONG(unsigned   long)    ----    c#:System.UInt32          //c++:DWORD(unsigned   long)    ----    c#:System.UInt32          //c++:DECIMAL                   ----    c#:System.Decimal          //c++:BOOL(long)                ----    c#:System.Boolean          //c++:CHAR(char)                ----    c#:System.Char          //c++:LPSTR(char   *)           ----    c#:System.String          //c++:LPWSTR(wchar_t   *)       ----    c#:System.String          //c++:LPCSTR(const   char   *)  ----    c#:System.String          //c++:LPCWSTR(const   wchar_t   *)      ----    c#:System.String          //c++:PCAHR(char   *)   ----    c#:System.String          //c++:BSTR              ----    c#:System.String          //c++:FLOAT(float)      ----    c#:System.Single          //c++:DOUBLE(double)    ----    c#:System.Double          //c++:VARIANT           ----    c#:System.Object          //c++:PBYTE(byte   *)   ----    c#:System.Byte[]         //c++:BSTR      ----    c#:StringBuilder         //c++:LPCTSTR   ----    c#:StringBuilder         //c++:LPCTSTR   ----    c#:string         //c++:LPTSTR    ----    c#:[MarshalAs(UnmanagedType.LPTStr)] string          //c++:LPTSTR 输出变量名    ----    c#:StringBuilder 输出变量名         //c++:LPCWSTR   ----    c#:IntPtr         //c++:BOOL      ----    c#:bool            //c++:HMODULE   ----    c#:IntPtr             //c++:HINSTANCE ----    c#:IntPtr          //c++:结构体    ----    c#:public struct 结构体{};          //c++:结构体 **变量名   ----    c#:out 变量名   //C#中提前申明一个结构体实例化后的变量名         //c++:结构体 &变量名    ----    c#:ref 结构体 变量名                 //c++:WORD      ----    c#:ushort         //c++:DWORD     ----    c#:uint         //c++:DWORD     ----    c#:int         //c++:UCHAR     ----    c#:int         //c++:UCHAR     ----    c#:byte         //c++:UCHAR*    ----    c#:string         //c++:UCHAR*    ----    c#:IntPtr         //c++:GUID      ----    c#:Guid         //c++:Handle    ----    c#:IntPtr         //c++:HWND      ----    c#:IntPtr         //c++:DWORD     ----    c#:int         //c++:COLORREF  ----    c#:uint         //c++:unsigned char     ----    c#:byte         //c++:unsigned char *   ----    c#:ref byte         //c++:unsigned char *   ----    c#:[MarshalAs(UnmanagedType.LPArray)] byte[]         //c++:unsigned char *   ----    c#:[MarshalAs(UnmanagedType.LPArray)] Intptr         //c++:unsigned char &   ----    c#:ref byte         //c++:unsigned char 变量名      ----    c#:byte 变量名         //c++:unsigned short 变量名     ----    c#:ushort 变量名         //c++:unsigned int 变量名       ----    c#:uint 变量名         //c++:unsigned long 变量名      ----    c#:ulong 变量名         //c++:char 变量名       ----    c#:byte 变量名   //C++中一个字符用一个字节表示,C#中一个字符用两个字节表示         //c++:char 数组名[数组大小]     ----    c#:MarshalAs(UnmanagedType.ByValTStr, SizeConst = 数组大小)]        public string 数组名; ushort         //c++:char *            ----    c#:string       //传入参数         //c++:char *            ----    c#:StringBuilder//传出参数         //c++:char *变量名      ----    c#:ref string 变量名         //c++:char *输入变量名  ----    c#:string 输入变量名         //c++:char *输出变量名  ----    c#:[MarshalAs(UnmanagedType.LPStr)] StringBuilder 输出变量名         //c++:char **           ----    c#:string         //c++:char **变量名     ----    c#:ref string 变量名         //c++:const char *      ----    c#:string         //c++:char[]            ----    c#:string         //c++:char 变量名[数组大小]     ----    c#:[MarshalAs(UnmanagedType.ByValTStr,SizeConst=数组大小)] public string 变量名;         //c++:struct 结构体名 *变量名   ----    c#:ref 结构体名 变量名         //c++:委托 变量名   ----    c#:委托 变量名         //c++:int       ----    c#:int         //c++:int       ----    c#:ref int         //c++:int &     ----    c#:ref int         //c++:int *     ----    c#:ref int      //C#中调用前需定义int 变量名 = 0;         //c++:*int      ----    c#:IntPtr         //c++:int32 PIPTR *     ----    c#:int32[]         //c++:float PIPTR *     ----    c#:float[]                //c++:double** 数组名          ----    c#:ref double 数组名         //c++:double*[] 数组名          ----    c#:ref double 数组名         //c++:long          ----    c#:int         //c++:ulong         ----    c#:int                  //c++:UINT8 *       ----    c#:ref byte       //C#中调用前需定义byte 变量名 = new byte();                 //c++:handle    ----    c#:IntPtr         //c++:hwnd      ----    c#:IntPtr                           //c++:void *    ----    c#:IntPtr                 //c++:void * user_obj_param    ----    c#:IntPtr user_obj_param         //c++:void * 对象名称    ----    c#:([MarshalAs(UnmanagedType.AsAny)]Object 对象名称                    //c++:char, INT8, SBYTE, CHAR                               ----    c#:System.SByte           //c++:short, short int, INT16, SHORT                        ----    c#:System.Int16           //c++:int, long, long int, INT32, LONG32, BOOL , INT        ----    c#:System.Int32           //c++:__int64, INT64, LONGLONG                              ----    c#:System.Int64           //c++:unsigned char, UINT8, UCHAR , BYTE                    ----    c#:System.Byte           //c++:unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR , __wchar_t             ----    c#:System.UInt16           //c++:unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT      ----    c#:System.UInt32           //c++:unsigned __int64, UINT64, DWORDLONG, ULONGLONG                            ----    c#:System.UInt64           //c++:float, FLOAT                                                              ----    c#:System.Single           //c++:double, long double, DOUBLE                                               ----    c#:System.Double         //Win32 Types        ----  CLR Type                  //Struct需要在C#里重新定义一个Struct         //CallBack回调函数需要封装在一个委托里,delegate static extern int FunCallBack(string str);         //unsigned char** ppImage替换成IntPtr ppImage         //int& nWidth替换成ref int nWidth         //int*, int&, 则都可用 ref int 对应         //双针指类型参数,可以用 ref IntPtr         //函数指针使用c++: typedef double (*fun_type1)(double); 对应 c#:public delegate double  fun_type1(double);         //char* 的操作c++: char*; 对应 c#:StringBuilder;         //c#中使用指针:在需要使用指针的地方 加 unsafe         //unsigned   char对应public   byte         /*          * typedef void (*CALLBACKFUN1W)(wchar_t*, void* pArg);          * typedef void (*CALLBACKFUN1A)(char*, void* pArg);          * bool BIOPRINT_SENSOR_API dllFun1(CALLBACKFUN1 pCallbackFun1, void* pArg);          * 调用方式为          * [UnmanagedFunctionPointer(CallingConvention.Cdecl)]          * public delegate void CallbackFunc1([MarshalAs(UnmanagedType.LPWStr)] StringBuilder strName, IntPtr pArg);          *           *           */               方法有不少,这里记录其中的一个方法。编译、调用通过了的。期间遇到的问题是C#调用时传递string类型的参数和返回值会报错。TargetInvocationException异常,值不在范围内,内存不可访问等等。 解决方法是;在c++的DLL中将string类型的参数返回值改为LPTSTR类型。在C#程序中使用StringBuilder类型来传入参数和接受返回值。 下面是代码: using System; using System.Collections.Generic; using System.Linq; using System.Text; //1. 打开项目“Tzb”,打开类视图,右击“Tzb”,选择“添加”-->“类”,类名设置为“dld”, //即dynamic loading dll 的每个单词的开头字母。 //2. 添加所需的命名空间及声明参数传递方式枚举: using System.Runtime.InteropServices; // 用 DllImport 需用此 命名空间 using System.Reflection; // 使用 Assembly 类需用此 命名空间 using System.Reflection.Emit; // 使用 ILGenerator 需用此 命名空间 namespace WpfApplication1 { //在“public class dld”上面添加如下代码声明参数传递方式枚举: /// /// 参数传递方式枚举 ,ByValue 表示值传递 ,ByRef 表示址传递 /// public enum ModePass { ByValue = 0x0001, ByRef = 0x0002 } public class DLD { //3. 声明LoadLibrary、GetProcAddress、FreeLibrary及私有变量hModule和farProc: /// /// 原型是 :HMODULE LoadLibrary(LPCTSTR lpFileName); /// /// < param name="lpFileName" / >DLL 文件名 /// 函数库模块的句柄 [DllImport("kernel32.dll")] static extern IntPtr LoadLibrary(string lpFileName); /// /// 原型是 : FARPROC GetProcAddress(HMODULE hModule, LPCWSTR lpProcName); /// /// < param name="hModule" / > 包含需调用函数的函数库模块的句柄 /// < param name="lpProcName" / > 调用函数的名称 /// 函数指针 [DllImport("kernel32.dll")] static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); /// /// 原型是 : BOOL FreeLibrary(HMODULE hModule); /// /// < param name="hModule" / > 需释放的函数库模块的句柄 /// 是否已释放指定的 Dll [DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)] static extern bool FreeLibrary(IntPtr hModule); /// /// Loadlibrary 返回的函数库模块的句柄 /// private IntPtr hModule = IntPtr.Zero; /// /// GetProcAddress 返回的函数指针 /// public IntPtr farProc = IntPtr.Zero; //4. 添加LoadDll方法,并为了调用时方便,重载了这个方法: /// /// 装载 Dll /// /// < param name="lpFileName" / >DLL 文件名 public void LoadDll(string lpFileName) { hModule = LoadLibrary(lpFileName); if (hModule == IntPtr.Zero) throw (new Exception(" 没有找到 :" + lpFileName + ".")); } // 若已有已装载Dll的句柄,可以使用LoadDll方法的第二个版本: public void LoadDll(IntPtr HMODULE) { if (HMODULE == IntPtr.Zero) throw (new Exception(" 所传入的函数库模块的句柄 HMODULE 为空 .")); hModule = HMODULE; } //5. 添加LoadFun方法,并为了调用时方便,也重载了这个方法,方法的具体代码及注释如下: /// /// 获得函数指针 /// /// < param name="lpProcName" / > 调用函数的名称 public void LoadFun(string lpProcName) { // 若函数库模块的句柄为空,则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")); // 取得函数指针 farProc = GetProcAddress(hModule, lpProcName); // 若函数指针,则抛出异常 if (farProc == IntPtr.Zero) throw (new Exception(" 没有找到 : " + lpProcName + " 这个函数的入口点 ")); } /// /// 获得函数指针 /// /// < param name="lpFileName" / > 包含需调用函数的 DLL 文件名 /// < param name="lpProcName" / > 调用函数的名称 public void LoadFun(string lpFileName, string lpProcName) { // 取得函数库模块的句柄 hModule = LoadLibrary(lpFileName); // 若函数库模块的句柄为空,则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 没有找到 :" + lpFileName + ".")); // 取得函数指针 farProc = GetProcAddress(hModule, lpProcName); // 若函数指针,则抛出异常 if (farProc == IntPtr.Zero) throw (new Exception(" 没有找到 :" + lpProcName + " 这个函数的入口点 ")); } //6. 添加UnLoadDll及Invoke方法,Invoke方法也进行了重载: /// /// 卸载 Dll /// public void UnLoadDll() { FreeLibrary(hModule); hModule = IntPtr.Zero; farProc = IntPtr.Zero; } /// /// 调用所设定的函数 /// /// < param name="ObjArray_Parameter" / > 实参 /// < param name="TypeArray_ParameterType" / > 实参类型 /// < param name="ModePassArray_Parameter" / > 实参传送方式 /// < param name="Type_Return" / > 返回类型 /// 返回所调用函数的 object public object Invoke(object[] ObjArray_Parameter, Type[] TypeArray_ParameterType, ModePass[] ModePassArray_Parameter, Type Type_Return) { // 下面 3 个 if 是进行安全检查 , 若不能通过 , 则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")); if (farProc == IntPtr.Zero) throw (new Exception(" 函数指针为空 , 请确保已进行 LoadFun 操作 !")); if (ObjArray_Parameter.Length != ModePassArray_Parameter.Length) throw (new Exception(" 参数个数及其传递方式的个数不匹配 .")); // 下面是创建 MyAssemblyName 对象并设置其 Name 属性 AssemblyName MyAssemblyName = new AssemblyName(); MyAssemblyName.Name = "InvokeFun"; // 生成单模块配件 AssemblyBuilder MyAssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly( MyAssemblyName, AssemblyBuilderAccess.Run); ModuleBuilder MyModuleBuilder = MyAssemblyBuilder.DefineDynamicModule("InvokeDll"); // 定义要调用的方法 , 方法名为“ MyFun ”,返回类型是“ Type_Return ” //参数类型是“ TypeArray_ParameterType ” MethodBuilder MyMethodBuilder = MyModuleBuilder.DefineGlobalMethod( "Init", MethodAttributes.Public | MethodAttributes.Static, Type_Return, TypeArray_ParameterType); // 获取一个 ILGenerator ,用于发送所需的 IL ILGenerator IL = MyMethodBuilder.GetILGenerator(); int i; for (i = 0; i < ObjArray_Parameter.Length; i++) {// 用循环将参数依次压入堆栈 switch (ModePassArray_Parameter[i]) { case ModePass.ByValue: IL.Emit(OpCodes.Ldarg, i); break; case ModePass.ByRef: IL.Emit(OpCodes.Ldarga, i); break; default: throw (new Exception(" 第 " + (i + 1).ToString() + " 个参数没有给定正确的传递方式 .")); } } if (IntPtr.Size == 4) {// 判断处理器类型 IL.Emit(OpCodes.Ldc_I4, farProc.ToInt32()); } else if (IntPtr.Size == 8 ) { IL.Emit(OpCodes.Ldc_I8, farProc.ToInt64()); } else { throw new PlatformNotSupportedException(); } IL.EmitCalli(OpCodes.Calli, CallingConvention.StdCall, Type_Return, TypeArray_ParameterType); IL.Emit(OpCodes.Ret); // 返回值 MyModuleBuilder.CreateGlobalFunctions(); // 取得方法信息 MethodInfo MyMethodInfo = MyModuleBuilder.GetMethod("Init"); return MyMethodInfo.Invoke(null, ObjArray_Parameter);// 调用方法,并返回其值 } //Invoke方法的第二个版本,它是调用了第一个版本的: /// /// 调用所设定的函数 /// /// < param name="IntPtr_Function" / > 函数指针 /// < param name="ObjArray_Parameter" / > 实参 /// < param name="TypeArray_ParameterType" / > 实参类型 /// < param name="ModePassArray_Parameter" / > 实参传送方式 /// < param name="Type_Return" / > 返回类型 /// 返回所调用函数的 object public object Invoke(IntPtr IntPtr_Function, object[] ObjArray_Parameter, Type[] TypeArray_ParameterType, ModePass[] ModePassArray_Parameter, Type Type_Return) { // 下面 2 个 if 是进行安全检查 , 若不能通过 , 则抛出异常 if (hModule == IntPtr.Zero) throw (new Exception(" 函数库模块的句柄为空 , 请确保已进行 LoadDll 操作 !")); if (IntPtr_Function == IntPtr.Zero) throw (new Exception(" 函数指针 IntPtr_Function 为空 !")); farProc = IntPtr_Function; return Invoke(ObjArray_Parameter, TypeArray_ParameterType, ModePassArray_Parameter, Type_Return); } } } /*******调用方法******/ private void button1_Click(object sender, RoutedEventArgs e) { DLD newDLL = new DLD(); newDLL.LoadFun("E:\\workspaces\\WpfApplication1\\Debug\\DLL.dll", "Init"); StringBuilder MyStringBuilder = new StringBuilder("Hello World!"); object[] obj = new object[] { MyStringBuilder }; Type[] ty = new Type[] { typeof(StringBuilder) }; ModePass[] mode = new ModePass[] { ModePass.ByValue }; Type Type_Return = typeof(StringBuilder); StringBuilder j = (StringBuilder)newDLL.Invoke(obj, ty, mode, Type_Return); } /********c++DLL中的函数*******/ extern "C" __declspec(dllexport) LPTSTR Init(LPTSTR a); LPTSTR Init(LPTSTR a) { strcat((char *)a, "added"); return a; }   c#如何调用c++的类 (2009-07-14 03:30:16) 0 转载▼
标签: 杂谈 分类: 编程技术
将c++类生成一个 .dll 文件 //-------------头文件 #pragma once   class testclass { public: testclass(void); ~testclass(void); public: WCHAR* GetName(void); }; //-------------CPP #include "StdAfx.h" #include "testclass.h"   testclass::testclass(void) { }   testclass::~testclass(void) { } WCHAR* testclass::GetName(void) { return NULL; }   //--------------接口头文件 #pragma once   #include "testclass.h"   class Insertface { public: Insertface(void); ~Insertface(void); public: void* CreateTextClass(void); WCHAR* GetName(void *textClass); BOOL DeleteTextClass(void *textClass); }; //----------------------CPP #include "StdAfx.h" #include "Insertface.h"   Insertface::Insertface(void) { }   Insertface::~Insertface(void) { } extern "C" _declspec(dllexport) void* Insertface::CreateTextClass(void) { testclass *p = new testclass(); return (void *)p; } extern "C" _declspec(dllexport) WCHAR* Insertface::GetName(void *textClass) { return ((testclass *)textClass)->GetName(); } extern "C" _declspec(dllexport) BOOL Insertface::DeleteTextClass(void *textClass) { delete (testclass *)textClass; return TRUE; }       C++的优势在于高效灵活,C#的优势在于简单易用,两者结合起来对项目开发来说是件好事,而且C++不容易反编译,也保障了代码的安全性,如果一些核心算法使用C#编写,那么保密就是一个问题。     C++生成的DLL一般只提供函数接口,不能直接用C#调用C++写的类,这样非常不方便,于是经过半天的反复测试,终于确定了一套可行的方案,下面就是整个流程的一个范例。   (1)编写C++类Mask,类前面的extern "C" class _declspec(dllexport)这么一串修饰符是必备的,不然无法导出类。   [cpp] view plaincopyprint?
  1. //Mask类头文件mask.h  
  2. #pragma once  
  3.    
  4. extern "C" class _declspec(dllexport) Mask  
  5. {  
  6. public:  
  7.        Mask(char* inputFile,int* maskValue,int maskLength);  
  8.        virtual void Run(void);  
  9.        ~Mask(void);  
  10.    
  11. private:  
  12.        char* _inputFile;  
  13.        int* _maskValue;  
  14.        int _maskLength;  
  15. };  
  (2)Mask类内部实现,这里使用char*和int*这两个有代表性的参数,C#向C++进行参数的传递是很难弄的一步。     [cpp] view plaincopyprint?
  1. //Mask类实现文件mask.cpp  
  2. Mask::Mask(char* inputFile ,int* maskValue,int maskLength)  
  3. {  
  4.        _inputFile=new char[strlen(inputFile)+1];  
  5.        strcpy(_inputFile,inputFile);  
  6.    
  7.        _maskValue=new int[maskLength];  
  8.        _maskLength=maskLength;  
  9.        for(int i=0;i<masklength;i++)  < span="">
  10.               _maskValue[i]=maskValue[i];  
  11. }  
  12.    
  13. void Mask::Run(void)  
  14. {  
  15. }  
  16.    
  17. Mask::~Mask(void)  
  18. {  
  19.        if (_inputFile)  
  20.        {  
  21.               delete [] _inputFile;  
  22.               _inputFile=NULL;  
  23.        }  
  24.        if (_maskValue)  
  25.        {  
  26.               delete [] _maskValue;  
  27.               _maskValue=NULL;  
  28.        }  
  29. }  
  可以设置生成类型为DLL,然后将debug目录下的dll文件和lib文件找到,留待下一步使用。     (3)编写C++.NET类MaskCLR,C++.NET我一直认为就是个摆设,没想到也能派上用场,难得啊,也不完全是个废物。MaskCLR可以调用Mask类,注意函数的参数已经变成String ^和int*,现在是.NET环境,使用指针没那么方便了。   [cpp] view plaincopyprint?
  1. //MaskCLR类头文件,用来包装Mask类MaskCLR.h  
  2. #pragma once  
  3. #include " mask.h"        //这个就是上面的Mask头文件  
  4.    
  5. public ref class MaskCLR  
  6. {  
  7. private:  
  8.        char* _inputFile;  
  9.        int* _maskValue;  
  10.        int _maskLength;  
  11.    
  12. public:  
  13.        MaskCLR(String ^ inputFile,int* maskValue,int maskLength);  
  14.        virtual void  Run(void) override;  
  15. };  
  (4)MaskCLR类内部实现,首先要处理参数类型问题,将String ^类型转为char*。在MaskCLR:: Run函数内部调用Mask类,Mask是DLL导出的类。     [cpp] view plaincopyprint?
  1. // MaskCLR类内部实现MaskCLR.cpp  
  2. #include "MaskCLR.h"  
  3.    
  4. MaskCLR::MaskCLR(String ^ inputFile,int* maskValue,int maskLength)  
  5. {  
  6.        _inputFile=GlobeFunction::StringToChar(inputFile);  
  7.        _maskValue=maskValue;  
  8.        _maskLength=maskLength;  
  9. }  
  10.    
  11. void MaskCLR:: Run(void)  
  12. {  
  13.        Mask mask(_inputFile, _maskValue,_maskLength);  
  14.        mask.Run();                    
  15. }  
  (5)将String ^类型转为char*,可以利用StringToHGlobalAnsi这个.NET自带的函数。     [csharp] view plaincopyprint?
  1. char* GlobeFunction::StringToChar(String ^str)  
  2. {  
  3.        return (char*)(Marshal::StringToHGlobalAnsi(str)).ToPointer();  
  4. }  
    (6)最终生成了两个DLL文件,一个是原始C++编写的,另一个是托管C++编写的,托管dll只是个外壳,最终还是调用原始dll,因此这两个dll要放在一起。下面终于进入C#环境了,首先将托管dll添加引用到C#工程中,C++.NET和C#可以直接互用。   (7)由于有个int*类型参数,在C#里指针属于不安全代码,因此使用unsafe关键字将涉及到指针的代码包括起来,在工程属性里设置允许使用不安全代码。定义int指针需要使用stackalloc关键字,创建一个int数组,对数组赋值后,将指针传递给类函数。 [csharp] view plaincopyprint?
  1. //c#调用托管dll中的MaskCLR类form1.cs  
  2. unsafe  
  3. {  
  4.        int* value = stackalloc int[1];  
  5.        value[0] = 0;  
  6.        MaskCLR mask = new MaskCLR("D:\\临时\\mask8.tif", value, 1);  
  7.        mask.Run();  
  8. }  
  (8)至此大功告成,可以用C#很方便的调用C++写的类了,如果觉得有用,不要忘了给我留言啊!!O(∩_∩)O~
这篇关于C#调用C++写的dll的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!