如何在Delphi VCL中获取活动显示器的准确名称?
获取准确显示器名称的解决方案
问题背景
使用EnumDisplayDevices编写的函数只能返回"Generic PnP Monitor"这类通用名称,无法获取设备管理器中显示的具体型号(如"Dell Inc. SE2717H/HX"),尝试多种方案后要么返回空值、仍为通用名,要么触发"Invalid data type for 'EDID'"运行时错误。
原实现代码:
function GetMonitorNames: TArray<string>; begin Result := []; var AdapterIndex := 0; var Adapter: TDisplayDevice; ZeroMemory(@Adapter, SizeOf(Adapter)); Adapter.cb := SizeOf(Adapter); while EnumDisplayDevices(nil, AdapterIndex, Adapter, 0) do begin const DISPLAY_ACTIVE_MONITORS = $00000001; if (Adapter.StateFlags and DISPLAY_ACTIVE_MONITORS) <> 0 then begin var MonitorIndex := 0; var Monitor: TDisplayDevice; ZeroMemory(@Monitor, SizeOf(Monitor)); Monitor.cb := SizeOf(Monitor); while EnumDisplayDevices( PChar(@Adapter.DeviceName), MonitorIndex, Monitor, 0 ) do begin if (Monitor.StateFlags and DISPLAY_ACTIVE_MONITORS) <> 0 then Result := Result + [Monitor.DeviceString]; Inc(MonitorIndex); ZeroMemory(@Monitor, SizeOf(Monitor)); Monitor.cb := SizeOf(Monitor); end; end; Inc(AdapterIndex); ZeroMemory(@Adapter, SizeOf(Adapter)); Adapter.cb := SizeOf(Adapter); end; end;
调用代码:
procedure TForm1.Button1Click(Sender: TObject); begin Memo1.Lines.Clear; Memo1.Lines.Add('Detected monitors:'); Memo1.Lines.Add('-------------------'); for var Name in GetMonitorNames do Memo1.Lines.Add(Name); end;
解决方案:使用SetupAPI获取设备友好名称
EnumDisplayDevices返回的是显示驱动层面的通用名称,要获取设备管理器中的具体名称,需要通过Windows SetupAPI读取设备的注册表属性。以下是稳定可靠的实现代码:
uses Winapi.Windows, Winapi.SetupAPI, Winapi.DevPropDef, System.Generics.Collections; function GetMonitorFriendlyNames: TArray<string>; var DeviceInfoSet: HDEVINFO; DeviceInfoData: SP_DEVINFO_DATA; DeviceIndex: DWORD; FriendlyName: array[0..255] of Char; RequiredSize: DWORD; begin Result := []; // 获取当前系统中已连接的显示器设备集合 DeviceInfoSet := SetupDiGetClassDevs(@GUID_DEVCLASS_MONITOR, nil, 0, DIGCF_PRESENT or DIGCF_DEVICEINTERFACE); if DeviceInfoSet = INVALID_HANDLE_VALUE then Exit; try DeviceIndex := 0; ZeroMemory(@DeviceInfoData, SizeOf(DeviceInfoData)); DeviceInfoData.cbSize := SizeOf(DeviceInfoData); // 枚举所有显示器设备 while SetupDiEnumDeviceInfo(DeviceInfoSet, DeviceIndex, DeviceInfoData) do begin // 优先读取设备友好名称(设备管理器中显示的名称) if SetupDiGetDeviceRegistryProperty(DeviceInfoSet, @DeviceInfoData, SPDRP_FRIENDLYNAME, nil, @FriendlyName, SizeOf(FriendlyName), @RequiredSize) then begin Result := Result + [StrPas(FriendlyName)]; end else begin // 友好名称不存在时, fallback 读取设备描述 if SetupDiGetDeviceRegistryProperty(DeviceInfoSet, @DeviceInfoData, SPDRP_DEVICEDESC, nil, @FriendlyName, SizeOf(FriendlyName), @RequiredSize) then begin Result := Result + [StrPas(FriendlyName)]; end; end; Inc(DeviceIndex); ZeroMemory(@DeviceInfoData, SizeOf(DeviceInfoData)); DeviceInfoData.cbSize := SizeOf(DeviceInfoData); end; finally SetupDiDestroyDeviceInfoList(DeviceInfoSet); end; end;
调用方式
procedure TForm1.Button1Click(Sender: TObject); var MonitorNames: TArray<string>; Name: string; begin Memo1.Lines.Clear; Memo1.Lines.Add('Detected monitors:'); Memo1.Lines.Add('-------------------'); MonitorNames := GetMonitorFriendlyNames; for Name in MonitorNames do Memo1.Lines.Add(Name); end;
方案说明
- 该方案直接读取Windows设备注册表中的属性,获取的名称与设备管理器完全一致,无需解析EDID,避免了EDID读取错误的问题。
- 优先读取
SPDRP_FRIENDLYNAME属性,这是显示器的具体型号名称;若该属性不存在,则读取SPDRP_DEVICEDESC作为备选,确保返回有效名称。
内容的提问来源于stack exchange,提问作者Shaun Roselt
相关产品推荐
相关产品推荐

