Home > OS >  Memory Access Violation from a Delphi written DLL. Trying to access through DLLImport
Memory Access Violation from a Delphi written DLL. Trying to access through DLLImport

Time:12-10

I need to work with a Delphi written DLL provided by a hardware vendor. In the document provided, it mentioned the below

int ReadCard(char *room, char *gate,char *stime, char *guestname, char *guestid, char *lift, char *track1, char *track2, long *cardno, int *st, int *Breakfast);

Further elaboration of the parameters are given as below:

  • room [out]: character string pointer, receive returned room number, 10 bytes recommended.

  • gate [out]: character string pointer, receive returned authorized common gates, can be NULL.

  • Guestname [out]: character string pointer, receive returned guest name, can be NULL.

  • Guestid [out]: character string pointer, receive returned guest ID, can be NULL.

  • Lift [in]:Lift floors, string parameter, “00” stands for default floor, “99”stands for authorization for all floors, and others are specified floor codes, e.g., “010205” stands for authorization for three floors 01,02,05.

  • track1 [out]: receive track 1 data of magnetic card, can be NULL.

  • track2 [out]: receive track 2 data of magnetic card, can be NULL.

  • Cardno [out]: long integer pointer, receive returned card number, can be NULL.

  • St [out]: integer pointer, receive returned card status: 1-normally used, 3-normally erased, 4-lost, 5-damaged, 6-automatically erased, can be NULL. Breakfast

  • [in]: Breakfast number.

Here's my problem - I keep getting memory access violation errors after trying all kinds of ways to DLLImport the method.

The error reads like "System.AccessViolationException: Attempted to read or write protected memory This is often an indication that other memory is corrupt." Error is thrown at ntdll.dll

Below are some of the attempts I've tried:

    [DllImport(DLL_FILE_PATH, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public unsafe static extern int ReadCard(
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder room,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder gate,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder stayPeriod,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder guestName,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder guestID,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder lift,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder trackData1,
        [MarshalAs(UnmanagedType.LPStr)] StringBuilder trackData2,
        out Int32[] cardNumber,
        out int[] cardStatus,
        out int[] breakfast
    );
    [DllImport(DLL_FILE_PATH, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public unsafe static extern int ReadCard(
        [MarshalAs(UnmanagedType.LPStr)] string room,
        [MarshalAs(UnmanagedType.LPStr)] string gate,
        [MarshalAs(UnmanagedType.LPStr)] string stayPeriod,
        [MarshalAs(UnmanagedType.LPStr)] string guestName,
        [MarshalAs(UnmanagedType.LPStr)] string guestID,
        [MarshalAs(UnmanagedType.LPStr)] string lift,
        [MarshalAs(UnmanagedType.LPStr)] string trackData1,
        [MarshalAs(UnmanagedType.LPStr)] string trackData2,
        out long cardNumber,
        out long cardStatus,
        out long breakfast
    );

        [DllImport(DLL_FILE_PATH, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public unsafe static extern int ReadCard(
        out char[] room,
        out char[] gate,
        out char[] stayPeriod,
        out char[] guestName,
        out char[] guestID,
        out char[] lift,
        out char[] trackData1,
        out char[] trackData2,
        out long[] cardNumber,
        out int[] cardStatus,
        out int[] breakfast
    );

    [DllImport(DLL_FILE_PATH, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
    public unsafe static extern int ReadCard(
        [MarshalAs(UnmanagedType.LPStr)] out string room,
        [MarshalAs(UnmanagedType.LPStr)] out string gate,
        [MarshalAs(UnmanagedType.LPStr)] out string stayPeriod,
        [MarshalAs(UnmanagedType.LPStr)] out string guestName,
        [MarshalAs(UnmanagedType.LPStr)] out string guestID,
        [MarshalAs(UnmanagedType.LPStr)] out string lift,
        [MarshalAs(UnmanagedType.LPStr)] out string trackData1,
        [MarshalAs(UnmanagedType.LPStr)] out string trackData2,
        IntPtr cardNumber,
        IntPtr cardStatus,
        IntPtr breakfast
    );

Any idea where did I missed?

CodePudding user response:

You can't use string for output parameters that expect pointers to char[] buffers. You need to use StringBuilder instead, per Microsoft's documentation: Default Marshaling for Strings: Fixed-Length String Buffers. For input parameters, string will work fine.

In C#, using long (ie Int64) for int (32bit integer) parameters in C/C and Delphi is wrong. You need to use int (ie Int32) instead. On the other hand,long in C/C may be either 32bit or 64bit, depending on platform, so you will have to check what the DLL is actually using. Chances are, it will probably be 32bit, assuming Windows.

But, why are you declaring the cardNumber, cardStatus, and breakfast parameters as arrays? Nothing in the documentation or C/C declaration suggests they are anything but single integers. cardNumber and cardStatus should be out int (or maybe out long for cardNumber), or IntPtr if you ever pass in null for them. breakfast is input only, so it should be simply int.

Try something more like this:

[DllImport(DLL_FILE_PATH, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern int ReadCard(
    StringBuilder room,
    StringBuilder gate,
    StringBuilder stayPeriod,
    StringBuilder guestName,
    StringBuilder guestID,
    string lift,
    StringBuilder trackData1,
    StringBuilder trackData2,
    out int cardNumber,
    out int cardStatus,
    int breakfast
);

When calling this from C#, just be sure to pre-allocate the StringBuilder parameters to the desired lengths beforehand, so the DLL has adequate memory to write to. For example:

StringBuilder roomSB = new StringBuilder(10);
ReadCard(roomSB, ...);
string room = roomSB.ToString();
  • Related