无法从c#调用用c ++编写的dll函数

Nur*_*yev 0 c# c++ dll pinvoke marshalling

我需要从C#console app调用一个用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;
}
Run Code Online (Sandbox Code Playgroud)

}

这是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]; 
};
Run Code Online (Sandbox Code Playgroud)

这是我在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;      
};
Run Code Online (Sandbox Code Playgroud)

这是我调用方法的代码(函数)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

并且我收到以下错误:托管调试助手'PInvokeStackImbalance'在'\ bin\Debug\FCFake.vshost.exe'中检测到问题.

附加信息:对PInvoke函数'Program :: GetCardInfoEx'的调用使堆栈失衡.这很可能是因为托管PInvoke签名与非托管目标签名不匹配.检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配.

如何解决这个问题?

UPD:我根据Dirk的回答编辑了TCardInfoEx结构.不幸的是,它没有帮助我

Dir*_*irk 5

快速浏览一下,我会说问题是你尝试映射到C#字符串的C++结构中的固定大小的char数组.

当你使用这样一个固定大小的数组时,它会嵌入到struct本身中,而不是使用指针,而指针只会将指针添加到结构而不是它指向的数据.

您可以修改C#结构,以便.NET知道如何使用MarshalAs-attribute 将其映射到本机世界:

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
string  OtherCardInformation;
Run Code Online (Sandbox Code Playgroud)

这适用于.NET字符串到C++ char数组的所有映射.

您可能还必须使用设置结构的字符编码

[StructLayout(CharSet = CharSet.Ansi)]
public struct TCardInfoEx
Run Code Online (Sandbox Code Playgroud)

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


您已指定包值(#pragma pack(1)).这也必须转换为.NET世界使用

[StructLayout(CharSet = CharSet.Ansi, Pack = 1)]
public struct TCardInfoEx
Run Code Online (Sandbox Code Playgroud)

最后你的签名不匹配:

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
Run Code Online (Sandbox Code Playgroud)

在.NET中,结构是按值传递的,因此必须通过引用传递它才能使签名匹配.

将C++映射char *到.NET类型始终取决于上下文.有时这样的指针用作输入字符串,有时用于任意输入数据,当然它也可以只是单个字符输出参数.


并且(希望最终)您的呼叫约定不匹配.默认调用约定DllImportStdCall在您的函数使用时__cdecl.所以你必须将它添加到你的DllImport属性中

[DllImport(..., CallingConvetion = CallingConvention.Cdecl, ...)]
Run Code Online (Sandbox Code Playgroud)

感谢@DavidHeffernan,这篇文章从它的原始版本扩展了很多,其中我只讨论了固定的数组大小问题.