You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 21:13:09