C
C#ā€¢16mo 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;
}
[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;
}
[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);
}
[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
14 Replies
Kouhai
Kouhaiā€¢16mo ago
Yes NodeListValue should be a ptr, nuint
Sekoree
SekoreeOPā€¢16mo ago
Ah hecc, thank you, is there any specific reason it has to be an unsigned one? Or are there any docs I could read about it?
Kouhai
Kouhaiā€¢16mo ago
Afaik not really, you can use nint as well
Sekoree
SekoreeOPā€¢16mo ago
It worked woohfast and yea, I just used nint/IntPtr now cause thats what the Marshal class gives me Thank you so much for your help!
Kouhai
Kouhaiā€¢16mo ago
<:MadoThumbsUp_MM:406514447973351444>
Unknown User
Unknown Userā€¢16mo ago
Message Not Public
Sign In & Join Server To View
MODiX
MODiXā€¢16mo ago
Use the /close command to mark a forum thread as answered
Unknown User
Unknown Userā€¢16mo ago
Message Not Public
Sign In & Join Server To View
Kouhai
Kouhaiā€¢16mo ago
?
Unknown User
Unknown Userā€¢16mo ago
Message Not Public
Sign In & Join Server To View
Kouhai
Kouhaiā€¢16mo ago
I didn't create the thread šŸ˜…
Unknown User
Unknown Userā€¢16mo ago
Message Not Public
Sign In & Join Server To View
MODiX
MODiXā€¢16mo ago
Use the /close command to mark a forum thread as answered
Kouhai
Kouhaiā€¢16mo ago
ThumbsUpSmile
Want results from more Discord servers?
Add your server