Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which way of marshaling is correct for this C code?

Tags:

c

c#

marshalling

I have two different implementation of marshaling C code into C#.

First of all the C code is:

int GetVersion(char * pVer, size_t * piSize);

My first attempt in C#(I don't show the DllImport part):

static extern int GetVersion( byte[] ver, ref UIntPtr size );

And the other attempt in C#:

static extern int GetVersion([MarshalAs(UnmanagedType.LPStr)] StringBuilder ver, ref ulong size);

I came through these from different examples in net. But I cannot conclude which one is correct.

like image 701
GNZ Avatar asked Nov 16 '25 14:11

GNZ


1 Answers

Bare bones: we can always do this

static extern int GetVersion(IntPtr ver, ref UIntPtr size );

I would never write this because I don't like what this generates for A->W conversion:

static extern int GetVersion([MarshalAs(UnmanagedType.LPStr)] StringBuilder ver, ref UIntPtr size);

Also, if this is a true counted string, the P/Invoke code doesn't work as it wants a 0 after the end.

I would normally write

static extern int GetVersion( byte[] ver, ref UIntPtr size );

in which you must first create ver at the maximum size. You can then convert ver to string using the stuff in System.Text.Encoding. GetVersion() almost certainly passes a constant back, so you can hardcode the correct encoding (probably ASCII anyway).

There's a pretty good chance you're missing [DllImport(..., CallingConvention=CallingConvention.Cdecl)] causing you some memory corruption, but I can't actually tell.

Mistake: don't use ulong for size_t. size_t is UIntPtr on all supported platforms.


Comment suggests a completely different signature GetDevInfo(int Index, DevInfo * pdevInfo); This is a different thing altogether and there's clearly one best choice:

const int IDLength = 100;
[StructLayout(LayoutKind.Sequential)]
struct DeviceInfo {
   [MarshalAs(UnmanagedType.ByValArray, SizeConst = IDLength)]
   byte[] ID;
}

static extern /* I don't know what's here because missing from comment */
    GetDevInfo(int index, ref strcut DeviceInfo info);

string GetInfo(int index)
{
    var info = new DeviceInfo();
    info.ID = new byte[IDLength];
    GetDevInfo(info.ID);
    return System.Text.Encoding.ASCII.GetString(info.ID, 0, strlen(info.ID));
}

int strlen(byte[] bytes)
{
    int i;
    for (i = 0; i < bytes.Length && bytes[i] != 0; i++)
        ;
    return i;
}
````
like image 105
Joshua Avatar answered Nov 19 '25 03:11

Joshua