簡體   English   中英

無法從C#調用用C ++編寫的DLL函數

[英]unable to invoke function of dll written in c++ from 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#中制作的struct

        [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;      
};

這是我調用方法(函數)的代碼

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 \\ Debug \\ FCFake.vshost.exe'中檢測到問題。

附加信息:調用PInvoke函數“ Program :: GetCardInfoEx”已使堆棧不平衡。 這可能是因為托管PInvoke簽名與非托管目標簽名不匹配。 檢查PInvoke簽名的調用約定和參數是否與目標非托管簽名匹配。

該如何解決呢?

UPD:我根據Dirk的答案編輯了TCardInfoEx結構。 不幸的是,它沒有幫助我

乍一看,我想說的問題是,您嘗試映射到C#字符串的C ++結構中的固定大小char數組。

當使用固定大小的數組時,它會嵌入到結構本身中,這與使用指針相反,后者只會將指針添加到結構中,而不會添加指向它的數據。

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

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

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

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

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

例如,如果您的C ++程序使用ANSI字符。


您已經指定了打包值( #pragma pack(1) )。 這也必須使用

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

最后,您的簽名並不完全匹配:

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中,結構是按值傳遞的,因此您必須通過ref傳遞它,以使簽名匹配。

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


並且(希望最后)您的呼叫約定不匹配。 DllImport的默認調用約定為StdCall而函數使用__cdecl 因此,您也必須將其添加到DllImport屬性中

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

感謝@DavidHeffernan,這篇文章從原始版本擴展了很多,在該版本中,我只討論了固定數組大小的問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM