Help with LibraryImport and custom marshalling

What am I doing wrong here? I am getting a System.AccessViolationException when it tries to convert the first string (DeviceName) 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);
    }
}



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;
}
Was this page helpful?