【C#】调用DLL问题汇总
一、尝试读取或写入受保护的内存,这通常指示其他内存已损坏
可能原因:
1、传入的数组长度开的太小了;
2、传入参数没有加ref。
二、C#调用DLL时提示:未找到指定模块
1、检查DLL中是否有该函数,并有调用接口
可以使用dumpbin.exe查看封装好的dll里都有什么函数,参见:
https://blog.csdn.net/wangzhichunnihao/article/details/112570828
https://blog.csdn.net/weixin_42953003/article/details/124383170
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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!