1

The MSDN documentation for EnumDisplayDevices states that passing NULL as the first parameter to the function returns information about the display adaptor(s) on the machine (passing a string returns information about the device with that name).

Various C# examples that I have seen on-line pass null to the function as follows:

result = EnumDisplayDevices(null, devNum, ref dd, flags);

However, when I pass null as the first parameter, I get a System.AccessViolationException with the message "Attempted to read or write protected memory".

If I change null to any random non-null string (eg, "hello"), then the function call succeeds (I just don't get any device information because there isn't a device called "hello").

So how do I pass null as the first parameter to the EnumDisplayDevices function? (I need to be able to pass names in subsequent calls to the function)

Relevant snippets of my code follow:

    [DllImport("user32.dll")]
    static extern bool EnumDisplayDevices(
        string lpDevice,
        uint iDevNum,
        ref DISPLAY_DEVICE lpDisplayDevice,
        uint dwFlags
        );

    [StructLayout(LayoutKind.Sequential)]
    public struct DISPLAY_DEVICE
    {
        public int cb;
        public string DeviceName;
        public string DeviceString;
        public int StateFlags;
        public string DeviceID;
        public string DeviceKey;
    }

    #region Public Interface
    public ObservableCollection<DisplayDevice> LoadDisplayDevices()
    {
        ObservableCollection<DisplayDevice> displayDevices = new ObservableCollection<DisplayDevice>();

        uint devNum = 0;
        uint flags = 0;
        bool result = false;

        DISPLAY_DEVICE dd = new DISPLAY_DEVICE();
        dd.cb = (int)Marshal.SizeOf(dd);

        try
        {
            result = EnumDisplayDevices(null, devNum, ref dd, flags);

            ...
John Saunders
  • 160,644
  • 26
  • 247
  • 397

1 Answers1

2

Original definition from MSDN:

typedef struct _DISPLAY_DEVICE {
  DWORD cb;
  TCHAR DeviceName[32];
  TCHAR DeviceString[128];
  DWORD StateFlags;
  TCHAR DeviceID[128];
  TCHAR DeviceKey[128];
} DISPLAY_DEVICE, *PDISPLAY_DEVICE;

All string fields are defined as fixed-size arrays. Your DISPLAY_DEVICE structure definition contains multiple string values without instructions how to marshal them, so they will be passed as pointers. You'll need to use MarshalAsAttribute to fix this:

[StructLayout(LayoutKind.Sequential)]
public struct DISPLAY_DEVICE
{
    public int cb;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string DeviceName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string DeviceString;
    public int StateFlags;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string DeviceID;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
    public string DeviceKey;
}
max
  • 33,369
  • 7
  • 73
  • 84
  • Beautiful, fantastic! Your answer makes so much sense that I'm somewhat embarrassed that I hadn't thought of that. Very much appreciated. – Harry Smith Mar 17 '15 at 04:03