使用 Delphi 中的 EnumDisplayDevices 打印显示器名称

Print the monitor's name with EnumDisplayDevices in Delphi

我需要阅读一些有关通过 EnumDisplayDevicesA 功能连接的监视器的信息。

我试图将以下用c++编写的example转换为delphi,但是当我尝试从PDISPLAY_DEVICEA结构[=12]中读取设备名称时出现问题=]因为它只有returns个汉字。 我认为这是与字符编码有关的问题,但我不知道如何解决它。

我的源代码:

program Monitor;

{$APPTYPE CONSOLE}
uses
  System.SysUtils;

const
  user32 = 'user32.dll';

type
  LONG = LongInt;

  BOOL = LongBool;

  PDISPLAY_DEVICE = ^DISPLAY_DEVICE;

  LPCSTR = array[0..128 - 1] of WideChar;

  PLPCSTR = ^LPCSTR;

  //https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-display_devicea
  DISPLAY_DEVICE = packed record
    cb: Cardinal;
    deviceName: array[0..32 - 1] of WideChar;
    deviceString: array[0..128 - 1] of WideChar;
    stateFlags: Cardinal;
    deviceID: array[0..128 - 1] of WideChar;
    deviceKey: array[0..128 - 1] of WideChar;
  end;

//https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaydevicesa
function EnumDisplayDevicesA(APCSTR: PLPCSTR; iDevNum: Cardinal; PDISPLAY_DEVICEA: PDISPLAY_DEVICE; dwFlags: Cardinal): BOOL; stdcall; external user32;

procedure PrintMonitorNames();
var
  LDisplayDevice: DISPLAY_DEVICE;
  LDeviceIndex: Integer;
  LMonitorIndex: Integer;
  LDeviceName: string;
begin
  LDisplayDevice.cb := Sizeof(LDisplayDevice);
  LDeviceIndex := 0;
  while EnumDisplayDevicesA(nil, LDeviceIndex, @LDisplayDevice, 0) do
  begin
    LDeviceName := LDisplayDevice.deviceName;
    Writeln('Device name: ' + LDeviceName);
    LMonitorIndex := 0;
    while EnumDisplayDevicesA(@LDeviceName, LMonitorIndex, @LDisplayDevice, 0) do
    begin
      Writeln(StrPas(LDisplayDevice.deviceName) + ' ' + StrPas(LDisplayDevice.deviceString));
      Inc(LMonitorIndex);
    end;
    Inc(LDeviceIndex);
  end;
end;

var
  LDummy: string;

begin
  Writeln('START');
  PrintMonitorNames();
  Writeln('FINISH');
  Readln(LDummy);
end.

您正在混合使用 ANSI 和 Unicode。

EnumDisplayDevices函数存在两个版本:

您调用的是 ANSI 版本 EnumDisplayDevicesA,但使用的是 DISPLAY_DEVICE 的 Unicode 版本。所以你需要使用 EnumDisplayDevicesW 来代替。

这种API函数在W版和A版中都存在的现象在WindowsAPI中无处不在,所以上面的说法很笼统。

由于这种编码不匹配导致您得到中文文本的事实也是 very well known


说了这么多,你根本不需要自己声明EnumDisplayDevices。你需要的一切都已经存在于 Delphi RTL 的 Windows.pas 单元中,就像我向你展示的那样 :

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Winapi.Windows;

begin

  var dd, md: TDisplayDevice;

  FillChar(dd, SizeOf(dd), 0);
  dd.cb := SizeOf(dd);
  FillChar(md, SizeOf(md), 0);
  md.cb := SizeOf(md);
  var i := 0;
  while EnumDisplayDevices(nil, i, dd, 0) do
  begin
    var j := 0;
    while EnumDisplayDevices(@dd.DeviceName[0], j, md, 0) do
    begin
      Writeln(md.DeviceString);
      Inc(j);
    end;
    Inc(i);
  end;

  Readln;

end.

注意 MSDN 是这样说的:

The winuser.h header defines EnumDisplayDevices as an alias which automatically selects the ANSI or Unicode version of this function based on the definition of the UNICODE preprocessor constant.

同样的评论适用于 Delphi RTL 的 Windows.pas