C# P/Invoke structure problem

后端 未结 4 502
[愿得一人]
[愿得一人] 2021-01-12 18:55

I am trying to write a C# P/Invoke wrapper for a C API (a native Win dll), and generally this is working fine. The only exception is a specific method which takes a struct a

相关标签:
4条回答
  • 2021-01-12 19:01

    Looks to me like the problem is that you're passing a reference by reference. Since MSTrackData is a class (i.e. reference type), passing it by reference is like passing a pointer-to-pointer.

    Change your managed prototype to:

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

    See the MSDN article about passing structures.

    0 讨论(0)
  • 2021-01-12 19:06

    All the answers given so far have a bit of the answer but are incomplete. You need the MarshalAs - ByValArray as well as the new, your MSTrackDatas are already references so you do not need to pass them by ref and you must check what calling convention ICEAPI represents, if it is StdCall you don't need to change anything but if it is cdecl you will need to add the CallingConvention to your DllImport attribute. Also, you may need to add a MarshalAs attribute to your bool return value to make sure it is marshaled as 4 byte WinApi style bool. Here are the declares you'll (probably) 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);
    
    0 讨论(0)
  • 2021-01-12 19:07

    I had almost exactly the same problem - but with ReadMagstripe. And the solution provided here for EncodeMagstripe did not work for ReadMagstripe! I think the reason it did not work was that ReadMagstripe has to return data into the TRACKDATA structure/class, while EncodeMagstripe only passes data to the dll and data in TRACKDATA does not need to be changed. Here is the implementation that eventually worked for me - both with 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 讨论(0)
  • 2021-01-12 19:20

    I would define the BYTE array not with new, but use the following code instead to initialise the right size:

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

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

    0 讨论(0)
提交回复
热议问题