2014-09-02 104 views
0

我需要調用那些DLL用C++編寫的C#控制檯應用程序的功能。我編組並在C#中編寫了相同的數據類型。但無論如何,我得到的錯誤。哪裏有問題?無法調用用C語言編寫的DLL函數++從C#

這裏是DLL的功能

extern "C" { 
SAMPLENATIVEDLL_API int GetCardInfoEx(INT64 Card, DWORD Restaurant, DWORD UnitNo, TCardInfoEx* Info, char* InpBuf, DWORD InpLen, WORD InpKind, char* OutBuf, DWORD OutLen, WORD OutKind) { 
    int res = getCardInfoEx(Card, Info, UnitNo); 
    return res; 
} 

}代碼

這裏是TCardInfoEx結構代碼

#pragma pack(1) 

typedef struct TCardInfoEx { 

    WORD Size;    
    BYTE Deleted;    
    BYTE Seize;    
    BYTE StopDate;   
    BYTE Holy;    
    BYTE Manager;    
    BYTE Blocked;    
    CHAR WhyLock[256];  

    CHAR Holder[40];   
    INT64 UserID;    
    DWORD CardAccountNumber; 
    DWORD TypeOfDefaulter;  
    WORD Bonus;    
    WORD Discount;   

    INT64 Summa;    

    INT64 AvailableSumma;  

    INT64 SummaOnCardAccount2; 
    INT64 SummaOnCardAccount3; 
    INT64 SummaOnCardAccount4; 
    INT64 SummaOnCardAccount5; 
    INT64 SummaOnCardAccount6; 
    INT64 SummaOnCardAccount7; 
    INT64 SummaOnCardAccount8; 

    CHAR OtherCardInformation[256]; 
    CHAR OtherCardInformation1[256]; 
    CHAR OtherCardInformation2[256]; 
}; 

以下是我在C#中做結構

 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
public struct TCardInfoEx 
{ 
    ushort Size;    
    byte Deleted;    
    byte Seize;    
    byte StopDate;   
    byte Holy;    
    byte Manager;    
    byte Blocked; 

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 
    String WhyLock; 

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 40)] 
    String Holder;  
    Int64 UserID;    
    uint CardAccountNumber; 
    uint TypeOfDefaulter;  
    ushort Bonus;    
    ushort Discount;   

    Int64 Summa;    

    Int64 AvailableSumma;  

    Int64 SummaOnCardAccount2; 
    Int64 SummaOnCardAccount3; 
    Int64 SummaOnCardAccount4; 
    Int64 SummaOnCardAccount5; 
    Int64 SummaOnCardAccount6; 
    Int64 SummaOnCardAccount7; 
    Int64 SummaOnCardAccount8; 

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 
    String OtherCardInformation; 

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 
    String OtherCardInformation1; 

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 
    String OtherCardInformation2;  
}; 

這裏是c頌歌,我調用的方法(函數)

class Program 
{ 
    [DllImport("SampleNativeDLL.dll", CharSet = CharSet.Unicode)] 
    public static extern int GetCardInfoEx(Int64 Card, uint Restaurant, uint UnitNo, TCardInfoEx Info, String InpBuf, uint InpLen, ushort InpKind, String OutBuf, uint OutLen, ushort OutKind); 

    static void Main(string[] args) 
    { 
     TCardInfoEx tc = new TCardInfoEx(); 
     GetCardInfoEx(1248000259045, 1, 1, tc, "", 0, 1, "", 1, 1); 
    } 
} 

而且我得到以下錯誤: 託管調試助手「PInvokeStackImbalance」已檢測到一個問題,「\ BIN \調試\ FCFake.vshost.exe」。

其他信息:對PInvoke的函數調用「計劃:: GetCardInfoEx」有不平衡的堆棧。這很可能是因爲託管的PInvoke簽名與非託管目標籤名不匹配。檢查PInvoke簽名的調用約定和參數是否與目標非託管簽名相匹配。

如何解決這個問題?

UPD:我編輯根據德克的回答TCardInfoEx結構。可惜的是,它並沒有幫助我

+0

嘗試爲您的字符串成員使用'[MarshalAs(UnmanagedType.ByValTStr,SizeConst = ????)]'屬性,或者將它們不作爲字符串編組,而是作爲Byte數組。 – 2014-09-02 07:14:42

+1

我不確定,但'字符串WhyLock'字符串持有人'不匹配'字符WhyLock [256]'和'字符持有人[40]'。你認爲String如何知道即256字節長? – lifeOfPI 2014-09-02 07:15:00

+0

包裝也是錯的。並且p/invoke需要通過ref傳遞結構。 – 2014-09-02 07:34:13

回答

5

快速瀏覽我想說的問題是在C++結構固定大小的字符數組,你試圖映射到C#字符串。

當使用這樣的固定大小的陣列就變成嵌入在結構本身,而不是使用指針,這將僅指針添加到結構,而不是它指向的數據。

您可以修改C#結構使.NET知道如何使用MarshalAs -attribute其映射到本地的世界:

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] 
string OtherCardInformation; 

,這適用於.NET字符串C++字符數組的所有映射。

您可能還需要設置字符編碼使用

[StructLayout(CharSet = CharSet.Ansi)] 
public struct TCardInfoEx 

例如結構,如果你的C++程序使用ANSI字符。


您已指定包值(#pragma pack(1))。這也必須被翻譯成。使用

[StructLayout(CharSet = CharSet.Ansi, Pack = 1)] 
public struct TCardInfoEx 

和NET世界最後你的簽名不匹配真正:

int GetCardInfoEx(
    INT64 Card, // long 
    DWORD Restaurant, // uint 
    DWORD UnitNo, // uint 
    TCardInfoEx* Info, // must be ref TCarfInfoEx 
    char* InpBuf, // could be string, could also be byte[] 
    DWORD InpLen, // uint 
    WORD InpKind, // ushort 
    char* OutBuf, // could be StringBuilder, or IntPtr 
    DWORD OutLen, // uint 
    WORD OutKind) // ushort 

在.NET結構是按值傳遞的,所以你必須通過裁判爲了通過它簽名匹配。

將C++ char *映射到.NET類型始終與上下文相關。有時候,這樣的指針被用作輸入字符串,有時用於任意輸入數據,當然它也可能只是單個字符輸出參數。


(希望最後)你的調用約定不匹配。 DllImport的默認調用約定是StdCall,而您的功能使用__cdecl。所以,你必須將它添加到您的DllImport屬性太

[DllImport(..., CallingConvetion = CallingConvention.Cdecl, ...)] 

由於@DavidHeffernan這個帖子從原來的版本,在其中我只涵蓋了固定數組的大小問題,擴大了不少。

+0

thx用於讓我放心吧:) – lifeOfPI 2014-09-02 07:18:35

+0

謝謝,我使用了你的建議,但它沒有幫助 – 2014-09-02 07:40:45

+0

@Nurzhan那是因爲其他錯誤。請閱讀我的評論。 – 2014-09-02 07:47:14