问题
I'm trying to get the display names as they appear in the "Screen Resolution" window (Win8.1 x64).
First I tried EnumDisplayDevices
var deviceInfo = new DISPLAY_DEVICEW();
uint i = 0;
while (true)
{
if (!NativeMethods.EnumDisplayDevices(null, i++, deviceInfo, 0))
{
break;
}
PrintDeviceInfo(deviceInfo);
NativeMethods.EnumDisplayDevices(deviceInfo.DeviceName, 0, deviceInfo, EDD_GET_DEVICE_INTERFACE_NAME);
PrintDeviceInfo(deviceInfo);
}
The second call to EnumDisplayDevices
(with EDD_GET_DEVICE_INTERFACE_NAME
) indeed yielded the display name as it appears for my main display (in DISPLAY_DEVICEW.DeviceString). However for my HDMI-connected TV that field contains Generic PnP Monitor, instead of SAMSUNG as it appears in the "Screen Resolution" Window. Perhaps the fact that it's connected by HDMI is somehow related?
I then tried the Setup API
var hdevinfo = NativeMethods.SetupDiGetClassDevs(ref GUID_DEVINTERFACE_MONITOR, null, IntPtr.Zero, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
if (hdevinfo == INVALID_HANDLE_VALUE) return;
var spDeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
uint memberIndex = 0;
while (true)
{
bool success = NativeMethods.SetupDiEnumDeviceInterfaces(hdevinfo, null, ref GUID_DEVINTERFACE_MONITOR, memberIndex++, spDeviceInterfaceData);
if (!success)
{
break;
}
PrintInterfaceData(spDeviceInterfaceData);
uint requiredSize;
var devInfoData = new SP_DEVINFO_DATA();
NativeMethods.SetupDiGetDeviceInterfaceDetail(hdevinfo, spDeviceInterfaceData, IntPtr.Zero, 0, out requiredSize, devInfoData);
PrintDevInfoData(devInfoData);
var interfaceDetail = Marshal.AllocHGlobal((int)requiredSize);
var cbSize = (Marshal.SizeOf(typeof(uint)) + Marshal.SystemDefaultCharSize);
Marshal.WriteInt32(interfaceDetail, 0, cbSize);
NativeMethods.SetupDiGetDeviceInterfaceDetail(hdevinfo, spDeviceInterfaceData, interfaceDetail, requiredSize, IntPtr.Zero, null);
var dynamicType = GetDeviceInterfaceDetailDataType(requiredSize);
var interfaceDetailStruct = Marshal.PtrToStructure(interfaceDetail, dynamicType);
Marshal.FreeHGlobal(interfaceDetail);
PrintInterfaceDetail(interfaceDetailStruct);
uint propertyRegDataType;
NativeMethods.SetupDiGetDeviceRegistryProperty(hdevinfo, devInfoData, SPDRP_FRIENDLYNAME, out propertyRegDataType, null, 0, out requiredSize);
Console.WriteLine(Marshal.GetLastWin32Error());
}
Looking at the returned values from the different methods, everything seems to work, but the last call to SetupDiGetDeviceRegistryProperty
fails with ERROR_INVALID_DATA
(that is, the method returns false and GetLastWin32Error
yields 13
). According to the docs, this means that the requested property does not exist for a device or if the property data is not valid.
I actually looped over all possible SPDRP
values (0-24) and they all result in the same failure. Just to clarify, I expected the method to fail, but with ERROR_INSUFFICIENT_BUFFER
and having requiredSize
set (the latter simply retains its previous value, as the unmanaged code doesn't change it).
Here's the signature for SetupDiGetDeviceRegistryProperty
(all other methods work as expected):
[StructLayout(LayoutKind.Sequential)]
public class SP_DEVINFO_DATA
{
public uint cbSize = (uint) Marshal.SizeOf(typeof (SP_DEVINFO_DATA));
public Guid ClassGuid;
public uint DevInst;
public IntPtr Reserved;
}
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetupDiGetDeviceRegistryProperty(
IntPtr deviceInfoSet,
SP_DEVINFO_DATA deviceInfoData,
uint property,
out uint propertyRegDataType,
byte[] propertyBuffer,
uint propertyBufferSize,
out uint requiredSize);
回答1:
Looks like what I'm looking for is impossible:
There is not a supported way to figour out the IDs that you referred to programmatically. It was never a design goal to provide a way for applications to label monitors with the same IDs that the screen resolution control panel uses
来源:https://stackoverflow.com/questions/24356289/setupdigetdeviceregistryproperty-fails-with-error-invalid-data