Как решить проблему с высоким DPI?

Мне нужно получить разрешение рабочего стола из программы Delphi.
Однако, если программа не поддерживает DPI, Windows будет лгать о реальном разрешении экрана, поэтому отсюда возникнут всевозможные проблемы.

Поскольку слишком много работы, чтобы сделать программу полностью осведомленной о DPI (и я стараюсь ИЗБЕГАТЬ решение WMI) Я думаю использовать быстрый подвох: я создам микроскопическую консольную программу с поддержкой DPI, которая будет считывать реальное разрешение.

Основная программа будет запускать эту маленькую программу (скрытую) каждый раз, когда ей нужно разрешение. Кажется, достаточно просто сделать. Верно?

Вопрос 1. Есть ли у меня другой (лучший) вариант?
Вопрос 2. Я пытался создать эту маленькую программу. Хотя он имеет около 10 строк кода, его размер EXE составляет 2,1 МБ, а объем памяти — 5,4 МБ! Могу ли я сделать его меньше? Если программа достаточно мала (менее 1 МБ ОЗУ), я могу оставить ее работать все время, не раздражая пользователей.


person Z80    schedule 28.10.2015    source источник
comment
Одним из решений может быть WMI, но WMI недоступен на всех компьютерах с Windows.   -  person Z80    schedule 28.10.2015
comment
Читал так же, в том числе упоминал о написании небольшой утилиты. Извините, если вы не объяснили разницу. Это не запугивание и не дерьмо. Это пользователи, которые пишут очень похожие вопросы, не объясняя четко, чем они отличаются. Не вините меня, если вы не сделали свою работу по разъяснению различий.   -  person Ken White    schedule 28.10.2015
comment
Я не понимаю, почему ты настаиваешь на том, чтобы быть грубым и агрессивным здесь. Во-первых, вы не имеете права говорить кому-либо держаться подальше — когда вы публикуете здесь, каждый может ответить, отредактировать или проголосовать за закрытие. Не нравится? Не публикуйте. Во-вторых, я не был грубым. Вы разместили вопрос, который оказался дублирующимся, и я закрыл его как таковой; он был вновь открыт. Хватит ныть. В-третьих, я предоставил здесь тысячи достойных ответов, как вы можете видеть в моем профиле. Если вы не хотите, чтобы ваши вопросы были закрыты, напишите более качественные вопросы. Если вы не хотите, чтобы их редактировали, лучше напишите вопросы.   -  person Ken White    schedule 28.10.2015


Ответы (2)


Вопрос 1: Есть ли у меня другой (лучший) вариант?

Вы можете использовать WMI в соответствии с вашим предыдущим вопросом: Как получить реальное разрешение экрана в системе с высоким разрешением?

Вопрос 2: Я пытался создать эту маленькую программу. Хотя он имеет около 10 строк кода, его размер EXE составляет 2,1 МБ, а объем памяти — 5,4 МБ! Могу ли я сделать его меньше?

Хитрость заключается в том, чтобы избежать использования каких-либо модулей VCL и свести к минимуму количество используемых модулей RTL. Ваша цель должна состоять в том, чтобы использовать только устройство Windows. Или даже избегайте этого и создайте собственный импорт Windows API только для тех функций, которые вам нужны.

Другим вариантом было бы создать эту программу с другим языком программирования, который лучше подходит для удаления мертвого кода. Я бы, вероятно, сделал это с помощью короткой программы на C.

person David Heffernan    schedule 28.10.2015
comment
Спасибо, Дэвид - я знаю о вашем предыдущем ответе - о wmi. Но по возможности стараюсь избегать WMI. На большом проценте компьютеров Windows WMI не будет работать должным образом или вообще не будет иметь WMI. Вторая проблема — кроссплатформенная совместимость. Сейчас я взвешиваю обе возможности (WMI против внешнего вспомогательного приложения «DOS») - person Z80; 28.10.2015
comment
Здесь нет ДОСа. Консольное приложение — это программа Windows, а не программа DOS. Не обязательно консольное приложение. Может быть приложением с графическим интерфейсом, которое не отображает графический интерфейс. В конце концов, зачем делать консоль, которая вам не нужна. Кросс-платформенность не имеет значения, поскольку осведомленность о DPI - это специфичная для Windows вещь. Я лично никогда не сталкивался с машиной без WMI, но вы явно знаете свои целевые машины. - person David Heffernan; 28.10.2015
comment
Я знаю. Это была шутка. Вот почему я взял «DOS» в кавычки. Извините за недопонимание. Консольные программы очень похожи на DOS-программы, которые вряд ли можно назвать (в их привлекательности «консолью»). - person Z80; 28.10.2015
comment
Похоже, служба WMI отключена по умолчанию: youtube.com/watch?v=JgsHfc- 4ВТ - person Z80; 28.10.2015

Это 30 КБ с простой иконкой, 15 КБ, если вы UPX, скомпилировано с помощью Delphi 10 Seattle и занимает примерно 150-200 мс в моей системе.

program ScreenSupport;

{$APPTYPE CONSOLE}

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

uses
  Windows,
  Messages;

{$R *.res}

{$SetPEFlags $0200} // IMAGE_FILE_DEBUG_STRIPPED}      // $0200
{$SetPEFlags $0004} // IMAGE_FILE_LINE_NUMS_STRIPPED}  // $0004
{$SetPEFlags $0008} // IMAGE_FILE_LOCAL_SYMS_STRIPPED} // $0008
{$SetPEFlags $0001} // IMAGE_FILE_RELOCS_STRIPPED}     // $0001

Const WM_APP = $8000;
      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;
person hikari    schedule 05.05.2016