Help with LibraryImport and custom marshalling
What am I doing wrong here? I am getting a
Calling code:
System.AccessViolationExceptionSystem.AccessViolationException when it tries to convert the first stringstring (DeviceNameDeviceName) from unmanaged to managed.[LibraryImport("user32.dll", EntryPoint = "EnumDisplayDevicesW", StringMarshalling = StringMarshalling.Utf16)]
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool EnumDisplayDevices(string? lpDevice, uint iDevNum, [MarshalUsing(typeof(DISPLAY_DEVICEMarshaller))] ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DISPLAY_DEVICE
{
[MarshalAs(UnmanagedType.U4)]
public uint cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string? DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string? DeviceString;
[MarshalAs(UnmanagedType.U4)]
public DisplayDeviceStateFlags StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string? DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string? DeviceKey;
public static DISPLAY_DEVICE Create() => new DISPLAY_DEVICE { cb = (uint)Marshal.SizeOf<DISPLAY_DEVICE>() };
}
[Flags]
public enum DisplayDeviceStateFlags : uint
{
AttachedToDesktop = 0x1,
MultiDriver = 0x2,
PrimaryDevice = 0x4,
MirroringDriver = 0x8,
VGACompatible = 0x10,
Removable = 0x20,
ModesPruned = 0x8000000,
Remote = 0x4000000,
Disconnect = 0x2000000
}
[CustomMarshaller(typeof(DISPLAY_DEVICE), MarshalMode.ManagedToUnmanagedRef, typeof(DISPLAY_DEVICEMarshaller))]
public static unsafe class DISPLAY_DEVICEMarshaller
{
internal struct DISPLAY_DEVICE_Unmanaged
{
public uint cb;
public ushort* DeviceName;
public ushort* DeviceString;
public uint StateFlags;
public ushort* DeviceID;
public ushort* DeviceKey;
}
public static DISPLAY_DEVICE ConvertToManaged(DISPLAY_DEVICE_Unmanaged unmanaged)
{
return new DISPLAY_DEVICE
{
cb = unmanaged.cb,
DeviceName = Utf16StringMarshaller.ConvertToManaged(unmanaged.DeviceName),
DeviceString = Utf16StringMarshaller.ConvertToManaged(unmanaged.DeviceString),
StateFlags = (DisplayDeviceStateFlags)unmanaged.StateFlags,
DeviceID = Utf16StringMarshaller.ConvertToManaged(unmanaged.DeviceID),
DeviceKey = Utf16StringMarshaller.ConvertToManaged(unmanaged.DeviceKey)
};
}
public static DISPLAY_DEVICE_Unmanaged ConvertToUnmanaged(DISPLAY_DEVICE managed)
{
return new DISPLAY_DEVICE_Unmanaged
{
cb = managed.cb,
DeviceName = Utf16StringMarshaller.ConvertToUnmanaged(managed.DeviceName),
DeviceString = Utf16StringMarshaller.ConvertToUnmanaged(managed.DeviceString),
StateFlags = (uint)managed.StateFlags,
DeviceID = Utf16StringMarshaller.ConvertToUnmanaged(managed.DeviceID),
DeviceKey = Utf16StringMarshaller.ConvertToUnmanaged(managed.DeviceKey)
};
}
public static unsafe void Free(DISPLAY_DEVICE_Unmanaged unmanaged)
{
Utf16StringMarshaller.Free(unmanaged.DeviceName);
Utf16StringMarshaller.Free(unmanaged.DeviceString);
Utf16StringMarshaller.Free(unmanaged.DeviceID);
Utf16StringMarshaller.Free(unmanaged.DeviceKey);
}
}[LibraryImport("user32.dll", EntryPoint = "EnumDisplayDevicesW", StringMarshalling = StringMarshalling.Utf16)]
[return: MarshalAs(UnmanagedType.Bool)]
public static partial bool EnumDisplayDevices(string? lpDevice, uint iDevNum, [MarshalUsing(typeof(DISPLAY_DEVICEMarshaller))] ref DISPLAY_DEVICE lpDisplayDevice, uint dwFlags);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DISPLAY_DEVICE
{
[MarshalAs(UnmanagedType.U4)]
public uint cb;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string? DeviceName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string? DeviceString;
[MarshalAs(UnmanagedType.U4)]
public DisplayDeviceStateFlags StateFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string? DeviceID;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
public string? DeviceKey;
public static DISPLAY_DEVICE Create() => new DISPLAY_DEVICE { cb = (uint)Marshal.SizeOf<DISPLAY_DEVICE>() };
}
[Flags]
public enum DisplayDeviceStateFlags : uint
{
AttachedToDesktop = 0x1,
MultiDriver = 0x2,
PrimaryDevice = 0x4,
MirroringDriver = 0x8,
VGACompatible = 0x10,
Removable = 0x20,
ModesPruned = 0x8000000,
Remote = 0x4000000,
Disconnect = 0x2000000
}
[CustomMarshaller(typeof(DISPLAY_DEVICE), MarshalMode.ManagedToUnmanagedRef, typeof(DISPLAY_DEVICEMarshaller))]
public static unsafe class DISPLAY_DEVICEMarshaller
{
internal struct DISPLAY_DEVICE_Unmanaged
{
public uint cb;
public ushort* DeviceName;
public ushort* DeviceString;
public uint StateFlags;
public ushort* DeviceID;
public ushort* DeviceKey;
}
public static DISPLAY_DEVICE ConvertToManaged(DISPLAY_DEVICE_Unmanaged unmanaged)
{
return new DISPLAY_DEVICE
{
cb = unmanaged.cb,
DeviceName = Utf16StringMarshaller.ConvertToManaged(unmanaged.DeviceName),
DeviceString = Utf16StringMarshaller.ConvertToManaged(unmanaged.DeviceString),
StateFlags = (DisplayDeviceStateFlags)unmanaged.StateFlags,
DeviceID = Utf16StringMarshaller.ConvertToManaged(unmanaged.DeviceID),
DeviceKey = Utf16StringMarshaller.ConvertToManaged(unmanaged.DeviceKey)
};
}
public static DISPLAY_DEVICE_Unmanaged ConvertToUnmanaged(DISPLAY_DEVICE managed)
{
return new DISPLAY_DEVICE_Unmanaged
{
cb = managed.cb,
DeviceName = Utf16StringMarshaller.ConvertToUnmanaged(managed.DeviceName),
DeviceString = Utf16StringMarshaller.ConvertToUnmanaged(managed.DeviceString),
StateFlags = (uint)managed.StateFlags,
DeviceID = Utf16StringMarshaller.ConvertToUnmanaged(managed.DeviceID),
DeviceKey = Utf16StringMarshaller.ConvertToUnmanaged(managed.DeviceKey)
};
}
public static unsafe void Free(DISPLAY_DEVICE_Unmanaged unmanaged)
{
Utf16StringMarshaller.Free(unmanaged.DeviceName);
Utf16StringMarshaller.Free(unmanaged.DeviceString);
Utf16StringMarshaller.Free(unmanaged.DeviceID);
Utf16StringMarshaller.Free(unmanaged.DeviceKey);
}
}Calling code:
public static DISPLAY_DEVICE GetPrimaryDisplay()
{
uint id = 0;
while (true)
{
DISPLAY_DEVICE d = DISPLAY_DEVICE.Create();
bool done = EnumDisplayDevices(null, id, ref d, 0);
if ((d.StateFlags & DisplayDeviceStateFlags.PrimaryDevice) == DisplayDeviceStateFlags.PrimaryDevice)
{
return d;
}
if (done)
break;
id++;
}
return default;
}public static DISPLAY_DEVICE GetPrimaryDisplay()
{
uint id = 0;
while (true)
{
DISPLAY_DEVICE d = DISPLAY_DEVICE.Create();
bool done = EnumDisplayDevices(null, id, ref d, 0);
if ((d.StateFlags & DisplayDeviceStateFlags.PrimaryDevice) == DisplayDeviceStateFlags.PrimaryDevice)
{
return d;
}
if (done)
break;
id++;
}
return default;
}