C # P / Invoke Structure Problem

I am trying to write C # P / Invoke for the C API (native Win dll) and it usually works fine. The only exception is the specific method that takes the structure as a parameter in the C code. The function is called without any exceptions, but returns false, indicating that something failed to execute.

In the API header file, the involved method and structures are defined as follows:

#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*/); 

I tried to create a C # P / Invoke shell using the following code:

 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); 

Then I try to call the EncodeMagstripe method using the following C # code:

 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()); } 

This throws an ApplicationException, and the error code is 801, which according to the documentation means that "The data contains too many characters for the selected track format 2". However, the selected track format should contain up to 39 characters (I also tried shorter lines).

I suspect that the problem is due to the fact that I was mistaken in defining MSTrackData, but I do not see what it could be. Anyone have any suggestions?

+6
c # struct pinvoke
source share
4 answers

All answers received so far have little response, but are incomplete. You need MarshalAs - ByValArray, as well as new ones, your MSTrackDatas are already links, so you do not need to pass them by ref, and you need to check what the ICEAPI call is, if it is StdCall, you do not need to change anything except if it is cdecl , you will need to add CallingConvention to your DllImport attribute. Alternatively, you may need to add the MarshalAs attribute to your bool return value to make sure it is marshaled as a 4-byte WinApi style style. Here are the ads you (possibly) need:

 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); 
+5
source share

I would not define a BYTE array with a new one, but instead use the following code to initialize the desired size:

[MarshalAs (UnmanagedType.byValTSt, SizeConst = 256)] public readonly Byte [] TrackData;

I have successfully used this in char arrays in the past.

+2
source share

It seems like the problem is that you are passing the link by link. Since MSTrackData is a class (for example, a reference type), passing it by reference is similar to passing a pointer to a pointer.

Change the managed prototype:

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

See the MSDN article on transferring structures .

+1
source share

I had almost the same problem - but with ReadMagstripe. And the solution provided here for EncodeMagstripe does not work for ReadMagstripe! I think the reason it did not work is because ReadMagstripe should return data to the TRACKDATA structure / class, while EncodeMagstripe only passes the data to the DLL, and the data in TRACKDATA does not need to be changed. Here is the implementation that ultimately worked for me - with both EncodeMagstripe and 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); } } 
0
source share

All Articles