【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