如何解决高 DPI 问题?

How to work arround the high DPI issue?

我需要从 Delphi 程序获取桌面分辨率。
但是,如果程序不支持 DPI,Windows 将谎报真实的屏幕分辨率,因此各种问题都会从这里出现。

因为让程序完全感知 DPI 的工作量太大(我尽量避免 WMI solution)我正在考虑使用一个快速的肮脏技巧:我将创建一个微观 DPI 感知控制台程序这将读取真正的分辨率。

主程序每次需要解析时都会使用启动这个小程序(隐藏)。看起来很简单。对吗?

问题 1:我还有其他(更好的)选择吗?
问题 2:我试图创建那个小程序。虽然它有大约 10 行代码,但其 EXE 大小为 2.1MB,内存占用为 5.4MB! 我可以把它变小吗?如果程序足够小(小于 1MB RAM),我可以一直保留它 运行 而不会激怒用户。

Question 1: Do I have another (better) option?

您可以按照您之前的问题使用 WMI:How to obtain the real screen resolution in a High DPI system?

Question 2: I tried to create that little program. Although is has something like 10 lines of code its EXE size is 2.1MB and its memory footprint is 5.4MB! Can I make it smaller?

诀窍是避免使用任何 VCL 单元,并尽量减少您使用的 RTL 单元的数量。您的目标应该是仅使用 Windows 单元。或者甚至避免它并为您需要的功能创建自己的 Windows API 导入。

另一种选择是使用不同的编程语言创建此程序,这种语言能够更好地删除无用代码。我可能会用一个简短的 C 程序来做到这一点。

这是 30KB 的纯图标,如果你 UPX 它是 15KB,用 Delphi 10 Seattle 编译,在我的系统中大约需要 150-200 毫秒。

program ScreenSupport;

{$APPTYPE CONSOLE}

{$WEAKLINKRTTI ON}
{$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}

uses
  Windows,
  Messages;

{$R *.res}

{$SetPEFlags 00} // IMAGE_FILE_DEBUG_STRIPPED}      // 00
{$SetPEFlags [=10=]04} // IMAGE_FILE_LINE_NUMS_STRIPPED}  // [=10=]04
{$SetPEFlags [=10=]08} // IMAGE_FILE_LOCAL_SYMS_STRIPPED} // [=10=]08
{$SetPEFlags [=10=]01} // IMAGE_FILE_RELOCS_STRIPPED}     // [=10=]01

Const WM_APP = 00;
      msgSendScreenres = WM_APP+1;
      SM_CXVIRTUALSCREEN = 78;
      SM_CYVIRTUALSCREEN = 79;

function GetDesktopHeight: Integer;
begin
  Result := GetSystemMetrics(SM_CYVIRTUALSCREEN);
end;

function GetDesktopWidth: Integer;
begin
  Result := GetSystemMetrics(SM_CXVIRTUALSCREEN);
end;

procedure SendScreenRes(t: THandle);
begin
  if t = 0 then Exit;
  PostMessage(t,msgSendScreenres,GetDesktopWidth,GetDesktopHeight);
end;

function IsAnyParam(s: string): Boolean;
Var a: Integer;
begin
  Result := False;
  if ParamCount = 0 then Exit;
  for a := 1 to ParamCount do
   if ParamStr(a) = s then Exit(True);
end;

function StrToInt(const S: string): Integer;
Var E: Integer;
begin
  Val(S, Result, E);
end;

begin
// screen res requested
  if IsAnyParam('-screenres') then begin
    try
      SendScreenRes(StrToInt(ParamStr(2)));
    except
      Exit;
    end;
  end;
end.

要使用它,请从您的主应用程序调用它:

Const msgSendScreenres = WM_APP+1;

ShellExecute(0,'open','ScreenSupport.exe',PChar('-screenres '+IntToStr(Form1.Handle)),'',SW_HIDE);

然后将其添加到主单元的私有声明中

procedure WMScreenRes(var Msg: TMessage); message msgSendScreenres;

然后抓住它

procedure TForm1.WMScreenRes(var Msg: TMessage);
begin
  ScreenWidth  := Msg.WParam;
  ScreenHeight := Msg.LParam;
end;