首页 > 解决方案 > 在 Delphi 中使用 EnumDisplayDevices 打印显示器的名称

问题描述

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

我尝试将以下用 c++ 编写的示例转换为 delphi,但是当我尝试从 PDISPLAY_DEVICEA 结构中读取设备名称时遇到问题,LDeviceName := LDisplayDevice.deviceName;因为它只返回中文字符。我认为这是与字符编码有关的问题,但我不知道如何解决。

我的源代码:

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.

标签: delphidllunicode

解决方案


您正在混合 ANSI 和 Unicode。

EnumDisplayDevices函数有两个版本:

您正在调用 ANSI 版本EnumDisplayDevicesA,但使用的是 Unicode 版本DISPLAY_DEVICE。所以你需要EnumDisplayDevicesW改用。

这种API函数在W版和A版都存在的现象在Windows API中随处可见,所以上面的说明很笼统。

由于这种编码不匹配而获得中文文本这一事实也是众所周知的


说了这么多,你根本不需要表态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 是这样说的:

winuser.h 头文件将 EnumDisplayDevices 定义为别名,它根据 UNICODE 预处理器常量的定义自动选择此函数的 ANSI 或 Unicode 版本。

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


推荐阅读