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

unable to invoke function of dll written in c++ from c#

本文关键字:dll 函数 c++ 调用      更新时间:2023-10-16

我需要从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;      
};

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

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-属性将其映射到本地世界:

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

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

您可能还需要使用设置结构的字符编码

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

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


您已经指定了一个包值(#pragma pack(1))。还必须使用将其翻译成.NET世界

[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,这篇文章从最初的版本扩展了很多,在最初的版本中,我只讨论了固定数组大小的问题。