我需要从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;
}
}
这是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结构。不幸的是,它没有帮助我
答案 0 :(得分:5)
快速浏览一下,我会说问题是你尝试映射到C#字符串的C ++结构中的固定大小的char数组。
当你使用这样一个固定大小的数组时,它会嵌入到struct本身中,而不是使用指针,指针只会将指针添加到结构而不是它指向的数据。
您可以修改C#结构,以便.NET知道如何使用MarshalAs
属性将其映射到本机世界:
[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中,结构是按值传递的,因此您必须通过引用传递它才能使签名匹配。
将C ++ char *
映射到.NET类型始终依赖于上下文。有时这样的指针用作输入字符串,有时用于任意输入数据,当然它也可以只是一个字符输出参数。
并且(希望最终)您的呼叫约定不匹配。当您的函数使用DllImport
时,StdCall
的默认调用约定为__cdecl
。因此,您必须将其添加到DllImport
属性中
[DllImport(..., CallingConvetion = CallingConvention.Cdecl, ...)]
感谢@DavidHeffernan,这篇文章从其原始版本扩展了很多,其中我只讨论了固定数组大小的问题。