0

I have a Delphi 3 app which has been distributed far and wide for at least a decade. Today I received a report that the app is not functioning properly on an Asus Transformer T100TA-C1-GR(S) Windows 8 tablet. Specifically, the app is refusing to run because it detects a screen resolution too small for the app to properly display itself. In the app, I have the following conditional check:

 if (Screen.Width < 800) or (Screen.Height < 600) then begin
    // display a message reporting screen resolution too low
    ShowMessage('blah blah...');
    Application.Terminate;

When I compiled a special version of my app to help debug the problem, and gave the app to the complaining user, they report back the following numbers:

    Width: 980
    Height: 550

Here is the extra code I added the special compilation that I then gave to the user:

 ShowMessage('Width: ' + IntToStr(Screen.Width) + #13#10 +
    'Height: ' + IntToStr(Screen.Height));

The user, however, swears that their tablet is configured to 1368x768. They even switched to 1024x768 and the same incorrect numbers are being reported by Delphi.

All TForm.Scaled properties are set to False.

One clue that might help... The screen width and height detection code (above) is run within the following procedure:

 procedure TForm1.WMDisaplayChange(var m: TWMDisplayChange);

Any idea what might be going on?

instrumentally
  • 119
  • 1
  • 3
  • 9
  • Let them set disable display scaling in compatibility settings. – Sertac Akyuz Dec 24 '14 at 20:12
  • 1
    This means the OS is reporting the screen dimensions incorrectly, not Delphi 3. Delphi (even 3) retrieves them via an API call (specifically, GetSystemMetrics), so it's reporting what it was told by the OS. – Ken White Dec 24 '14 at 20:31

1 Answers1

8

Your application is subject to DPI virtualization. Your app has not indicated to the system that it is aware of and supports high DPI display devices. The machine in question uses a font scaling of greater than 125%. That's the high DPI cut off point. Beyond that scaling, the system virtualizes the font scaling for non DPI aware apps. From the documentation:

Windows Vista introduced a feature called DPI virtualization, which provides a level of automatic scaling support to applications that are not DPI–aware. With this feature, Windows scales the size of the text and UI elements of applications that are not DPI-aware so that they are appropriately sized on high DPI settings without changes in the application. This prevents potential usability and readability issues that occur when applications render too small on high DPI screens.

In Windows Vista through Windows 8, this feature provides "virtualized" system metrics and UI elements to not DPI–aware applications, as if they are running at 96 DPI. The application then renders to a 96 DPI off-screen surface and DWM scales the resulting application window to match the DPI setting. For example, if the DPI display setting is 144, DWM scales the application's window by 150%, or 144/96.

A consequence of this is that the system fakes the screen dimensions. When your application is DPI virtualized the system reports the virtualized dimensions rather than the true dimensions.

Best practice is to declare your application to be high DPI aware, and scale the UI according to the user's font scaling preferences. Of course, this is going to be quite a change for you. Possibly not one that you wish to take one.

Another option is to ask the user to use a reduced font scaling. They probably won't be very keen on that either.

Yet another option would be to manifest your app to be high DPI aware and continue not to scale it. Then it would certainly run, but it would fail to respect the user's font scaling preferences. Again, I imagine that the user would be non-plussed.

If you don't currently manifest your app, then doing so now will result in it not being virtualized. And by that I mean app virtualization rather than DPI virtualization. Whilst you really should not still be running virtualized, you may run into trouble if you disable virtualization.

And Sertac suggests another option. Get the user to apply compatibility settings for your app to disable DPI virtualization. You could apply that to the app as it is today, without re-compiling and at least your user would be able to make progress.

Fundamentally, there is a problem with your user's machine setup. If it has 768 vertical pixels and around 140% scaling, that really is a 100% scaled equivalent of 550 pixels. That's not very many pixels. Your application is objecting because the screen is too small and, well, perhaps that really is the case.

This various answers here may be useful to you: How do I make my GUI behave well when Windows font scaling is greater than 100%

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • If the application is checking the resolution at startup for a reason (and not just for annoying the user), high DPI awareness will not be a solution: UI elements won't fit available space. In that case using the compatibility setting that disables display scaling for that application might be the only solution (short of forcing the user to change DPI setting for the system/monitor). – Sertac Akyuz Dec 24 '14 at 20:52
  • @Sertac High DPI with no scaling would allow the app to run, though the user would be pissed off. That's pretty much the same as disabling scaling. – David Heffernan Dec 24 '14 at 20:58
  • Yes, I saw the edit, +1. Compatibility setting might still be preferred though, to give the user an option, he/she may have larger monitor etc.. – Sertac Akyuz Dec 24 '14 at 20:59
  • David responded, "Best practice is to declare your application to be high DPI aware..." And how would I go about doing such a thing using Delphi 3? – instrumentally Dec 24 '14 at 21:01
  • Same as in any Windows app. Add the DPI aware settings to your app manifest. Do you have one? And you'd also need to scale the ui of your app. Not a trivial task. I added a useful link at the bottom of my answer. – David Heffernan Dec 24 '14 at 21:02
  • @Sertac that's the quickest and least intrusive option for the user. – David Heffernan Dec 24 '14 at 21:06
  • David responded: "Add the DPI aware settings to your app manifest." Is there such a thing as a "app manifest" for a Delphi 3 .exe? That's Delphi THREE, not XE3. – instrumentally Dec 24 '14 at 22:57
  • A app manifest is just a resource. Link it to any executable you please. There's no tooling in the IDE. You need to write the manifest text. Write the resource script. Compile the resource. And link it. Which is how I still do it on XE7 as it happens. You really need a manifest. You needed one back in 2005 when Vista came out. – David Heffernan Dec 24 '14 at 22:58
  • @DavidHeffernan: An app manifest can alternatively reside as a separate `.manifest` file in the same folder as the `.exe` file, it does not have to be compiled into the app's resources (though that is the preferred approach). If the `.exe` file is named `myapp.exe`, the manifest must be named `myapp.exe.manifest`, for instance. – Remy Lebeau Dec 24 '14 at 23:18
  • @DavidHeffernan: Manifests where introduced in XP to enable Visual Styles via ComCtl32.dll v6. Vista and later have added additional manifest fields to control other features (UAC, DPI, etc). – Remy Lebeau Dec 24 '14 at 23:21
  • @DavidHeffernan: And FYI, XE2 and later support assigning a custom manifest without having to write a pre-compiled resource script for it. There is a `Custom Manifest` setting in the Project Options to specify a path to a `.manifest` file, which is then linked into the resources for you. – Remy Lebeau Dec 24 '14 at 23:24
  • @Remy Thanks. I do of course know about custom manifests in the IDE. I have other reasons for using my own build process. Manifests weren't introduced just for comctl32 v6. SxS relies on them. – David Heffernan Dec 24 '14 at 23:29
  • @DavidHeffernan: True about SxS, I forgot about that one. – Remy Lebeau Dec 24 '14 at 23:36