C#调用C++的dll所遇到的问题以及解决方式

907 阅读4分钟

一、问题集合

  1. 跨语言中涉及的数据类型的对应问题
  2. C#如何接收C++中的返回值
  3. C++导出函数类型为非void的,C#如何接收
  4. 如何查看C++生成的DLL中有哪些函数
  5. 数据参数类型无误,调用的DLL返回值收不到

二、解决方案

  1. 在跨语言中,有时候数据类型所占用的字节数是不一样的。因此,数据类型的传输是讲究一个对仗的。通俗的说就是我们要保持在C#调用C++的时候函数参数占的字节数是一致的,如果不一致会导致数据传输失败。在C#端会进行相应的错误提示,例如:未处理DllNotFoundException:无法加载 DLL“EKFLib.dll”: 内存位置访问无效。 (异常来自 HRESULT:0x800703E6) 如出现这个提示,大概率就是你在C#端中设置的参数数据类型出错导致。
  • 数据类型的对应关系

C++

  • HANDLE(void *) ---- c#:System.IntPtr
  • Byte(unsigned char) ---- c#:System.Byte
  • SHORT(short) ---- c#:System.Int16
  • WORD(unsigned short) ---- c#:System.UInt16
  • INT(int) ---- c#:System.Int16
  • INT(int) ---- c#:System.Int32
  • UINT(unsigned int) ---- c#:System.UInt16
  • UINT(unsigned int) ---- c#:System.UInt32
  • LONG(long) ---- c#:System.Int32
  • ULONG(unsigned long) ---- c#:System.UInt32
  • DWORD(unsigned long) ---- c#:System.UInt32
  • DECIMAL ---- c#:System.Decimal
  • BOOL(long) ---- c#:System.Boolean
  • CHAR(char) ---- c#:System.Char
  • LPSTR(char *) ---- c#:System.String
  • LPWSTR(wchar_t *) ---- c#:System.String
  • LPCSTR(const char *) ---- c#:System.String
  • LPCWSTR(const wchar_t *) ---- c#:System.String
  • PCAHR(char *) ---- c#:System.String
  • BSTR ---- c#:System.String
  • FLOAT(float) ---- c#:System.Single
  • DOUBLE(double) ---- c#:System.Double
  • VARIANT ---- c#:System.Object
  • PBYTE(byte *) ---- c#:System.Byte[] //c++:BSTR ---- c#:StringBuilder
  • LPCTSTR ---- c#:StringBuilder
  • LPCTSTR ---- c#:string
  • LPTSTR ---- c#:[MarshalAs(UnmanagedType.LPTStr)] string
  • LPTSTR 输出变量名 ---- c#:StringBuilder 输出变量名
  • LPCWSTR ---- c#:IntPtr
  • BOOL ---- c#:bool
  • HMODULE ---- c#:IntPtr
  • HINSTANCE ---- c#:IntPtr
  • 结构体 ---- c#:public struct 结构体{};
  • 结构体 变量名 ---- c#:out 变量名 //C#中提前申明一个结构体实例化后的变量名
  • 结构体 &变量名 ---- c#:ref 结构体 变量名
  • WORD ---- c#:ushort
  • DWORD ---- c#:uint
  • DWORD ---- c#:int //c++:UCHAR ---- c#:int
  • UCHAR ---- c#:byte
  • UCHAR ---- c#:string
  • UCHAR ---- c#:IntPtr //c++:GUID ---- c#:Guid
  • Handle ---- c#:IntPtr
  • HWND ---- c#:IntPtr
  • DWORD ---- c#:int
  • COLORREF ---- c#:uint
  • unsigned char ---- c#:byte
  • unsigned char * ---- c#:ref byte
  • unsigned char * ---- c#:[MarshalAs(UnmanagedType.LPArray)] byte[]
  • unsigned char * ---- c#:[MarshalAs(UnmanagedType.LPArray)] Intptr
  • unsigned char & ---- c#:ref byte
  • unsigned char 变量名 ---- c#:byte 变量名
  • unsigned short 变量名 ---- c#:ushort 变量名
  • unsigned int 变量名 ---- c#:uint 变量名
  • unsigned long 变量名 ---- c#:ulong 变量名
  • char 变量名 ---- c#:byte 变量名 //C++中一个字符用一个字节表示,C#中一个字符用两个字节表示
  • char 数组名[数组大小] ---- c#:MarshalAs(UnmanagedType.ByValTStr, SizeConst = 数组大小)] public string 数组名;
  • ushort char * ---- c#:string //传入参数
  • char * ---- c#:StringBuilder//传出参数
  • char *变量名 ---- c#:ref string 变量名
  • char *输入变量名 ---- c#:string 输入变量名
  • char *输出变量名 ---- c#:[MarshalAs(UnmanagedType.LPStr)] StringBuilder 输出变量名
  • char ** ---- c#:string
  • char *变量名 ---- c#:ref string 变量名
  • const char * ---- c#:string
  • char[] ---- c#:string
  • char 变量名[数组大小] ---- c#:[MarshalAs(UnmanagedType.ByValTStr,SizeConst=数组大小)] public - string 变量名;
  • c++:struct 结构体名 变量名 ---- c#:ref 结构体名 变量名 委托 变量名 ---- c#:委托 变量名
  • int ---- c#:int
  • int ---- c#:ref int
  • int & ---- c#:ref int
  • int * ---- c#:ref int //C#中调用前需定义int 变量名 = 0;
  • int ---- c#:IntPtr
  • int32 PIPTR * ---- c#:int32[]
  • float PIPTR * ---- c#:float[]
  • double 数组名 ---- c#:ref double 数组名
  • double[] 数组名 ---- c#:ref double 数组名
  • long ---- c#:int
  • ulong ---- c#:int

摘抄自

在查找数据类型的时候,看到这些会感觉头昏眼花,再此总结一些本人在项目开发中常用的数据类型

C++

C++C#
intint
char*string
const char*string
  1. 接收参数的返回值用ref
  2. 在DLL中已经封装好的带返回值的函数在接收时,是否需要统一数字类型呢?这个问题对于新手来说,是一个比较困惑的问题。在此,应该了解一下C#是没有指针这个概念的,如需要接收指针就要用IntPtr对其进行接收

举个例子:

在C++中导出DLLEXPORT char* GetPath(char* path);,在C#端中写成[DllImport("ClientAdapter.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]

private static extern void GetPatternPath(ref byte path);

  1. 使用depends.exe,打开后,点击File把dll导入

image.png

如上图所示Function中的就是dll中封装的函数

  1. 方法一:使用两端打log的方式查找问题出在哪;方法二:检查C#或者C++中传递的参数是否正确;方法三:当存在多参数时,用控制变量法去对其进行一一排除,注意内部数据类型互转可能出现的问题。