C#C
C#3y ago
Sekoree

✅ P/Invoke, changing values in a struct overwrites overwrites other values

Heyo!
I'm working on a P/Invoke library for libMPV and it has a way to send it structured data instead of just strings as commands, but I'm kind of stuck at the struct layout.
The root looks like this (it has a union so there is a bunch of values at offset 0)
[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct MPVNode
{
    [FieldOffset(0)] 
    public nint StringValue;
    [FieldOffset(0)]
    public int YesNoFlag;
    [FieldOffset(0)] 
    public long IntegerValue;
    [FieldOffset(0)] 
    public double DoubleValue;
    [FieldOffset(0)] 
    public MPVNodeList NodeListValue;
    [FieldOffset(0)] 
    public MPVByteArray ByteArrayValue;
    [FieldOffset(8)] 
    public MPVFormat Format;
}

To send commands I have to sent it a NodeList (in array or map/dictionary form, I'm using array here so the keys value is unused)
[StructLayout(LayoutKind.Explicit, Size = 24)]
public struct MPVNodeList
{
    [FieldOffset(0)]
    public int Num;
    [FieldOffset(8)]
    public IntPtr Nodes;
    [FieldOffset(16)]
    public IntPtr Keys;
}

I'm calling it method via:
[LibraryImport("libmpv-2", EntryPoint = "mpv_command_node", StringMarshalling = StringMarshalling.Utf8)]
public static extern MPVError MPVCommandNode(MPVHandle ctx, ref MPVNode args, out MPVNode result);

public void Test()
{
    var loadNode = new MPVNode()
    {
        Format = MPVFormat.String,
        StringValue = Marshal.StringToCoTaskMemUTF8("loadfile\0")
    };
    var dataNode = new MPVNode()
    {
        Format = MPVFormat.String,
        StringValue = Marshal.StringToCoTaskMemUTF8("<Path to a video file for now>\0")
    };
    var arr = new[] { loadNode, dataNode };
    var ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf<MPVNode>() * arr.Length);
    for (int i = 0; i < arr.Length; i++) 
        Marshal.StructureToPtr(arr[i], ptr + i * Marshal.SizeOf<MPVNode>(), false);

    var arrayNode = new MPVNode()
    {
        Format = MPVFormat.NodeArray,
        NodeListValue = new MPVNodeList()
        {
           Num = 2,
           Nodes = ptr
        }
    };
    Interop.MPVCommandNode(_handle, ref arrayNode, out var result);
}

The struct in the header file looks like this:
https://github.com/mpv-player/mpv/blob/ce7997649816e4d6c05071fbd4ecac0557120720/libmpv/client.h#L750
And the method is here:
https://github.com/mpv-player/mpv/blob/ce7997649816e4d6c05071fbd4ecac0557120720/libmpv/client.h#L930
I noticed that the Format value in
arrayNode
changes when
NodeListValue
is set, so I believe some value has the wrong size and data in the struct gets overwritten? And thats probably what breaks it, I'd assume something is wrong with my struct, but I just cant seem to find what, I looked at other (C#) libraries but they either implement it the same as me afaik or just skip over that method
Should
NodeListValue
just be an
IntPtr
or something like that?
My results vary between getting
Invalid Parameter
as error from the lib or outright corrupt memory exceptions
Was this page helpful?