- C#动态调用C++编写的DLL函数
- 
动态加载DLL需要使用Windows API函数:LoadLibrary、GetProcAddress以及FreeLibrary。我们可以使用DllImport在C#中使用这三个函数。 
 [DllImport("Kernel32")]
 public static extern int GetProcAddress(int handle, String funcname);
 [DllImport("Kernel32")]
 public static extern int LoadLibrary(String funcname);
 [DllImport("Kernel32")]
 public static extern int FreeLibrary(int handle);
 当我们在C++中动态调用Dll中的函数时,我们一般的方法是:
 假设DLL中有一个导出函数,函数原型如下:
 BOOL __stdcall foo(Object &object, LPVOID lpReserved);
 1、首先定义相应的函数指针:
 typedef BOOL (__stdcall *PFOO)(Object &object, LPVOID lpReserved);
 2、调用LoadLibrary加载dll:
 HINSTANCE hInst = ::LoadLibraryW(dllFileName);
 3、调用GetProcAddress函数获取要调用函数的地址:
 PFOO foo = (PFOO)GetProcAddress(hInst,"foo");
 if(foo == NULL)
 {
 FreeLibrary(hInst);
 return false;
 }
 4、调用foo函数:
 BOOL bRet = foo(object,(LPVOID)NULL);
 5、使用完后应释放DLL:
 FreeLibrary(hInst);
 那么在C#中应该怎么做呢?方法基本上一样,我们使用委托来代替C++的函数指针,通过.NET Framework 2.0新增的函数GetDelegateForFunctionPointer来得到一个委托的实例:
 下面封装了一个类,通过该类我们就可以在C#中动态调用Dll中的函数了:
 public class DLLWrapper
 {
 ///<summary>
 /// API LoadLibrary
 ///</summary>
 [DllImport("Kernel32")]
 public static extern int LoadLibrary(String funcname);
 ///<summary>
 /// API GetProcAddress
 ///</summary>
 [DllImport("Kernel32")]
 public static extern int GetProcAddress(int handle, String funcname);
 ///<summary>
 /// API FreeLibrary
 ///</summary>
 [DllImport("Kernel32")]
 public static extern int FreeLibrary(int handle);
 ///<summary>
 ///通过非托管函数名转换为对应的委托, by jingzhongrong
 ///</summary>
 ///<param name="dllModule">通过LoadLibrary获得的DLL句柄</param>
 ///<param name="functionName">非托管函数名</param>
 ///<param name="t">对应的委托类型</param>
 ///<returns>委托实例,可强制转换为适当的委托类型</returns>
 public static Delegate GetFunctionAddress(int dllModule, string functionName, Type t)
 {
 int address = GetProcAddress(dllModule, functionName);
 if (address == 0)
 return null;
 else
 return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t);
 }
 ///<summary>
 ///将表示函数地址的IntPtr实例转换成对应的委托, by jingzhongrong
 ///</summary>
 public static Delegate GetDelegateFromIntPtr(IntPtr address, Type t)
 {
 if (address == IntPtr.Zero)
 return null;
 else
 return Marshal.GetDelegateForFunctionPointer(address, t);
 }
 ///<summary>
 ///将表示函数地址的int转换成对应的委托,by jingzhongrong
 ///</summary>
 public static Delegate GetDelegateFromIntPtr(int address, Type t)
 {
 if (address == 0)
 return null;
 else
 return Marshal.GetDelegateForFunctionPointer(new IntPtr(address), t);
 }
 }
 通过这个类,我们这样调用DLL:
 1、声明相应的委托(正确声明很重要,否则不能调用成功,后面有详细介绍)。
 2、加载DLL:
 int hModule = DLLWrapper.LoadLibrary(dllFilePath);
 if (hModule == 0)
 return false;
 3、获取相应的委托实例:
 FOO foo = (FOO)DLLWrapper.GetFunctionAddress(hModule, "foo", typeof(FOO));
 if (foo == null)
 {
 DLLWrapper.FreeLibrary(hModule);
 return false;
 }
 4、调用函数:
 foo(...);
 5、.NET并不能自动释放动态加载的DLL,因此我们在使用完DLL后应该自己释放DLL:
 DLLWrapper.FreeLibrary(hModule);
 下面我们将就委托应如何声明进行相应的讨论,在实际操作过程中,我发现使用DllImport方法和动态调用方法两者在C#中对DLL中函数原型的声明是有些区别的,下面我介绍动态调用中委托的声明:
 1、首先应该注意的是,C++中的类型和C#中类型的对应关系,比如C++中的long应该对应C#中的Int32而不是long,否则将导致调用结果出错。
 2、结构的声明使用StructLayout对结构的相应布局进行设置,具体的请查看MSDN:
 使用LayoutKind指定结构中成员的布局顺序,一般可以使用Sequential:
 [StructLayout(LayoutKind.Sequential)]
 struct StructVersionInfo
 {
 public int MajorVersion;
 public int MinorVersion;
 }
 另外,如果单独使用内部类型没有另外使用到字符串、结构、类,可以将结构在C#中声明为class:
 [StructLayout(LayoutKind.Sequential)]
 class StructVersionInfo
 {
 public int MajorVersion;
 public int MinorVersion;
 }
 对应C++中的声明:
 typedef struct _VERSION_INFO
 {
 int MajorVersion;
 int MinorVersion;
 } VERSION_INFO, *PVERSION_INFO;
 如果结构中使用到了字符串,最好应指定相应的字符集:
 [StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode)]
 部分常用的声明对应关系(在结构中):
 C++:字符串数组
 wchar_t Comments[120];
 C#:
 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 120)]
 public string Comments;
 C++:结构成员
 VERSION_INFO ver;
 C#
 publicStructVersionInfo ver;
 C++:函数指针声明
 PFOO pFoo; //具体声明见文章前面部分
 C#:
 publicIntPtr pFoo; //也可以为 public int pFoo;
 //不同的声明方法可以使用上面DLLWrapper类的相应函数获取对应的委托实例
 如果在结构中使用到了union,那么可以使用FieldOffset指定具体位置。
 3、委托的声明:
 当C++编写的DLL函数需要通过指针传出将一个结构:如以下声明:
 void getVersionInfo(VERSION_INFO *ver);
 对于在C#中声明为class的结构(当VERSION_INFO声明为class)
 delegate voidgetVersionInfo(VERSION_INFO ver);
 如果结构声明为struct,那么应该使用如下声明:
 delegate voidgetVersionInfo(refVERSION_INFO ver);
 注意:应该使用ref关键字。
 如果DLL函数需要传入一个字符串,比如这样:
 BOOL __stdcall jingzhongrong1(const wchar_t* lpFileName, int* FileNum);
 那么使用委托来调用函数的时候应该在C#中如下声明委托:
 delegate bool jingzhongrong1(
 [MarshalAs(UnmanagedType.LPWStr)]String FileName,
 ref int FileNum);
 注意:应该使用[MarshalAs(UnmanagedType.LPWStr)]和String进行声明。
 如果要在DLL函数中传出一个字符串,比如这样:
 void __stdcall jingzhongrong2(
 wchar_t* lpFileName, //要传出的字符串
 int* Length);
 那么我们如下声明委托:
 //使用委托从非托管函数的参数中传出的字符串,
 //应该这样声明,并在调用前为StringBuilder预备足够的空间
 delegate void jingzhongrong2(
 [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpFileName,
 ref int Length,
 );
 在使用函数前,应先为StringBuilder声明足够的空间用于存放字符串:
 StringBuilder fileName = new StringBuilder(FileNameLength);参考: http://www.2cto.com/kf/201007/52562.html 参考: http://www.codeproject.com/Articles/12121/Essential-P-Invoke 参考: http://www.codeproject.com/Articles/339290/PInvoke-pointer-safety-Replacing-IntPtr-with-unsaf 参考: http://www.dotblogs.com.tw/merlin/archive/2012/07/17/73424.aspx ============================== (转载:http://blog.csdn.net/xqf222/article/details/5877795) //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);
 *
 *
 */f you want to pass a byte array to native DLL as parameter, you can use the Intptr to do this, please check the demo below. //C++ API code: DEMODLL_API void TestArrayPara(BYTE * pArray, int nSize) { for (int i=0; i<nSize; i++) printf("%d\n", pArray[i]); } //C# code: class Class2 { [DllImport(@"DemoDll.dll")] public static extern void TestArrayPara(IntPtr pArray, int nSize); public static void Test() { Console.WriteLine("This is C# program"); byte[] array = new byte[16]; for (int i = 0; i < 16; i++) { array[i] = (byte)(i + 97); } //============================ int size = Marshal.SizeOf(array[0]) * array.Length; IntPtr pnt = Marshal.AllocHGlobal(size); try { // Copy the array to unmanaged memory. Marshal.Copy(array, 0, pnt, array.Length); } finally { // Free the unmanaged memory. // Marshal.FreeHGlobal(pnt); } //============================ TestArrayPara(pnt, array.Length); Marshal.FreeHGlobal(pnt); Console.WriteLine("End of app"); } } In addition to Mashal.Copy method, please refer to the following article. 
 
                    
                     
                    
                 
                    
                 

 
         
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号