1

Good morning, I'm tring to implement a pos device. The native api is a C++ lib, that I have to use in my C# project. I already imported almost all the functions required (working fine), but there is one of them that I cannot do it anyway.

This is the function into the C++ lib:

__declspec(dllexport) void setHost(char* id, Host* host, char* trx, char* flag);

This is the struct declared into the C++ lib:

typedef struct {
   int hostConnectionType;
   char ipAddress[15 + 1];
   int tcpPort;
   int hostTransportProtocol;
   char gtId[5 + 1];
   char gprsAPN[64 + 1];
   char gprsUserName[15 + 1];
   char gprsPassword[8 + 1];
   int tlsCertificateId;
   char personalizationId[10 + 1];
   int gtIndex;
} Host;

And this is the usage sample in C++ demo project:

Host test_host;
test_host.hostConnectionType = 3;
test_host.hostTransportProtocol = 2;
test_host.gtIndex = 9;
strcpy(test_host.ipAddress, "199.188.177.166");
test_host.tcpPort = 54321;
strcpy(test_host.gtId, "12345");
test_host.tlsCertificateId = 21985;
strcpy(test_host.personalizationId, "001");
MobilePosAdapter_dll("00000123", &test_host, "0000000001", "1000");

What I done in my C# project is declaring the Host struct:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Host
{
    public int hostConnectionType;

    public int hostTransportProtocol;

    public int tcpPort;

    public int tlsCertificateId;

    public int gtIndex;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    public string ipAddress;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
    public string gtId;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 65)]
    public string gprsAPN;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
    public string gprsUserName;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 9)]
    public string gprsPassword;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 11)]
    public string personalizationId;
}

Then, declaring the function, attributes and sign:

[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private delegate void cdecl__setHost([MarshalAs(UnmanagedType.LPStr)] string id, Host Host, [MarshalAs(UnmanagedType.LPStr)] string trx, [MarshalAs(UnmanagedType.LPStr)] string flags);

private static cdecl__setHost _setHost = (cdecl__setHost)Marshal.GetDelegateForFunctionPointer(NativeMethods.GetProcAddress(DllPtr, "setHost"), typeof(cdecl__setHost));

Finally test it:

Host host = new Host()
{
    gtId = 12345,
    gtIndex = 0,
    hostConnectionType = 3,
    hostTransportProtocol = 2,
    ipAddress = "199.188.177.166",
    personalizationId = "001",
    tcpPort = 54321,
    tlsCertificateId = 21985
};

_setHost("00000123", host, "0000000001", "1000");

I got a System.AccessViolationException.

What I wrong?

This is the only function in the entire API it has a struct as parameter. The others functions has struct as value return, and they works all fine.

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
  • The order of the fields is not the same, maybe that is the problem – Ackdari Feb 08 '21 at 10:39
  • You might also try the [`FieldOffset`](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.fieldoffsetattribute?view=net-5.0) attribute to explicit the order of the fields. – Ackdari Feb 08 '21 at 10:43
  • `char[]` should be `ByValTStr` not `LPStr`, your fields are in the wrong order, and your `SizeConst` are not right either – Charlieface Feb 08 '21 at 10:53

1 Answers1

0

Just a quick guess

On the unmanaged side, the parameter type is Host* and in C++ you're passing &test_host.

However, on the C# side, you have a struct (a thing that is by default pass by-a-copy, so by-val) and you're passing just host to a function with declared params of:

private delegate
    void cdecl __setHost(
        [MarshalAs(UnmanagedType.LPStr)] string id,
        Host host,    // <--------- HERE
        [MarshalAs(UnmanagedType.LPStr)] string trx,
        [MarshalAs(UnmanagedType.LPStr)] string flags
    );

Note that you have not added any MarshalAs to the host parameter, so I suppose it will be passed by-copy, and some damage will occur, as the receiving function expects to get a pointer (few bytes) not the whole structure (few times more).

First thing I'd try is adding ANY hint that you want to pass it as a pointer, for example:

private delegate
    void cdecl __setHost(
        [MarshalAs(UnmanagedType.LPStr)] string id,
        ref Host host,    // <--------- HERE
        [MarshalAs(UnmanagedType.LPStr)] string trx,
        [MarshalAs(UnmanagedType.LPStr)] string flags
    );

and call it like

_setHost("00000123", ref host, "0000000001", "1000");

Also, be sure to double-check what Ackdari said in comments - if the field order differs between what C# provides and what native code expected, that may also result in the same exception..

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
  • 1
    `char[]` should be `ByValTStr` not `LPStr` – Charlieface Feb 08 '21 at 10:52
  • doing `...[MarshalAs(UnmanagedType.ByValTStr)] string id,...` the intellisense of VS got me this error: **The unmanaged type 'ByValTStr' is valid only for fields** – Andrea Fausti Feb 08 '21 at 11:56
  • @AndreaFausti Sorry I was referring to the struct which is `char[]`, you're right the delegate is `char*` which is `LPStr` – Charlieface Feb 08 '21 at 13:29
  • 1
    The solution was both suggestions: **`ref`** in the delegate declaration; **"parameters order"** in the struct declaration. Thank you very much!! – Andrea Fausti Feb 08 '21 at 14:21