Need to resolve HMONITOR --> deviceName (or deviceName --> HMONITOR) in windows
Asked Answered
C

1

6

EDIT - See Update at end

This is for Delphi 7.0 Build 4.453

Summary

I need to be able to take the Handle property from a TMonitor object (an element in the Monitors array in the TScreen component) which is a HMONITOR, and turn it into the string you would use in calls to EnumDisplaySettings as the lpszDeviceName parameter.

(my end goal is to get a list of device settings from a given HMONITOR value, by passing the resolved lpszDeviceName into calls to EnumDisplaySettings).

Detailed Information

As mentioned above, the Screen.Monitors[x].Handle property is of type HMONITOR and is normally used to pass into the GetMonitorInfo function, which returns, geometry information, but no lpszDeviceName. (note: there is a TMonitorInfoEx structure that has a szDevice field, but it does not seem to get filled in on my system, even though i am setting the cbSize field to the appropriate size).

Alternatively, if i can use a szDeviceName to get the equivalent HMONITOR value, i could plug it into the following function, which would use it in a comparison (I have inserted a call to fictitious function called hMonitorFromDeviceName in the code below) to indicate how it would be used.

function GetMonitorDeviceName(hmon : HMONITOR) : string;
var
  DispDev : TDisplayDevice;
  deviceName : string;
  nDeviceIndex : integer;
begin
  Result := '';

  FillChar(DispDev, sizeof(DispDev),0);
  DispDev.cb := sizeof(DispDev);

  nDeviceIndex := 0;
  while (EnumDisplayDevices(nil, nDeviceIndex, DispDev, 0)) do
  begin

     if ( hMonitorFromDeviceName(DispDev.DeviceString) = hmon ) then
     begin
        Result := StrPas(DispDev.DeviceString);
        exit;
     end;

     inc(nDeviceIndex);

  end;
end;

Update

Thanks to David Heffernan, I have tested his solution, and here is a sample function to get the monitor name from a given handle:

function GetMonitorName(hmon : HMONITOR) : string;
type
  TMonitorInfoEx = record
    cbSize: DWORD;
    rcMonitor: TRect;
    rcWork: TRect;
    dwFlags: DWORD;
    szDevice: array[0..CCHDEVICENAME - 1] of AnsiChar;
end;
var
  DispDev : TDisplayDevice;
  deviceName : string;
   monInfo : TMonitorInfoEx;
begin
  Result := '';

  monInfo.cbSize := sizeof(monInfo);
  if GetMonitorInfo(hmon,@monInfo) then
  begin

    DispDev.cb := sizeof(DispDev);
     EnumDisplayDevices(@monInfo.szDevice, 0, DispDev, 0);
     Result := StrPas(DispDev.DeviceString);

  end;
end;
Currycomb answered 1/4, 2013 at 15:27 Comment(0)
S
7

I think that you must be calling GetMonitorInfo incorrectly. This code:

{$APPTYPE CONSOLE}

uses
  SysUtils, MultiMon, Windows, Forms;

var
  i: Integer;
  MonitorInfo: TMonitorInfoEx;
begin
  MonitorInfo.cbSize := SizeOf(MonitorInfo);
  for i := 0 to Screen.MonitorCount-1 do
  begin
    if not GetMonitorInfo(Screen.Monitors[i].Handle, @MonitorInfo) then
      RaiseLastOSError;
    Writeln(MonitorInfo.szDevice);
  end;
  Readln;
end.

produces this output on my machine:

\\.\DISPLAY1
\\.\DISPLAY2

I suspect that your call to GetMonitorInfo is failing in some way and perhaps you are not checking the return value for errors.


Having searched QualityCentral I suspect you have fallen victim to a known bug in older versions of Delphi: QC#3239. This is reported fixed in version 10.0.2124.6661 which is Delphi 2006.


Your comments confirm this diagnosis. To fix the problem you'll need a new TMonitorInfoEx definition. Here's one that will work on your pre-Unicode Delphi:

type
  TMonitorInfoEx = record
    cbSize: DWORD;
    rcMonitor: TRect;
    rcWork: TRect;
    dwFlags: DWORD;
    szDevice: array[0..CCHDEVICENAME - 1] of AnsiChar;
  end;

If you add that to the code above (before you declare the variables of course) then I believe it will resolve your problem.


As an interesting aside, even in XE3, these structs have not been translated correctly: QC#114460. Admittedly the error is rather benign as it only affects PMonitorInfoExA and TMonitorInfoExA, but the error caught me out whilst trying to solve the problem in this question!

Selry answered 1/4, 2013 at 15:38 Comment(6)
when i run this on my system i get "the parameter is incorrect" raised as an exception.Currycomb
That happens when cbSize is incorrect. What value of cbSize do you see under the debugger? And which version of Delphi do you have? There's something amiss here. I'm sure we can get to the bottom of it and that API call will work for you.Selry
i am using delphi 7, and Writeln(Format('TMonitorInfoEx = %d TMonitorInfo = %d',[Sizeof(TMonitorInfoEx), sizeof (TMonitorInfo)])) outputs : TMonitorInfoEx = 76 TMonitorInfo = 40Currycomb
You are on an older ANSI Delphi I think. The correct value should be 72. I think this solves the puzzle. It would be nice to know your Delphi version too.Selry
Thanks. Can we please have the Delphi version. Then we can tag the question appropriately.Selry
Interestingly these records are still declared incorrectly in XE3! The A and W versions are totally messed up. I'll submit a new QC report.Selry

© 2022 - 2024 — McMap. All rights reserved.