【问题标题】:How to obtain the real screen resolution in a High DPI system?如何在高 DPI 系统中获得真实的屏幕分辨率?
【发布时间】:2014-11-23 20:42:16
【问题描述】:

因此,Delphi 程序不支持 DPI。直到最近,当我需要一台具有高 DPI 的计算机中的真实屏幕分辨率 (Wrong resolution reported by Screen.Width when "Make it easier to read what's on screen" is 150%) 时,这才让我感到困扰。一些建议是让应用程序感知高 DPI(XML 清单),但其他人警告我们这涉及大量工作!所以,懒惰(或缺乏时间),我想知道是否有一个技巧来计算真正的分辨率。

我想到的一个非常肮脏的技巧是创建一个可识别 DPI 的配套工具(小型控制台应用程序)。我所要做的就是调用这个工具并从中获得真正的分辨率。 相当斯巴达,但它应该工作。无论如何,必须有更好的方法来做到这一点!

【问题讨论】:

    标签: delphi resolution delphi-xe dpi


    【解决方案1】:

    这就是我绕过这个限制的方法:

    Function GetScreenRect():TRect;
    var
      canvas: TCanvas;
    begin
      canvas := TCanvas.Create;
      canvas.Handle := GetDC(0);
      result := Canvas.ClipRect;
      ReleaseDC(0, canvas.Handle);
      canvas.Free;
    end;
    

    【讨论】:

    • 所以你不需要其他任何东西,你插入我的代码,你得到HighDPI下的真实屏幕尺寸,问题就解决了。
    【解决方案2】:

    Win32_DesktopMonitor WMI 类将产生信息。

    例如,使用取自这里的代码:Delphi7: Get attached monitor properties

    {$APPTYPE CONSOLE}
    
    uses
      SysUtils,
      ActiveX,
      ComObj,
      Variants;
    
    function VarStrNull(VarStr: OleVariant): string;
    // dummy function to handle null variants
    begin
      Result := '';
      if not VarIsNull(VarStr) then
        Result := VarToStr(VarStr);
    end;
    
    procedure GetMonitorInfo;
    var
      objWMIService: OleVariant;
      colItems: OleVariant;
      colItem: OleVariant;
      oEnum: IEnumvariant;
      iValue: LongWord;
    
      function GetWMIObject(const objectName: String): IDispatch;
      var
        chEaten: Integer;
        BindCtx: IBindCtx;
        Moniker: IMoniker;
      begin
        OleCheck(CreateBindCtx(0, BindCtx));
        OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten,
          Moniker));
        OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
      end;
    
    begin
      objWMIService := GetWMIObject('winmgmts:\\localhost\root\CIMV2');
      colItems := objWMIService.ExecQuery
        ('SELECT * FROM Win32_DesktopMonitor', 'WQL', 0);
      oEnum := IUnknown(colItems._NewEnum) as IEnumvariant;
      while oEnum.Next(1, colItem, iValue) = 0 do
      begin
        Writeln('Caption      ' + VarStrNull(colItem.Caption));
        Writeln('Device ID    ' + VarStrNull(colItem.DeviceID));
        Writeln('Width        ' + VarStrNull(colItem.ScreenWidth));
        Writeln('Height       ' + VarStrNull(colItem.ScreenHeight));
        Writeln;
      end;
    end;
    
    begin
      try
        CoInitialize(nil);
        try
          GetMonitorInfo;
          Readln;
        finally
          CoUninitialize;
        end;
      except
        on E: Exception do
        begin
          Writeln(E.Classname, ': ', E.Message);
          Readln;
        end;
      end;
    end.
    

    如果由于任何原因 WMI 不可用,那么您需要一个单独的 DPI 感知进程来完成这项工作。这也将需要一些 IPC。

    另一个问题是最新版本的 Windows 已经改变了这些 WMI 类的行为。您可能需要使用不同的 WMI 查询。见这里:https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/138d387c-b222-4c9f-b3bb-c69ee890491c/problem-with-win32desktopmonitor-in-windows-8-platform?forum=windowsgeneraldevelopmentissues

    【讨论】:

    • 这听起来像是一个绝妙的解决方案。不幸的是,我对 WMI 的体验很糟糕,某些计算机上没有这种体验。我的程序在一些 WMI 函数之前使用过。最后我不得不删除它们。如果您在 Google 中搜索“WMI 不可用”,您会看到很多报告。
    • @davidhefferman-我知道为什么我收到这么多与 WMI 相关的错误:WMI 是一项服务,在某些系统上默认情况下不启用!!!!!! youtube.com/watch?v=JgsHfc-4TUE
    • 这不符合我的 WMI 经验
    • @hikari 见最后一段
    • 放弃这个。改为使用 dpi-aware 清单制作了一个助手 exe,使用主应用程序的句柄作为参数运行它 -> 助手在 l/w 参数上发送一条带有 w/h 的消息,完成。 (比读取控制台输出更快)
    猜你喜欢
    • 1970-01-01
    • 2021-06-27
    • 2011-11-10
    • 2011-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多