6

我正在尝试为 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 定义中做错了什么,但我看不出这可能是什么。有没有人有什么建议?

4

4 回答 4

5

到目前为止给出的所有答案都有一些答案,但不完整。你需要 MarshalAs - ByValArray 以及新的,你的 MSTrackDatas 已经是引用,所以你不需要通过 ref 传递它们,你必须检查调用约定 ICEAPI 代表什么,如果是 StdCall,你不需要改变任何东西如果是 cdecl,则需要将 CallingConvention 添加到 DllImport 属性中。此外,您可能需要向 bool 返回值添加 MarshalAs 属性,以确保将其编组为 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);
于 2009-03-20T18:15:43.167 回答
2

我不会用 new 定义 BYTE 数组,而是使用以下代码来初始化正确的大小:

[MarshalAs(UnmanagedType.byValTSt, SizeConst =256)] 公共只读 Byte[] TrackData;

我过去曾在 char 数组上成功使用过它。

于 2009-03-17T15:35:27.490 回答
1

在我看来,问题在于您正在通过引用传递引用。由于MSTrackData是一个类(即引用类型),因此通过引用传递它就像传递一个指向指针的指针。

将您的托管原型更改为:

public static extern bool EncodeMagstripe(IntPtr hDC,
                    MSTrackData pTrack1,
                    MSTrackData pTrack2,
                    MSTrackData pTrack3,
                    MSTrackData reserved);

请参阅有关传递结构的 MSDN 文章。

于 2009-03-17T17:05:24.747 回答
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);
        }
    }
于 2009-07-31T03:55:13.283 回答