一、尝试读取或写入受保护的内存,这通常指示其他内存已损坏
可能原因:
1、传入的数组长度开的太小了;
2、传入参数没有加ref。
二、C#调用DLL时提示:未找到指定模块
1、检查DLL中是否有该函数,并有调用接口
可以使用dumpbin.exe查看封装好的dll里都有什么函数,参见:
https://blog.csdn.net/wangzhichunnihao/article/details/112570828
2、DLL中可能有调用其他的DLL,但调用时未查找到
比如一个集合了操作示波器、万用表等设备函数接口的DLL,使用时往往需要先在电脑上安装示波器、万用表的使用环境(驱动等)。
如果电脑上安装了万用表的环境,没有安装示波器的环境,即使调用该DLL时只是调用了操作万用表的函数,也是会提示找不到模块的。
3、C:\Windows\System32或C:\Windows\SysWOW64中缺少DLL
像是MSVCP60D.DLL、MSVCRTD.DLL这种。一般这些DLL安装了VC6.0应该就可以。
4、在将用VS2008编写的WinForm项目转为VS2013项目后,调用dll报该错
尝试将用VC6 C++编写的dll项目转为VS2013项目,同时在WinForm项目内写调用接口时,加上
[DllImport("xxx.dll", SetLastError = true,CallingConvention=CallingConvention.Cdecl)]
参考:https://blog.csdn.net/jing_cs/article/details/78559508
三、程序在32位操作系统上运行正常,在64位操作系统上运行提示“试图加载格式不正确”
点击项目属性,把目标平台Any CPU 设置为X86。
四、在C++函数中返回false,在C#里调用该DLL却返回true
C++函数:
bool test() { if (xx) { return TRUE; } return FALSE; }
WinForm调用:
while (test() == true) { }
在C++中返回FALSE,在C#里调用却是true,导致死循环。
可能是bool类型的问题。返回类型改为int返回就正常了:
int test() { if (xx) { return 1; } return 0; }
调用DLL时出现的错误总结:https://blog.csdn.net/qq_22654855/article/details/113456374
五、引用子文件夹下的DLL
修改App.config:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <runtime> <!--xmlns是必需的特性。指定程序集绑定所需的 XML 命名空间。--> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <publisherPolicy apply="yes"/> <!--指定运行时是否使用发布者策略--> <!--指定加载程序集时公共语言运行时搜索的子目录, 其中privatePath是相对于*.exe.config文件的相对路径,多个文件夹以分号分隔。--> <probing privatePath="DLL;DLL\OtherDLL"/> </assemblyBinding> </runtime> </configuration>
注意:如果这些Dll在修改路径前就已经引用到 解决方案资源管理器--引用 里,那转移后,如果编译报错的话,要重新把这些Dll添加到引用。并且要把这些Dll“复制本地”属性设为False,否则再次生成的时候会在转移前的文件夹下再次出现。
但对于自己编写的一些动态库好像没办法用这个方法引用,还是要放在Bin文件夹下才能调用到。
参考:https://blog.csdn.net/weixin_39994714/article/details/115201485
六、调用DLL数据类型转换
1、char、char*类型转换
在C#中char是16位的;C++中的char是8位的。
(1)unsigned char类型可以用byte类型代替。
(2)char*可以用string传入。
2、结构体转换
https://www.cnblogs.com/HansZimmer/p/12485234.html
3、byte[]和byte*的复制和转换
https://www.cnblogs.com/castor-xu/p/14719493.html
4、DLL转换类型对照
C++:unsigned char * ---- C#:ref byte 或 [MarshalAs(UnmanagedType.LPArray)]
C++:unsigned char & ---- C#:ref bytebyte[] 或 [MarshalAs(UnmanagedType.LPArray)] Intptr
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++:LONG(long) ---- C#:System.Int32
C++:ULONG(unsigned long)---- C#:System.UInt32
C++:DWORD(unsigned long) ---- C#:System.UInt32
C++:结构体 **变量名 ---- C#:out 变量名 //C#中提前申明一个结构体实例化后的变量名
C++:结构体 &变量名 ---- C#:ref 结构体 变量名
参考:https://blog.csdn.net/weixin_28823431/article/details/117127717
5、用C#实现联合体
情景:有一个接收函数,在C++中用法是传入一个变量的地址,变量类型是一个联合体。
C++函数:int Test(unsigned char* data)
使用如下:
AFRAME * pAFrame; //定义 pAFrame = (AFRAME *)malloc(DATA_LENGTH);//DATA_LENGTH = 16;
memset(pAFrame,0,DATA_LENGTH); int rstatus = Test((unsigned char *)pAFrame);//传入地址取得数据
//做其他处理
if(pAFrame[0].Fra1.Info.bit.d1 == 0)
{
} ...
其中,联合体定义如下:
typedef union{ struct{ BYTE d1:4; BYTE d2:2; BYTE d3:1; BYTE d4:1; }bit; BYTE byte; }INFO; typedef struct _Frame1 { INFO Info; WORD data1; BYTE data2[8]; BYTE data3[2]; BYTE data4[3]; }FRAME1; typedef struct _Frame2 { INFO Info; DWORD data1; BYTE data2[8]; BYTE data3[3]; }FRAME2; typedef union{ FRAME1 Fra1; FRAME2 Fra2; }AFRAME;
(1)第一种方法:char * 对应C#中的Intptr,有时候C#中也可以用byte[]代替。
封装: [DllImport("test.dll", SetLastError = true)] public static extern int Test(byte[] data); //不需要加ref
调用: byte[] data = new byte[16]; int res = Test(data); //执行后需要根据联合体的定义对data进行数据解析
这是最简单的解决方式。
(2)如果不想解析字节数组,那么就需要在C#实现联合体,然后像C++里一样实现。
在C#实现联合体:
[StructLayout(LayoutKind.Explicit)] struct INFO { [FieldOffset(0)] public byte value; [FieldOffset(1)] byte _d1; [FieldOffset(1)] byte _d2; [FieldOffset(1)] byte _d3; [FieldOffset(1)] byte _d4; public byte d1 { get { _d1 = (byte)(this.value & 0x0F); //4bit
return _d1; } set { this.value = (byte)(((value & 0x01) << 7) | ((value & 0x01) << 6) | ((value & 0x03) <<
4) | (value & 0x0F)); } } public byte d2 { get { _d2 = (byte)((this.value >> 4) & 0x03); //2bit
return _d2; } set { this.value = (byte)(((value & 0x01) << 7) | ((value & 0x01) << 6) | ((value & 0x03) <<
4) | (value & 0x0F)); } } public byte d3 { get { _d3 = (byte)((this.value >> 6) & 0x01); //1bit
return _d3; } set { this.value = (byte)(((value & 0x01) << 7) | ((value & 0x01) << 6) | ((value & 0x03) <<
4) | (value & 0x0F)); } } public byte d4 { get { _d4 = (byte)((this.value >> 7) & 0x1); //1bit
return _d4; } set { this.value = (byte)(((value & 0x01) << 7) | ((value & 0x01) << 6) | ((value & 0x03) <<
4) | (value & 0x0F)); } } } struct FRAME1 { public INFO Info; public short data1; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] data2; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public byte[] data3; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] data4; } struct FRAME2 { public INFO Info; public uint data1; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] data2; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] data3; } [StructLayout(LayoutKind.Explicit)] struct AFRAME { [FieldOffset(0)] public FRAME1 Fra1; [FieldOffset(0)] public FRAME2 Fra2; };
封装:
[DllImport("test.dll", CharSet = CharSet.Ansi)] static extern UInt32 Test(IntPtr pReceive);
调用:
IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(AFRAME))); res = Test(pt); for (UInt32 i = 0; i < 1; i++) { AFRAME pAFrame = (AFRAME)Marshal.PtrToStructure((IntPtr)((UInt32)pt + i * Marshal.SizeOf(typeof (AFRAME))), typeof(AFRAME)); //做其他处理
if(pAFrame[0].Fra1.Info.bit.d1 == 0) { } ... }
第二种方法我没有验证到最后,因为这个地方会报错:“因为它在0偏移位置处包含一个对象字段”。
[StructLayout(LayoutKind.Explicit)] struct AFRAME { [FieldOffset(0)] public FRAME1 Fra1; [FieldOffset(0)] public FRAME2 Fra2; };
没找到解决办法...
主要是学到了在C#中实现位域的方法,那个是验证了能用的。其他的先Mark一下。
学习网址:
1)IntPtr数据类型相关操作
byte[]转IntPtr、IntPtr转byte、IntPtr转Stream:
https://blog.csdn.net/qq_41452267/article/details/109484161/
byte[]、struct、intptr等的相互转换:网址
2)调用带结构体指针的C Dll的方法
https://www.cnblogs.com/ye-ming/p/8004314.html
3)C#使用结构体实现共用体
https://www.cnblogs.com/willick/p/14274914.html
举例:
// FieldOffset 表示偏移的位置(以字节为单位) // sizeof(int) = 4, sizeof(byte) = 1
[FieldOffset(0)] public int Address; // Address是占用4个字节的整数;
[FieldOffset(0)] public byte Byte1; //在0字节处对齐,其实就是指向同一个内存
[FieldOffset(1)] public byte Byte2; [FieldOffset(2)] public byte Byte3; [FieldOffset(3)] public byte Byte4; //当使用Address进行访问时,则直接使用四个字节 //而访问Byte1、Byte2、Byte3、Byte4则分别使用1个字节 //Byte1、Byte2、Byte3、Byte4共同组成了Address
附:一个实例
6、C#实现位域
C++中的位域成员有2种用途。一种是存放位标志的,还有一种是存放数值的。
(1)存放位标志
应该跟位域枚举是一个东西。主要为了方便位运算。在C#中通过enum实现。
(网上搜索在C#中实现位域,很多答案说用enum,如果是要实现上面那种存放数值的位域,用enum是不能实现的)
C/C++位域知识小结:
https://blog.csdn.net/dragon101788/article/details/54315703
https://www.cnblogs.com/x_wukong/p/5743369.html
枚举类型与位域枚举Enum:
https://www.cnblogs.com/springsnow/p/9428501.html#_label2_3
使用C++ struct位域的方法:
https://blog.csdn.net/sailor32731958/article/details/4844064
(2)实现C++中的数值型位域
https://blog.csdn.net/tiger_zhao/article/details/45075897(我参考的)
https://blog.csdn.net/XinChiMaker/article/details/104924707
5、C语言共用体(C语言union用法)详解
http://c.biancheng.net/view/2035.html
6、联合体(union)的使用方法及其本质
https://blog.csdn.net/huqinweI987/article/details/23597091