如何解决高 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;
我需要从 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;