像 MultiMonitor 工具一样读取监视器名称
Read monitor name like MultiMonitor tool does
我想知道 Delphi 中是否有办法读取 监视器名称,正如我在 MultiMonitorTool[= 中看到的那样30=] 程序:
目前我尝试通过以下方式阅读:
MonitorInfo.szDevice
(它returns "\.\DISPLAY1"
)
monitorname(Screen.Monitors[i].MonitorNum)
(它returns "Generic-Non-PnP-Monitor"
)
Screen.Monitors[i].FriendlyName
(它returns "Generic-Non-PnP-Monitor"
)
procedure TMonitorOrientationDemoForm.RefreshMonitorsList;
var
i: integer;
li: TListItem;
MonitorInfo: TMonitorInfoEx;
LTmp: string;
function ArrayToString(const a: array of Char): string;
begin
if Length(a) > 0 then
Result := StrPas(PChar(@a[0]))
else
Result := '';
end;
begin
lvMonitors.Items.BeginUpdate;
try
lvMonitors.Items.Clear;
for i := 0 to Screen.MonitorCount - 1 do
begin
li := lvMonitors.Items.Add;
MonitorInfo.cbSize := SizeOf(MonitorInfo);
if not GetMonitorInfo(Screen.Monitors[i].Handle, @MonitorInfo) then
continue;
LTmp := ArrayToString(MonitorInfo.szDevice);
//Screen.Monitors[i].FriendlyName -> returns the string "Generic-Non-PnP-Monitor"
//MonitorInfo.szDevice -> returns the string "\.\DISPLAY1"
//monitorname(Screen.Monitors[i].MonitorNum) -> returns the string "Generic-Non-PnP-Monitor"
LI.Caption := Screen.Monitors[i].FriendlyName;
li.SubItems.Add(MonitorOrientationToString(Screen.Monitors[i].Orientation));
if Screen.Monitors[i].SupportsRotation then
li.SubItems.Add('Yes')
else
li.SubItems.Add('No');
end;
finally
lvMonitors.Items.EndUpdate;
end;
end;
function TMonitorOrientationDemoForm.monitorname(numberof: integer): string;
Var
Cntr: Cardinal;
Info: TDisplayDevice;
AdapterName: PChar;
OldPos, j, i: integer;
a: tstringlist;
Begin
a := tstringlist.create;
Cntr := numberof;
Info.cb := SizeOf(Info);
While EnumDisplayDevices(Nil, Cntr, Info, 0) Do
Begin
AdapterName := StrAlloc(SizeOf(Info.DeviceName));
StrCopy(AdapterName, Info.DeviceName);
EnumDisplayDevices(AdapterName, 0, Info, 0);
a.Add(Info.DeviceString);
for i := 1 to a.count - 1 do
begin
a.Delete(i);
Result := a.Strings[0];
StrDispose(AdapterName);
Inc(Cntr);
End;
End;
End;
基于EnumDisplayDevices
and has described in several previous Qs [1, 2]的标准方法在Delphi中可以这样写:
procedure TForm1.FormCreate(Sender: TObject);
var
dd, md: TDisplayDevice;
begin
ListBox1.Items.BeginUpdate;
try
ListBox1.Clear;
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
ListBox1.Items.Add(md.DeviceString);
Inc(j);
end;
Inc(i);
end;
finally
ListBox1.Items.EndUpdate;
end;
end;
在我的系统上,这可以正确识别我的三台戴尔显示器,但无法识别我的三星壁挂式电视(“通用 PnP 显示器”)。
首先你必须知道有些显示器在 windows 中没有注册名称,但如果它们有名称,你可以使用 MultiMonitorTool 等程序读取它,所以你也可以用Delphi阅读它。
您可以从注册表中读取 EDID 键值。
EDID 键的值在安装显示器时创建,可以被显示器驱动程序覆盖。
此程序读取活动监视器的 EDID。
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Win.Registry, Vcl.ComCtrls, Winapi.MultiMon,
Winapi.Windows, Vcl.Forms, System.Classes, System.StrUtils,
System.Character;
function GetMonitorName(index : Integer) : String;
const
Key = '\SYSTEM\CurrentControlSet\Enum\DISPLAY\';
type
IBMString = type Ansistring(1253);
var
Handle: HMONITOR;
i, j, k : Integer;
Registry: TRegistry;
MonitorName : IBMString;
DispDev : TDisplayDevice;
subKeysNames : TStringList;
MonitorInfo : TMonitorInfoEx;
DeviceIDSplit : TArray<String>;
EDID : array [0 .. 127] of Byte;
DeviceName, DeviceString, DeviceID, DeviceKey, Driver : string;
begin
Result := '';
Handle := Screen.Monitors[index].Handle;
MonitorInfo.cbSize := sizeof(MonitorInfo);
if GetMonitorInfo(Handle, @MonitorInfo) then
begin
DispDev.cb := sizeof(DispDev);
EnumDisplayDevices(@MonitorInfo.szDevice, 0, DispDev, 0);
DeviceName := StrPas(DispDev.DeviceName); //'\.\DISPLAY1\Monitor0' //This Line Can Be Removed
DeviceString := StrPas(DispDev.DeviceString); //'Generic PnP Monitor' //This Line Can Be Removed
DeviceID := StrPas(DispDev.DeviceID);
DeviceKey := StrPas(DispDev.DeviceKey); //This Line Can Be Removed
DeviceIDSplit := DeviceID.Split(['\']);
if Length(DeviceIDSplit) < 3 then Exit;
Driver := '';
for i := 2 to High(DeviceIDSplit) do
Driver := Driver + '\' + DeviceIDSplit[i];
System.Delete(Driver, 1, 1);
subKeysNames := TStringList.Create;
Registry := TRegistry.Create(KEY_READ);
Registry.RootKey := HKEY_LOCAL_MACHINE;
try
try
Registry.OpenKeyReadOnly(Key);
Registry.GetKeyNames(subKeysNames);
finally
Registry.CloseKey;
end;
if subKeysNames.IndexOf(DeviceIDSplit[1]) < 0 then Exit;
try
Registry.OpenKeyReadOnly(Key + DeviceIDSplit[1]);
Registry.GetKeyNames(subKeysNames);
finally
Registry.CloseKey;
end;
for i := 0 to subKeysNames.Count - 1 do
begin
try
if registry.OpenKeyReadOnly(Key + DeviceIDSplit[1] + '\' + subKeysNames[i]) then
begin
if Registry.ReadString('Driver') <> Driver then Continue;
Registry.CloseKey;
if registry.OpenKeyReadOnly(Key + DeviceIDSplit[1] + '\' + subKeysNames[i] + '\' + 'Device Parameters') then
begin
Registry.ReadBinaryData('EDID', EDID, 128);
Registry.CloseKey;
end;
for j := 0 to 3 do
begin
if (EDID[54 + 18 * j] = 0) and
(EDID[55 + 18 * j] = 0) and
(EDID[56 + 18 * j] = 0) and
(EDID[57 + 18 * j] = $FC) and
(EDID[58 + 18 * j] = 0) then
begin
k := 0;
while (EDID[59 + 18 * j + k] <> $A) and (k < 13) do
Inc(k);
SetString(MonitorName, PAnsiChar(@EDID[59 + 18 * j]), k);
Result := MonitorName;
Break;
end;
end;
end;
finally
Registry.CloseKey;
end;
end;
finally
Registry.Free;
subKeysNames.Free;
end;
end;
end;
var
i : Integer;
begin
for i := 0 to Screen.MonitorCount-1 do
begin
Writeln(GetMonitorName(i));
end;
Readln;
end.
并且这个程序读取了所有的监视器名称,其中一些可能没有名称。
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Win.Registry, Vcl.ComCtrls, Winapi.MultiMon,
Winapi.Windows, Vcl.Forms, System.Classes, System.StrUtils,
System.Character;
function GetAllMonitorName : TStrings;
const
Key = '\SYSTEM\CurrentControlSet\Enum\DISPLAY\';
type
IBMString = type Ansistring(1253);
var
Registry: TRegistry;
H, i, j, k : Integer;
MonitorName : IBMString;
EDID : array [0 .. 127] of Byte;
subKeysNames, subKeys : TStringList;
begin
Result := TStringList.Create;
subKeys := TStringList.Create;
subKeysNames := TStringList.Create;
Registry := TRegistry.Create(KEY_READ);
Registry.RootKey := HKEY_LOCAL_MACHINE;
try
try
Registry.OpenKeyReadOnly(Key);
Registry.GetKeyNames(subKeysNames);
finally
Registry.CloseKey;
end;
for h := 0 to subKeysNames.Count - 1 do
begin
try
Registry.OpenKeyReadOnly(Key + subKeysNames[h]);
Registry.GetKeyNames(subKeys);
finally
Registry.CloseKey;
end;
for i := 0 to subKeys.Count - 1 do
begin
try
if registry.OpenKeyReadOnly(Key + subKeysNames[h] + '\' + subKeys[i]) then
begin
if registry.OpenKeyReadOnly(Key + subKeysNames[h] + '\' + subKeys[i] + '\' + 'Device Parameters') then
begin
Registry.ReadBinaryData('EDID', EDID, 128);
Registry.CloseKey;
end;
MonitorName := '';
for j := 0 to 3 do
begin
if (EDID[54 + 18 * j] = 0) and
(EDID[55 + 18 * j] = 0) and
(EDID[56 + 18 * j] = 0) and
(EDID[57 + 18 * j] = $FC) and
(EDID[58 + 18 * j] = 0) then
begin
k := 0;
while (EDID[59 + 18 * j + k] <> $A) and (k < 13) do
Inc(k);
SetString(MonitorName, PAnsiChar(@EDID[59 + 18 * j]), k);
Break;
end;
end;
Result.Add(MonitorName);
end;
finally
Registry.CloseKey;
end;
end;
end;
finally
subKeys.Free;
Registry.Free;
subKeysNames.Free;
end;
end;
var
i : Integer;
begin
Writeln(GetAllMonitorName.Text);
Readln;
end.
如果你想写一个类似 MultiMonitorTool 的程序,你可以从 EDID 阅读更多信息,更多信息阅读 Extended Display Identification Data
这对我来说很新,要么是因为我在 Windows 7 上执行它,要么是因为我还有一台电视而不是显示器,所以我也只能得到一个使用常规方法的通用“PnP-Monitor(标准)”。但是下面的代码给了我一个有用的结果:
program Whosebug68064094;
{$APPTYPE CONSOLE}
const
user32= 'user32.dll';
QDC_ALL_PATHS= 1;
DISPLAYCONFIG_MODE_INFO_TYPE_TARGET= 2;
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME= 2;
ERROR_SUCCESS= 0;
type
LUID= Int64;
LONG= LongInt; // Just anything 32bit
BOOL= LongBool;
// ENUMs, all starting with 1
DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS= Cardinal;
DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY= Cardinal;
DISPLAYCONFIG_ROTATION= Cardinal;
DISPLAYCONFIG_SCALING= Cardinal;
DISPLAYCONFIG_SCANLINE_ORDERING= Cardinal;
DISPLAYCONFIG_MODE_INFO_TYPE= Cardinal;
DISPLAYCONFIG_PIXELFORMAT= Cardinal;
DISPLAYCONFIG_TOPOLOGY_ID= Cardinal;
PDISPLAYCONFIG_DEVICE_INFO_HEADER= ^DISPLAYCONFIG_DEVICE_INFO_HEADER;
DISPLAYCONFIG_DEVICE_INFO_HEADER= packed record
typ, size: Cardinal;
adapterId: LUID;
id: Cardinal;
end;
DISPLAYCONFIG_TARGET_DEVICE_NAME= packed record
header: DISPLAYCONFIG_DEVICE_INFO_HEADER;
flags: DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS;
outputTechnology: DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY;
edidManufactureId, edidProductCodeId: Word;
connectorInstance: Cardinal;
monitorFriendlyDeviceName: Array[0.. 64- 1] of WideChar;
monitorDevicePath: Array[0.. 128- 1] of WideChar;
end;
DISPLAYCONFIG_PATH_SOURCE_INFO= packed record
adapterId: LUID;
id, modeInfoIdx, statusFlags: Cardinal;
end;
DISPLAYCONFIG_RATIONAL= packed record
Numerator, Denominator: Cardinal;
end;
DISPLAYCONFIG_PATH_TARGET_INFO= packed record
adapterId: LUID;
id, modeInfoIdx: Cardinal;
outputTechnology: DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY;
rotation: DISPLAYCONFIG_ROTATION;
scaling: DISPLAYCONFIG_SCALING;
refreshRate: DISPLAYCONFIG_RATIONAL;
scanLineOrdering: DISPLAYCONFIG_SCANLINE_ORDERING;
targetAvailable: BOOL;
statusFlags: Cardinal;
end;
PDISPLAYCONFIG_PATH_INFO= ^DISPLAYCONFIG_PATH_INFO;
DISPLAYCONFIG_PATH_INFO= packed record
sourceInfo: DISPLAYCONFIG_PATH_SOURCE_INFO;
targetInfo: DISPLAYCONFIG_PATH_TARGET_INFO;
flags: Cardinal;
end;
DISPLAYCONFIG_2DREGION= packed record
cx, cy: Cardinal;
end;
DISPLAYCONFIG_VIDEO_SIGNAL_INFO= packed record
pixelRate: UInt64;
hSyncFreq, vSyncFreq: DISPLAYCONFIG_RATIONAL;
activeSize, totalSize: DISPLAYCONFIG_2DREGION;
videoStandard: Cardinal;
end;
DISPLAYCONFIG_TARGET_MODE= packed record
targetVideoSignalInfo: DISPLAYCONFIG_VIDEO_SIGNAL_INFO;
end;
POINTL= packed record // Or TPOINT right away
x, y: LONG;
end;
DISPLAYCONFIG_SOURCE_MODE= packed record
width, height: Cardinal;
pixelFormat: DISPLAYCONFIG_PIXELFORMAT;
position: POINTL;
end;
RECTL= packed record // Or TRECT right away
left, top, right, bottom: LONG;
end;
DISPLAYCONFIG_DESKTOP_IMAGE_INFO= packed record
PathSourceSize: POINTL;
DesktopImageRegion, DesktopImageClip: RECTL;
end;
PDISPLAYCONFIG_MODE_INFO= ^DISPLAYCONFIG_MODE_INFO;
DISPLAYCONFIG_MODE_INFO= packed record
infoType: DISPLAYCONFIG_MODE_INFO_TYPE;
id: Cardinal;
adapterId: LUID;
case Byte of
1: ( targetMode: DISPLAYCONFIG_TARGET_MODE );
2: ( sourceMode: DISPLAYCONFIG_SOURCE_MODE );
3: ( desktopImageInfo: DISPLAYCONFIG_DESKTOP_IMAGE_INFO );
end;
PDISPLAYCONFIG_TOPOLOGY_ID= ^DISPLAYCONFIG_TOPOLOGY_ID;
function GetDisplayConfigBufferSizes( flags: Cardinal;
numPathArrayElements, numModeInfoArrayElements: PCardinal ): LONG; stdcall; external user32; // Vista
function QueryDisplayConfig( flags: Cardinal; numPathArrayElements: PCardinal;
pathArray: PDISPLAYCONFIG_PATH_INFO; numModeInfoArrayElements: PCardinal;
modeInfoArray: PDISPLAYCONFIG_MODE_INFO;
currentTopologyId: PDISPLAYCONFIG_TOPOLOGY_ID ): LONG; stdcall; external user32; // Windows 7
function DisplayConfigGetDeviceInfo(
requestPacket: PDISPLAYCONFIG_DEVICE_INFO_HEADER ): LONG; stdcall; external user32; // Vista
// Actually using all this
procedure GetFriendlyMonitorNames();
var
vName: DISPLAYCONFIG_TARGET_DEVICE_NAME;
iPath, iMode: Cardinal;
aPath: Array of DISPLAYCONFIG_PATH_INFO;
aMode: Array of DISPLAYCONFIG_MODE_INFO;
i: Integer;
begin
if not GetDisplayConfigBufferSizes( QDC_ALL_PATHS, @iPath, @iMode )= ERROR_SUCCESS then exit;
SetLength( aPath, iPath ); // For me: 104...
SetLength( aMode, iMode ); // ...and 30
if not QueryDisplayConfig
( QDC_ALL_PATHS
, @iPath, @aPath[0]
, @iMode, @aMode[0]
, nil
)= ERROR_SUCCESS then exit;
for i:= 0 to Integer(iMode)- 1 do begin
case aMode[i].infoType of
DISPLAYCONFIG_MODE_INFO_TYPE_TARGET: begin
vName.header.size:= SizeOf( DISPLAYCONFIG_TARGET_DEVICE_NAME );
vName.header.adapterId:= aMode[i].adapterId;
vName.header.id:= aMode[i].id;
vName.header.typ:= DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
if( DisplayConfigGetDeviceInfo( @vName ) )= ERROR_SUCCESS then begin
Writeln( vName.monitorFriendlyDeviceName ); // For me: M237WD
end;
end;
end;
end;
end;
begin
GetFriendlyMonitorNames();
end.
在较旧的上网本上,友好名称为空(成功的函数调用,tho),而通用 .DeviceString
方法给我“Digital-Flachbildschirm (1280x1024 60Hz)”,这更有帮助。我想至少必须考虑这两种方法。
编辑: 包括所有从 C++ 翻译而来的手写定义。现在它是一个完整的程序,运行 和 Delphi 7 没问题。至少需要 Windows 7.
我想知道 Delphi 中是否有办法读取 监视器名称,正如我在 MultiMonitorTool[= 中看到的那样30=] 程序:
目前我尝试通过以下方式阅读:
MonitorInfo.szDevice
(它returns"\.\DISPLAY1"
)monitorname(Screen.Monitors[i].MonitorNum)
(它returns"Generic-Non-PnP-Monitor"
)Screen.Monitors[i].FriendlyName
(它returns"Generic-Non-PnP-Monitor"
)
procedure TMonitorOrientationDemoForm.RefreshMonitorsList;
var
i: integer;
li: TListItem;
MonitorInfo: TMonitorInfoEx;
LTmp: string;
function ArrayToString(const a: array of Char): string;
begin
if Length(a) > 0 then
Result := StrPas(PChar(@a[0]))
else
Result := '';
end;
begin
lvMonitors.Items.BeginUpdate;
try
lvMonitors.Items.Clear;
for i := 0 to Screen.MonitorCount - 1 do
begin
li := lvMonitors.Items.Add;
MonitorInfo.cbSize := SizeOf(MonitorInfo);
if not GetMonitorInfo(Screen.Monitors[i].Handle, @MonitorInfo) then
continue;
LTmp := ArrayToString(MonitorInfo.szDevice);
//Screen.Monitors[i].FriendlyName -> returns the string "Generic-Non-PnP-Monitor"
//MonitorInfo.szDevice -> returns the string "\.\DISPLAY1"
//monitorname(Screen.Monitors[i].MonitorNum) -> returns the string "Generic-Non-PnP-Monitor"
LI.Caption := Screen.Monitors[i].FriendlyName;
li.SubItems.Add(MonitorOrientationToString(Screen.Monitors[i].Orientation));
if Screen.Monitors[i].SupportsRotation then
li.SubItems.Add('Yes')
else
li.SubItems.Add('No');
end;
finally
lvMonitors.Items.EndUpdate;
end;
end;
function TMonitorOrientationDemoForm.monitorname(numberof: integer): string;
Var
Cntr: Cardinal;
Info: TDisplayDevice;
AdapterName: PChar;
OldPos, j, i: integer;
a: tstringlist;
Begin
a := tstringlist.create;
Cntr := numberof;
Info.cb := SizeOf(Info);
While EnumDisplayDevices(Nil, Cntr, Info, 0) Do
Begin
AdapterName := StrAlloc(SizeOf(Info.DeviceName));
StrCopy(AdapterName, Info.DeviceName);
EnumDisplayDevices(AdapterName, 0, Info, 0);
a.Add(Info.DeviceString);
for i := 1 to a.count - 1 do
begin
a.Delete(i);
Result := a.Strings[0];
StrDispose(AdapterName);
Inc(Cntr);
End;
End;
End;
基于EnumDisplayDevices
and has described in several previous Qs [1, 2]的标准方法在Delphi中可以这样写:
procedure TForm1.FormCreate(Sender: TObject);
var
dd, md: TDisplayDevice;
begin
ListBox1.Items.BeginUpdate;
try
ListBox1.Clear;
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
ListBox1.Items.Add(md.DeviceString);
Inc(j);
end;
Inc(i);
end;
finally
ListBox1.Items.EndUpdate;
end;
end;
在我的系统上,这可以正确识别我的三台戴尔显示器,但无法识别我的三星壁挂式电视(“通用 PnP 显示器”)。
首先你必须知道有些显示器在 windows 中没有注册名称,但如果它们有名称,你可以使用 MultiMonitorTool 等程序读取它,所以你也可以用Delphi阅读它。
您可以从注册表中读取 EDID 键值。 EDID 键的值在安装显示器时创建,可以被显示器驱动程序覆盖。
此程序读取活动监视器的 EDID。
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Win.Registry, Vcl.ComCtrls, Winapi.MultiMon,
Winapi.Windows, Vcl.Forms, System.Classes, System.StrUtils,
System.Character;
function GetMonitorName(index : Integer) : String;
const
Key = '\SYSTEM\CurrentControlSet\Enum\DISPLAY\';
type
IBMString = type Ansistring(1253);
var
Handle: HMONITOR;
i, j, k : Integer;
Registry: TRegistry;
MonitorName : IBMString;
DispDev : TDisplayDevice;
subKeysNames : TStringList;
MonitorInfo : TMonitorInfoEx;
DeviceIDSplit : TArray<String>;
EDID : array [0 .. 127] of Byte;
DeviceName, DeviceString, DeviceID, DeviceKey, Driver : string;
begin
Result := '';
Handle := Screen.Monitors[index].Handle;
MonitorInfo.cbSize := sizeof(MonitorInfo);
if GetMonitorInfo(Handle, @MonitorInfo) then
begin
DispDev.cb := sizeof(DispDev);
EnumDisplayDevices(@MonitorInfo.szDevice, 0, DispDev, 0);
DeviceName := StrPas(DispDev.DeviceName); //'\.\DISPLAY1\Monitor0' //This Line Can Be Removed
DeviceString := StrPas(DispDev.DeviceString); //'Generic PnP Monitor' //This Line Can Be Removed
DeviceID := StrPas(DispDev.DeviceID);
DeviceKey := StrPas(DispDev.DeviceKey); //This Line Can Be Removed
DeviceIDSplit := DeviceID.Split(['\']);
if Length(DeviceIDSplit) < 3 then Exit;
Driver := '';
for i := 2 to High(DeviceIDSplit) do
Driver := Driver + '\' + DeviceIDSplit[i];
System.Delete(Driver, 1, 1);
subKeysNames := TStringList.Create;
Registry := TRegistry.Create(KEY_READ);
Registry.RootKey := HKEY_LOCAL_MACHINE;
try
try
Registry.OpenKeyReadOnly(Key);
Registry.GetKeyNames(subKeysNames);
finally
Registry.CloseKey;
end;
if subKeysNames.IndexOf(DeviceIDSplit[1]) < 0 then Exit;
try
Registry.OpenKeyReadOnly(Key + DeviceIDSplit[1]);
Registry.GetKeyNames(subKeysNames);
finally
Registry.CloseKey;
end;
for i := 0 to subKeysNames.Count - 1 do
begin
try
if registry.OpenKeyReadOnly(Key + DeviceIDSplit[1] + '\' + subKeysNames[i]) then
begin
if Registry.ReadString('Driver') <> Driver then Continue;
Registry.CloseKey;
if registry.OpenKeyReadOnly(Key + DeviceIDSplit[1] + '\' + subKeysNames[i] + '\' + 'Device Parameters') then
begin
Registry.ReadBinaryData('EDID', EDID, 128);
Registry.CloseKey;
end;
for j := 0 to 3 do
begin
if (EDID[54 + 18 * j] = 0) and
(EDID[55 + 18 * j] = 0) and
(EDID[56 + 18 * j] = 0) and
(EDID[57 + 18 * j] = $FC) and
(EDID[58 + 18 * j] = 0) then
begin
k := 0;
while (EDID[59 + 18 * j + k] <> $A) and (k < 13) do
Inc(k);
SetString(MonitorName, PAnsiChar(@EDID[59 + 18 * j]), k);
Result := MonitorName;
Break;
end;
end;
end;
finally
Registry.CloseKey;
end;
end;
finally
Registry.Free;
subKeysNames.Free;
end;
end;
end;
var
i : Integer;
begin
for i := 0 to Screen.MonitorCount-1 do
begin
Writeln(GetMonitorName(i));
end;
Readln;
end.
并且这个程序读取了所有的监视器名称,其中一些可能没有名称。
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Win.Registry, Vcl.ComCtrls, Winapi.MultiMon,
Winapi.Windows, Vcl.Forms, System.Classes, System.StrUtils,
System.Character;
function GetAllMonitorName : TStrings;
const
Key = '\SYSTEM\CurrentControlSet\Enum\DISPLAY\';
type
IBMString = type Ansistring(1253);
var
Registry: TRegistry;
H, i, j, k : Integer;
MonitorName : IBMString;
EDID : array [0 .. 127] of Byte;
subKeysNames, subKeys : TStringList;
begin
Result := TStringList.Create;
subKeys := TStringList.Create;
subKeysNames := TStringList.Create;
Registry := TRegistry.Create(KEY_READ);
Registry.RootKey := HKEY_LOCAL_MACHINE;
try
try
Registry.OpenKeyReadOnly(Key);
Registry.GetKeyNames(subKeysNames);
finally
Registry.CloseKey;
end;
for h := 0 to subKeysNames.Count - 1 do
begin
try
Registry.OpenKeyReadOnly(Key + subKeysNames[h]);
Registry.GetKeyNames(subKeys);
finally
Registry.CloseKey;
end;
for i := 0 to subKeys.Count - 1 do
begin
try
if registry.OpenKeyReadOnly(Key + subKeysNames[h] + '\' + subKeys[i]) then
begin
if registry.OpenKeyReadOnly(Key + subKeysNames[h] + '\' + subKeys[i] + '\' + 'Device Parameters') then
begin
Registry.ReadBinaryData('EDID', EDID, 128);
Registry.CloseKey;
end;
MonitorName := '';
for j := 0 to 3 do
begin
if (EDID[54 + 18 * j] = 0) and
(EDID[55 + 18 * j] = 0) and
(EDID[56 + 18 * j] = 0) and
(EDID[57 + 18 * j] = $FC) and
(EDID[58 + 18 * j] = 0) then
begin
k := 0;
while (EDID[59 + 18 * j + k] <> $A) and (k < 13) do
Inc(k);
SetString(MonitorName, PAnsiChar(@EDID[59 + 18 * j]), k);
Break;
end;
end;
Result.Add(MonitorName);
end;
finally
Registry.CloseKey;
end;
end;
end;
finally
subKeys.Free;
Registry.Free;
subKeysNames.Free;
end;
end;
var
i : Integer;
begin
Writeln(GetAllMonitorName.Text);
Readln;
end.
如果你想写一个类似 MultiMonitorTool 的程序,你可以从 EDID 阅读更多信息,更多信息阅读 Extended Display Identification Data
这对我来说很新,要么是因为我在 Windows 7 上执行它,要么是因为我还有一台电视而不是显示器,所以我也只能得到一个使用常规方法的通用“PnP-Monitor(标准)”。但是下面的代码给了我一个有用的结果:
program Whosebug68064094;
{$APPTYPE CONSOLE}
const
user32= 'user32.dll';
QDC_ALL_PATHS= 1;
DISPLAYCONFIG_MODE_INFO_TYPE_TARGET= 2;
DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME= 2;
ERROR_SUCCESS= 0;
type
LUID= Int64;
LONG= LongInt; // Just anything 32bit
BOOL= LongBool;
// ENUMs, all starting with 1
DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS= Cardinal;
DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY= Cardinal;
DISPLAYCONFIG_ROTATION= Cardinal;
DISPLAYCONFIG_SCALING= Cardinal;
DISPLAYCONFIG_SCANLINE_ORDERING= Cardinal;
DISPLAYCONFIG_MODE_INFO_TYPE= Cardinal;
DISPLAYCONFIG_PIXELFORMAT= Cardinal;
DISPLAYCONFIG_TOPOLOGY_ID= Cardinal;
PDISPLAYCONFIG_DEVICE_INFO_HEADER= ^DISPLAYCONFIG_DEVICE_INFO_HEADER;
DISPLAYCONFIG_DEVICE_INFO_HEADER= packed record
typ, size: Cardinal;
adapterId: LUID;
id: Cardinal;
end;
DISPLAYCONFIG_TARGET_DEVICE_NAME= packed record
header: DISPLAYCONFIG_DEVICE_INFO_HEADER;
flags: DISPLAYCONFIG_TARGET_DEVICE_NAME_FLAGS;
outputTechnology: DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY;
edidManufactureId, edidProductCodeId: Word;
connectorInstance: Cardinal;
monitorFriendlyDeviceName: Array[0.. 64- 1] of WideChar;
monitorDevicePath: Array[0.. 128- 1] of WideChar;
end;
DISPLAYCONFIG_PATH_SOURCE_INFO= packed record
adapterId: LUID;
id, modeInfoIdx, statusFlags: Cardinal;
end;
DISPLAYCONFIG_RATIONAL= packed record
Numerator, Denominator: Cardinal;
end;
DISPLAYCONFIG_PATH_TARGET_INFO= packed record
adapterId: LUID;
id, modeInfoIdx: Cardinal;
outputTechnology: DISPLAYCONFIG_VIDEO_OUTPUT_TECHNOLOGY;
rotation: DISPLAYCONFIG_ROTATION;
scaling: DISPLAYCONFIG_SCALING;
refreshRate: DISPLAYCONFIG_RATIONAL;
scanLineOrdering: DISPLAYCONFIG_SCANLINE_ORDERING;
targetAvailable: BOOL;
statusFlags: Cardinal;
end;
PDISPLAYCONFIG_PATH_INFO= ^DISPLAYCONFIG_PATH_INFO;
DISPLAYCONFIG_PATH_INFO= packed record
sourceInfo: DISPLAYCONFIG_PATH_SOURCE_INFO;
targetInfo: DISPLAYCONFIG_PATH_TARGET_INFO;
flags: Cardinal;
end;
DISPLAYCONFIG_2DREGION= packed record
cx, cy: Cardinal;
end;
DISPLAYCONFIG_VIDEO_SIGNAL_INFO= packed record
pixelRate: UInt64;
hSyncFreq, vSyncFreq: DISPLAYCONFIG_RATIONAL;
activeSize, totalSize: DISPLAYCONFIG_2DREGION;
videoStandard: Cardinal;
end;
DISPLAYCONFIG_TARGET_MODE= packed record
targetVideoSignalInfo: DISPLAYCONFIG_VIDEO_SIGNAL_INFO;
end;
POINTL= packed record // Or TPOINT right away
x, y: LONG;
end;
DISPLAYCONFIG_SOURCE_MODE= packed record
width, height: Cardinal;
pixelFormat: DISPLAYCONFIG_PIXELFORMAT;
position: POINTL;
end;
RECTL= packed record // Or TRECT right away
left, top, right, bottom: LONG;
end;
DISPLAYCONFIG_DESKTOP_IMAGE_INFO= packed record
PathSourceSize: POINTL;
DesktopImageRegion, DesktopImageClip: RECTL;
end;
PDISPLAYCONFIG_MODE_INFO= ^DISPLAYCONFIG_MODE_INFO;
DISPLAYCONFIG_MODE_INFO= packed record
infoType: DISPLAYCONFIG_MODE_INFO_TYPE;
id: Cardinal;
adapterId: LUID;
case Byte of
1: ( targetMode: DISPLAYCONFIG_TARGET_MODE );
2: ( sourceMode: DISPLAYCONFIG_SOURCE_MODE );
3: ( desktopImageInfo: DISPLAYCONFIG_DESKTOP_IMAGE_INFO );
end;
PDISPLAYCONFIG_TOPOLOGY_ID= ^DISPLAYCONFIG_TOPOLOGY_ID;
function GetDisplayConfigBufferSizes( flags: Cardinal;
numPathArrayElements, numModeInfoArrayElements: PCardinal ): LONG; stdcall; external user32; // Vista
function QueryDisplayConfig( flags: Cardinal; numPathArrayElements: PCardinal;
pathArray: PDISPLAYCONFIG_PATH_INFO; numModeInfoArrayElements: PCardinal;
modeInfoArray: PDISPLAYCONFIG_MODE_INFO;
currentTopologyId: PDISPLAYCONFIG_TOPOLOGY_ID ): LONG; stdcall; external user32; // Windows 7
function DisplayConfigGetDeviceInfo(
requestPacket: PDISPLAYCONFIG_DEVICE_INFO_HEADER ): LONG; stdcall; external user32; // Vista
// Actually using all this
procedure GetFriendlyMonitorNames();
var
vName: DISPLAYCONFIG_TARGET_DEVICE_NAME;
iPath, iMode: Cardinal;
aPath: Array of DISPLAYCONFIG_PATH_INFO;
aMode: Array of DISPLAYCONFIG_MODE_INFO;
i: Integer;
begin
if not GetDisplayConfigBufferSizes( QDC_ALL_PATHS, @iPath, @iMode )= ERROR_SUCCESS then exit;
SetLength( aPath, iPath ); // For me: 104...
SetLength( aMode, iMode ); // ...and 30
if not QueryDisplayConfig
( QDC_ALL_PATHS
, @iPath, @aPath[0]
, @iMode, @aMode[0]
, nil
)= ERROR_SUCCESS then exit;
for i:= 0 to Integer(iMode)- 1 do begin
case aMode[i].infoType of
DISPLAYCONFIG_MODE_INFO_TYPE_TARGET: begin
vName.header.size:= SizeOf( DISPLAYCONFIG_TARGET_DEVICE_NAME );
vName.header.adapterId:= aMode[i].adapterId;
vName.header.id:= aMode[i].id;
vName.header.typ:= DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
if( DisplayConfigGetDeviceInfo( @vName ) )= ERROR_SUCCESS then begin
Writeln( vName.monitorFriendlyDeviceName ); // For me: M237WD
end;
end;
end;
end;
end;
begin
GetFriendlyMonitorNames();
end.
在较旧的上网本上,友好名称为空(成功的函数调用,tho),而通用 .DeviceString
方法给我“Digital-Flachbildschirm (1280x1024 60Hz)”,这更有帮助。我想至少必须考虑这两种方法。
编辑: 包括所有从 C++ 翻译而来的手写定义。现在它是一个完整的程序,运行 和 Delphi 7 没问题。至少需要 Windows 7.