我正在尝试为C API(本机Win dll)编写C#P / Invoke包装器,通常这很正常。唯一的例外是将结构作为C代码中的参数的特定方法。该函数在没有任何异常的情况下被调用,但它返回false,表示执行中的某些内容失败。
在API头文件中,涉及的方法和结构定义如下:
#define MAX_ICE_MS_TRACK_LENGTH 256
typedef struct tagTRACKDATA
{
UINT nLength;
BYTE TrackData[MAX_ICE_MS_TRACK_LENGTH];
} TRACKDATA, FAR* LPTRACKDATA;
typedef const LPTRACKDATA LPCTRACKDATA;
BOOL ICEAPI EncodeMagstripe(HDC /*hDC*/,
LPCTRACKDATA /*pTrack1*/,
LPCTRACKDATA /*pTrack2*/,
LPCTRACKDATA /*pTrack3*/,
LPCTRACKDATA /*reserved*/);
我尝试使用以下代码创建C#P / Invoke包装器:
public const int MAX_ICE_MS_TRACK_LENGTH = 256;
[StructLayout(LayoutKind.Sequential)]
public class MSTrackData {
public UInt32 nLength;
public readonly Byte[] TrackData = new byte[MAX_ICE_MS_TRACK_LENGTH];
}
[DllImport("ICE_API.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool EncodeMagstripe(IntPtr hDC,
[In]ref MSTrackData pTrack1,
[In]ref MSTrackData pTrack2,
[In]ref MSTrackData pTrack3,
[In]ref MSTrackData reserved);
然后我尝试使用以下C#代码调用EncodeMagstripe方法:
CardApi.MSTrackData trackNull = null;
CardApi.MSTrackData track2 = new CardApi.TrackData();
byte[] trackBytes = Encoding.ASCII.GetBytes(";0123456789?");
track2.nLength = (uint)trackBytes.Length;
Buffer.BlockCopy(trackBytes, 0, track2.TrackData, 0, trackBytes.Length);
if (!CardApi.EncodeMagstripe(hDC, ref trackNull, ref track2, ref trackNull, ref trackNull)) {
throw new ApplicationException("EncodeMagstripe failed", Marshal.GetLastWin32Error());
}
这会导致抛出ApplicationException,错误代码为801,根据文档的含义,“数据包含所选Track 2格式的太多字符”。但是,所选的曲目格式最多允许39个字符(我也尝试过较短的字符串)。
我怀疑问题是由于我在MSTrackData定义中做错了,但我看不出这可能是什么。有没有人有任何建议?
答案 0 :(得分:5)
到目前为止给出的所有答案都有一些答案,但不完整。你需要MarshalAs - ByValArray以及新的,你的MSTrackDatas已经是引用所以你不需要通过ref传递它们你必须检查ICEAPI代表什么样的调用约定,如果它是StdCall你不需要改变任何东西但是如果是cdecl,则需要将CallingConvention添加到DllImport属性中。此外,您可能需要将MarshalAs属性添加到bool返回值,以确保它被封送为4字节WinApi样式bool。以下是您(可能)需要的声明:
public const int MAX_ICE_MS_TRACK_LENGTH = 256;
[StructLayout(LayoutKind.Sequential)]
public class MSTrackData {
public UInt32 nLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public Byte[] TrackData = new byte[MAX_ICE_MS_TRACK_LENGTH];
}
[DllImport("ICE_API.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EncodeMagstripe(IntPtr hDC,
[In] MSTrackData pTrack1,
[In] MSTrackData pTrack2,
[In] MSTrackData pTrack3,
[In] MSTrackData reserved);
答案 1 :(得分:2)
我将使用new定义BYTE数组,但使用以下代码来初始化正确的大小:
[MarshalAs(UnmanagedType.byValTSt,SizeConst = 256)] public readonly Byte [] TrackData;
我过去在char数组上成功使用过它。
答案 2 :(得分:1)
在我看来,问题在于您是通过引用传递引用。由于MSTrackData
是一个类(即引用类型),因此通过引用传递它就像传递一个指向指针一样。
将您的托管原型更改为:
public static extern bool EncodeMagstripe(IntPtr hDC,
MSTrackData pTrack1,
MSTrackData pTrack2,
MSTrackData pTrack3,
MSTrackData reserved);
请参阅有关passing structures的MSDN文章。
答案 3 :(得分:0)
我有几乎完全相同的问题 - 但是使用了ReadMagstripe。这里为EncodeMagstripe提供的解决方案对ReadMagstripe不起作用!我认为它不起作用的原因是ReadMagstripe必须将数据返回到TRACKDATA结构/类,而EncodeMagstripe只将数据传递给dll,而TRACKDATA中的数据不需要更改。以下是最终适用于我的实现 - 包括EncodeMagstripe和ReadMagstripe:
public const int MAX_ICE_MS_TRACK_LENGTH = 256;
[StructLayout(LayoutKind.Sequential)]
public struct TRACKDATA
{
public UInt32 nLength;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string szTrackData;
}
[DllImport("ICE_API.dll", EntryPoint="_ReadMagstripe@20", CharSet=CharSet.Auto,
CallingConvention=CallingConvention.Winapi, SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool ReadMagstripe(int hdc, ref TRACKDATA ptrack1, ref TRACKDATA ptrack2,
ref TRACKDATA ptrack3, ref TRACKDATA reserved);
[DllImport("ICE_API.dll", EntryPoint="_EncodeMagstripe@20", CharSet=CharSet.Auto,
CallingConvention = CallingConvention.Winapi, SetLastError=true)]
public static extern bool EncodeMagstripe(int hdc, [In] ref TRACKDATA ptrack1, [In] ref TRACKDATA ptrack2,
[In] ref TRACKDATA ptrack3, [In] ref TRACKDATA reserved);
/*
....
*/
private void EncodeMagstripe()
{
ICE_API.TRACKDATA track1Data = new ICE_API.TRACKDATA();
ICE_API.TRACKDATA track2Data = new ICE_API.TRACKDATA();
ICE_API.TRACKDATA track3Data = new ICE_API.TRACKDATA();
ICE_API.TRACKDATA reserved = new ICE_API.TRACKDATA();
//if read magstripe
bool bRes = ICE_API.ReadMagstripe(printer.Hdc, ref track1Data, ref track2Data,
ref track3Data, ref reserved);
//encode magstripe
if (bRes)
{
track2Data.szTrackData = "1234567890";
track2Data.nLength = 10;
bRes = ICE_API.EncodeMagstripe(printer.Hdc, ref track1Data, ref track2Data, ref track3Data, ref reserved);
}
}