【C#】调用DLL问题汇总


 

一、尝试读取或写入受保护的内存,这通常指示其他内存已损坏

 

可能原因:

 

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

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM