Skip to content
⌘ NSIS Forum Archive

NSIS on HiDPI displays

64 posts

rgreen#
Thanks.
I viewed the manifest in ResourceHacker. It looks ok.

<application xmlns="urn:schemas-microsoft-com:asm.v3"><windowsSettings><dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware></windowsSettings></application> 
No to !uninstfinalize.

"ManifestDPIAware true" in the installer had been present before. I did not add it to the uninstaller script, which is a separate *.nsh, until now. I pasted this mesagebox into "\NSIS\Contrib\Modern UI 2\Interface.nsh". It runs before anything else has a chance:

MessageBox mb_ok SysDpi=$0
Installer = 144
Uninstaller = 96 
The spawned instance of the uninstall binary and the regular click-and-run instance return 96 and graphics are blurred by Windows DWM.

Works on Window 10, not 11!
Anders#
You can try adding ManifestDPIAwareness "System" as well but that should not really make a difference.

I don't have Windows 11 to test on.
rgreen#
'system' appears similar to 'true', I think.

The question is whether to use DPI-awareness. Hugh (above) mentioned a quirk of Windows at 120 dpi. The width is more narrow. It opens a whole new can of worms.

If you have header Bmps with fixed proportions for 96, 120, 144, 192, should you stretch the 96 dpi image to 119 and the 120 dpi image to 143?

===

Do we know or can you guess if the uninstaller problem is an 11 bug, or more likely my Windows 11?
Anders#
Originally Posted by rgreen View Post
'system' appears similar to 'true', I think.
Yes but it uses the newer manifest element.

For anyone not using custom pages you can do:

ManifestDPIAware True
ManifestDPIAwareness "PerMonitorV2,System" 
but it does not work correctly on secondary monitors with a different DPI for custom pages. I have even seen unexplainable issues on the license page. The specifics of per-monitor DPI dialog automatic layout has still not been explained by Microsoft AFAIK.

Originally Posted by rgreen View Post
The question is whether to use DPI-awareness. Hugh (above) mentioned a quirk of Windows at 120 dpi. The width is more narrow. It opens a whole new can of worms.

If you have header Bmps with fixed proportions for 96, 120, 144, 192, should you stretch the 96 dpi image to 119 and the 120 dpi image to 143?


Do we know or can you guess if the uninstaller problem is an 11 bug, or more likely my Windows 11?
Don't know and don't know. Seeing as it works on 10 and we are using the correct documented manifest elements it does smell like a 11 bug but I don't really understand why it would only affect the uninstaller.
rgreen#
Very possibly I am using custom pages on a system with one monitor. I'd make two observations, I did not notice distortion of controls at 96, 120 and 144 dpi with "ManifestDPIAware True/System/False". Adding `"ManifestDPIAwareness "PerMonitorV2,System"` did not unfortunately fix the uninstaller.

In the beginning I thought it was a security restriction. The uninstaller is created on the user's computer that would void my original manifest.

Maybe we'll never know. Thanks anyway.
Anders#
Regarding "ManifestDPIAware True/System/False", System and True are the exact same thing. At first only true existed, then when per-monitor became a thing in 8.1 the terminology used by Microsoft changed. System means aware of the DPI on the primary monitor, blurry on others if they have a different DPI.

False should have the same effect as not setting this attribute at all (I say should as setting it adds false to the manifest and not setting it at all does nothing to the manifest).

Ignoring the manifest, you could try
Function un.onInit
System::Call 'SHCORE::SetProcessDpiAwareness(i1)'
FunctionEnd 
rgreen#
If the manifest says 'false', the attribute is redundant if also false. Hmm, ok.

I'm not sure what having no manifest does. In the unlikely event that removing the manifest entirely fixes the uninstaller, it means it's a bug.

Is there a way to programmatically disable manifest creation in the uninstaller? Removing it by force in ResourceHacker from the file breaks the uninstaller. On second thought, I wouldn't take it out. It must be right to have a manifest.
Anders#
The dpi manifest attribute was invented in Vista. No attribute is "XP mode".

As a test you can remove it with ResHacker but you need to run it with /NCRC to get passed the startup check. You can try the System::Call thing, just remove ManifestDPIAware.

For no manifest at all you need RequestExecutionLevel none and XPStyle off at the bottom of your .nsi (and no Manifest lines (doh)).
rgreen#
Removing ManifestDPIAware from the script as you pointed out is the same as 'false'.

I can play with manifests. I got a code off a NSIS forum thread many years ago that allows you to manually configure manifests. So far it hasn't resolved the uninstaller issue:

!define MANIFEST
!ifdef MANIFEST
  !define ResHacker "${ABSDIR}\Resource Hacker\ResourceHacker.exe" ; external tool
  !tempfile tmpexe
  !tempfile tmpmanifest
  !define level "none" ; = 'RequestExecutionLevel user'
  !define build ${NSIS_VERSION}
  !appendfile ${tmpmanifest} '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">\
<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="Nullsoft.NSIS.exehead" type="win32"/>\
<description>Nullsoft Install System "${build}"</description>\
<dependency><dependentAssembly>\
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" \
processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>\
</dependentAssembly></dependency>\
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">\
<security><requestedPrivileges><requestedExecutionLevel level="${level}" uiAccess="false"/></requestedPrivileges></security>\
</trustInfo>\
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"><application>\
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>\
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>\
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>\
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>\
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>\
</application></compatibility>\
</assembly>'
  !packhdr "${tmpexe}" '"${ResHacker}" -addoverwrite "${tmpexe}", "${tmpexe}", "${tmpmanifest}", 24, 1, 1033'
  !delfile "${tmpexe}"
!endif
I took out the dpi-aware lines:

<application xmlns="urn:schemas-microsoft-com:asm.v3"><windowsSettings>\
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>\
</windowsSettings></application>\ 
But, ...

The removed lines appear to be hard-coded. The (un)installers retain the original more or less apart from an added OS, and include that which I removed:

<application xmlns="urn:schemas-microsoft-com:asm.v3"><windowsSettings><dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware></windowsSettings></application> 
Here is the whole manifest, virtually identical to the default NSIS manifest:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
          manifestVersion="1.0">
    <assemblyIdentity version="1.0.0.0"
                      processorArchitecture="*"
                      name="Nullsoft.NSIS.exehead"
                      type="win32"/>
    <description>Nullsoft Install System v3.08</description>
    <dependency>
        <dependentAssembly>
            <assemblyIdentity type="win32"
                              name="Microsoft.Windows.Common-Controls"
                              version="6.0.0.0"
                              processorArchitecture="*"
                              publicKeyToken="6595b64144ccf1df"
                              language="*"/>
        </dependentAssembly>
    </dependency>
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level="requireAdministrator"
                                         uiAccess="false"/>
            </requestedPrivileges>
        </security>
    </trustInfo>
    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
        <application>
            <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
            <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
            <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
            <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
        </application>
    </compatibility>
    <application xmlns="urn:schemas-microsoft-com:asm.v3">
        <windowsSettings>
            <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
        </windowsSettings>
    </application>
</assembly>
All that has changed is the executionLevel: asinvoker -> requireAdministrator

And one more OS.

And, ...

Installer = 144
Uninstaller = 96 
No change.

    <application xmlns="urn:schemas-microsoft-com:asm.v3">
        <windowsSettings>
            <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
        </windowsSettings>
    </application> 
No change, despite my best efforts to remove the code manually.

Windows 10 works and Windows 11 is broken. I can live with that.
rgreen#
Here is the manifest I replaced manually:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
          manifestVersion="1.0">
    <assemblyIdentity version="1.0.0.0"
                      processorArchitecture="*"
                      name="Nullsoft.NSIS.exehead"
                      type="win32"/>
    <description>Nullsoft Install System v3.08</description>
    <dependency>
        <dependentAssembly>
            <assemblyIdentity type="win32"
                              name="Microsoft.Windows.Common-Controls"
                              version="6.0.0.0"
                              processorArchitecture="*"
                              publicKeyToken="6595b64144ccf1df"
                              language="*"/>
        </dependentAssembly>
    </dependency>
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
        <security>
            <requestedPrivileges>
                <requestedExecutionLevel level="requireAdministrator"
                                         uiAccess="false"/>
            </requestedPrivileges>
        </security>
    </trustInfo>
    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
        <application>
            <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
            <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
            <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
            <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
        </application>
    </compatibility>
    <application xmlns="urn:schemas-microsoft-com:asm.v3">
        <windowsSettings>
            <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
        </windowsSettings>
    </application>
</assembly>
I couldn't expunge:

<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> 
That seems to be hard-coded.

Installer = 144
Uninstaller = 96 
Windows 11. No change.
Anders#
This has no manifest at all.

!include MUI2.nsh
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE English
!macro DPI txt
System::Call 'USER32::SetProcessDPIAware()i.r0'
System::Call 'SHCORE::GetProcessDpiAwareness(p0,*i0r1)'
${IfThen} $1 = 1 ${|} StrCpy $1 PROCESS_SYSTEM_DPI_AWARE ${|}
System::Call 'USER32::GetDpiForSystem()i.r2'
System::Call 'USER32::IsProcessDPIAware()i.r3'
MessageBox MB_OK "${txt}: result=$0 get=$1 sysdpi=$2 aware=$3"
!macroend
Function .onInit
!insertmacro DPI Inst
FunctionEnd
Function un.onInit
!insertmacro DPI Uninst
FunctionEnd
Section
WriteUninstaller "$exedir\UnTest.exe"
ExecShell '' "$exedir\UnTest.exe"
Quit
SectionEnd
Section -un.Test
Quit
SectionEnd
ManifestDPIAware notset
RequestExecutionLevel none ; You can set this to User to avoid UAC but it will get a manifest
XPStyle off 
I'm not expecting this to work better than with a manifest but the return values of the functions should be interesting.

Windows 10 gives me: Uninst: result=1 get=PROCESS_SYSTEM_DPI_AWARE sysdpi=120 aware=1
rgreen#
It has a remnant manifest still. I don't know why.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
          manifestVersion="1.0">
    <assemblyIdentity version="1.0.0.0"
                      processorArchitecture="*"
                      name="Nullsoft.NSIS.exehead"
                      type="win32"/>
    <description>Nullsoft Install System v3.08</description>
    <dependency>
        <dependentAssembly>
            <assemblyIdentity type="win32"
                              name="Microsoft.Windows.Common-Controls"
                              version="6.0.0.0"
                              processorArchitecture="*"
                              publicKeyToken="6595b64144ccf1df"
                              language="*"/>
        </dependentAssembly>
    </dependency>
</assembly>
I doubt the manifest matters. The results are interesting.

I did:
ManifestDPIAware notset
RequestExecutionLevel none
XPStyle off
#ManifestSupportedOS all ; (commented out) 
WIN 11:
Installer - result=1 get=PROCESS_SYSTEM_DPI_AWARE sysdpi=120 aware=1
Uninstaller - result=1 get=0 sysdpi=96 aware=0
test ($exedir\UnTest.exe) - result=1 get=PROCESS_SYSTEM_DPI_AWARE sysdpi=120 aware=1
Generating "Untest.exe" overrides the usual uninstaller built at runtime. It was three separate tests, actually.

a) instdir/bin/uninst.exe
b) exedir/UnTest.exe

Yes, an interesting result indeed. "Untest.exe" as the proxy uninstaller is DPI-aware. Who would have thought that?

This was the third test:

c) instdir/bin/UnTest.exe

WIN 11:
test - result=1 get=PROCESS_SYSTEM_DPI_AWARE sysdpi=120 aware=1
Does this mean something in my uninstaller triggers "aware=0"?

It was a good test to do. I was about to give up. 'Thank you!'

The DPI issue remains current for the moment. Windows 10 and Windows 11 treat the uninstallers differently. In my "uninst.exe" there may be a problem with certain Exe files that are sometimes flagged as dangerous by Defender. They are not dangerous, but could that be an issue?
Anders#
I don't understand why instdir/bin/uninst.exe would be special. Is it not generated by WriteUninstaller like everything else? Or could it be compatibility related to the filename? Any why would it still have the manifest? XPStyle on it looks like. Maybe you can create a minimal example .nsi that reproduces the problem?

"result=1 get=0" means the Set call claims to succeed but somehow it still did not apply it to the process.
rgreen#
The uninstaller is normal, depending on where you run it. Windows 11, abnormal.

; "uninst.exe" (ansi) <> "uuninst.exe" (unicode)
DetailPrint "$(DETAIL_DATA)"
SetOutPath "$INSTDIR\bin"
WriteUninstaller "$INSTDIR\bin\${uninstx}" 
I had a code for writing a text string at the end of the file. That is disabled and can't be it.

It may after all be my Windows 11, which is hard to test. My only choice, it would seem, is to disable as much of the installer and uninstaller, until the behavior changes.

UnTest.exe clearly does work, so haystack here I come. I hope there's a needle.
rgreen#
Solved it. Thanks very much for (and to) your help, Anders.

If in the installation that names the folder in which "uninst.exe" resides 'bin', the uninstaller is not dpi-aware run from the location. Instead calling the folder 'binary' or probably anything else during installation fixes the dpi-awareness problem.

How is this possible?

Why is it wrong to name the folder 'bin' in Windows 11?
rgreen#
To do the right thing, I believe I should test this some more on my system.

A possibility is that the Properties for "uninst.exe" were configured to run the file in compatibility mode with DPI handled by Windows.

High DPI scaling override, tick the box inside properties, i.e.: "Override high DPI scaling behavior. Scaling performed by > Application, System, System Enhanced." ... Well, it's a theory. The folder perhaps cached the Properties setting. Not a good theory, however, since I never touched the Properties. To boot, both file and folder were repeatedly uninstalled while testing.

At present, I cannot reproduce the problem by renaming the folder back to 'bin'. The issue seems gone. The uninstaller is now dpi-aware.

I hope to reproduce it by reverting to the install script where the name was originally 'bin'. Maybe it was a combination of all factors combined.

Surely this is connected with NSIS and possibly manifests, so trying to get to the bottom of it might serve some purpose.

It will take awhile before I can get back to the older version. A day or two.

No need to reply.
rgreen#
Tests : ManifestDPIAware notset

11
22000: get=PROCESS_SYSTEM_DPI_AWARE sysdpi=144 aware=1
10
19044: get=PROCESS_SYSTEM_DPI_AWARE sysdpi=144 aware=1
7
7601: get=0 sysdpi=error aware=1
Vista
6002: get=0 sysdpi=error aware=1
XP (virtual machine)
2600: get=0 sysdpi=error aware=error
Graceful errors -- that's good.

On the older OSes, 7 & Vista, switching on dpi-awareness does not much.

They are dpi-aware to the extent that DPI defaults to 96 regardless of screen resolutions and font sizes, and DWM scales bitmaps.

Larger header images are displayed where an actual value for 'sysdpi=' exists, otherwise stretching can be applied to 96 dpi images that are the default based on the application's size.

Windows 11 still won't allow 'bin/uninst.exe' to run at anything other than 96 dpi. I have renamed my folder 'un' as a workaround.

The macro is fantastic. Thank you so much for it.🙂👍

I don't know about the "ManifestDPIAware system" setting. Wouldn't it be easier for every monitor to just apply scaling according to DPI?

System::Call 'USER32::GetDpiForSystem()i.r2'
If not 96 -> scale.

But what the hay, I'm sure there's situation where it is useful to set "system".
Anders#
GetProcessDpiAwareness and GetDpiForSystem are not going to work on older systems (the functions don't even exist). No functions exist on XP. The get dpi function in the plug-in posted in this thread will work. Use the manifest to turn DPI aware on.

ManifestDPIAware system is exactly the same as ManifestDPIAware true.

Why do you want the uninstaller in a subfolder anyway?
rgreen#
No reason. The install mirrors the tree structure of a website containing any number of folders, but the installer can go anywhere.

'bin' is merely a habit.

===

There are a few more quirks in Windows at different DPI settings.

I found `System::Call 'SHCORE::GetProcessDpiAwareness(p0,*i0r1)'` useful in setting the dpi-awareness only for standard sizes. 120, 144, 168, 192.

168 dpi is as bad as 120 dpi when it comes to the application window size. It is more narrow than at 96 dpi.

No one can say what future Windows versions will bring, but because 168 and 120 dpi alter the aspect-ratio by a lot, the developers might decide to streamline all DPI sizes, so there is a nice gradient when you switch to a higher or lower monitor setting. For designers this would mean adapting.

All things considered, "ManifestDPIAware false" is the safest choice, letting Windows re-scale everything up from 96 dpi. I had wondered about using "ManifestDPIAware true" in the past. It seemed like a fair choice, but having investigated it, the "false" setting, as I now think, has a lot going for it.

To switch dpi-aware on and off, I used a variable '$DPI_SCALE'. The default 96 dpi image is 's.bmp' or 'sUN.bmp' (uninstaller).

MACRO
!macro DPIX UN
  StrCpy $DPI_SCALE "DWM" # ; user variable
  System::Call 'SHCORE::GetProcessDpiAwareness(p0,*i0r1)'
  ${IfThen} $1 = 1 ${|} StrCpy $1 PROCESS_SYSTEM_DPI_AWARE ${|}
  System::Call 'USER32::GetDpiForSystem()i.r2'
  System::Call 'USER32::IsProcessDPIAware()i.r3'
  ; example - 6002: get=0 sysdpi=error aware=1 (Vista)
  #MessageBox MB_OK "${TEMP1}: get=$1 sysdpi=$2 aware=$3"
    StrCpy $SYS_DPI $2 #
    StrCpy $DPI_AWARE $3 #
  ${If} $1 == "PROCESS_SYSTEM_DPI_AWARE" ; process okay
  ${AndIf} $3 == 1 ; aware
    ${If} $2 == 192
    ${OrIf} $2 == 168
    ${OrIf} $2 == 144
    ${OrIf} $2 == 120
        StrCpy $DPI_SCALE "" #
      ${If} $2 == 192
        File "/oname=$PluginsDir\modern-header.bmp" "xl${UN}.bmp" ; 192
      ${ElseIf} $2 == 168
        File "/oname=$PluginsDir\modern-header.bmp" "l${UN}.bmp" ; 168
      ${ElseIf} $2 == 144
        File "/oname=$PluginsDir\modern-header.bmp" "m${UN}.bmp" ; 144
      ${ElseIf} $2 == 120
        File "/oname=$PluginsDir\modern-header.bmp" "n${UN}.bmp" ; 120
      ${EndIf}
      ${IfNot} $DPI_SCALE == "DWM"
        !insertmacro MUI_HEADERIMAGE_INITHELPER_LOADIMAGEWITHMACRO \
          MUI_LOADANDXALIGNIMAGE ; "NoStretchNoCrop"
      ${EndIf}
    ${EndIf}
  ${EndIf}
!macroend 
INSTALLER
Function .onInit
  ; DPI-aware on-switch provided ManifestDPIAware is "notset"
  GetWinVer ${TEMP1} Build
  IntCmp ${TEMP1} 10240 0 jump 0 ; require Windows 10
  ; dpi font-scaling was eratic on earlier Windows
  System::Call 'USER32::SetProcessDPIAware()i.r0'
  jump:
  ...
FunctionEnd
Function onGUIInit_func
  ; Re-scaled bitmaps
  !insertmacro DPIX ""
  ...
FunctionEnd 
UNINSTALLER
Function un.onInit
  GetWinVer ${TEMP1} Build
  IntCmp ${TEMP1} 10240 0 jump 0 ; Windows 10+
  System::Call 'USER32::SetProcessDPIAware()i.r0'
  jump:
  ...
FunctionEnd
Function un.onGUIInit_func
  ; Re-scaled bitmaps
  !insertmacro DPIX "UN"
  ...
FunctionEnd 
May it help someone.
rgreen#
The macro needs to know the plugins folder path, I forgot, at the top. Sorry. My bad.

!define /redef PATH "$PLUGINSDIR\modern-header.bmp" ; for inst.exe and uninst.exe 
Anders#
Just checking IsProcessDPIAware is probably enough. And for anyone else, use the plug-in instead of GetDpiForSystem. Your usage of GetDpiForSystem is incorrect, it does not exist in Win 10 RTM, it was added in the Anniversary Update.
rgreen#
Good to know. Change 10240 to 14393?

{erratic with R's}

An innocent question. 'SysCompImg.dll' checks for dpi-awareness.

SysCompImg::GetSysDpi

For me that's really the crux. I'm happy to replace DPI detection with the plugin, which is very handy and useful, but if your system is dpi-unaware via the manifest missing the DPI declaration or "ManifestDPIAware notset", you wouldn't be able to use the plugin?

DPI-awareness relies on `System::Call 'USER32::SetProcessDPIAware()i.r0'`. ?
rgreen#
To expand a little. My small header image (96 dpi) stretches to fit, that it must, in order for Windows to rescale it at higher DPI settings; older versions of Windows, like 7 and Vista, require the image to stretch. Only the alternate high dpi images, unavailable to 7 and Vista, disable stretching. I guess they could stretch, but my experience was that stretching distorted them sometimes. They'd have to exactly fit the UI frame.

To sum it up as follows, to determine whether stretch-to-fit should be enabled, dpi-aware is not set to true automatically. DWM does not stretch images when/if dpi-aware is "true".
Anders#
SysCompImg::GetSysDpi first tries GetDpiForSystem and if it's not there it falls back to the GetDC code I posted earlier in this thread. For systems where DPI awareness is a thing it reports the DPI of the process. On older systems there is just one global DPI value and it returns that (< Vista). The global is usually 96 on old systems but can be set to 120. The plug-in does not care how you became aware.

Awareness requires "ManifestDPIAware true" or System::Call 'USER32::SetProcessDPIAware()i.r0' in .onInit. Microsoft recommends the manifest method but calling the function depending on a condition is the only way to make it dynamic. NSIS might display the unpacking dialog before .onInit in large installers and that could in theory break SetProcessDPIAware FYI.

If you are OK with losing some of the left side of the image you could try the AspectFitHeight image stretch mode.

If you choose to be unaware, "ManifestGdiScaling true" is supposed to make the text less blurry in some cases (192 dpi). This setting is undocumented because it was never tested enough to see if there are unwanted side-effects. See https://blogs.windows.com/windowsdev...-desktop-apps/
rgreen#
Thanks. I prefer the dynamic solution, but I'm flexible as to how best it can be achieved.

Example (small default image)

A) (dynamic)
22000: get=0 sysdpi=96 aware=0

[Image stretched @ 144 dpi]

B)
22000: get=PROCESS_SYSTEM_AWARE sysdpi=144 aware=1

[Image scaled @ 144 dpi]

In case they they don't show up, there are supposed be 81x81 pixel gif images. 'A' is smooth, 'B' pixelated.

No. They can't be previewed. I'll forego the links. It seems pretty clear that dynamic is better.

If you are OK with losing some of the left side of the image you could try the AspectFitHeight image stretch mode.
Getting the exact dimensions right when placing an image is hard what with HiDPI being so variable in Windows. My images are too large. "NoStretchNoCrop" I like.
rgreen#
Originally Posted by Anders View Post
NSIS might display the unpacking dialog before .onInit in large installers and that could in theory break SetProcessDPIAware
That is worrisome, although on fast systems, Windows 10/11, less worrisome. An unpacking dialog happened on Windows 7 recently (to me), due to the embedded large images.
Anders#
MSDN says
Although it is not recommended, it is possible to set the default DPI awareness programmatically. Once a window (an HWND) has been created in your process, changing the DPI awareness mode is no longer supported.
but I don't know if not supported means it wont work or if they just don't approve of it.
rgreen#
My understanding and experience also is that the window is created first, dpi-awareness not yet set, which defaults to "false" on older systems, and then the process that turns on dpi-awareness kicks in and switching it back off is no longer supported.

They'd probably want discourage a programmatic on-off switch. Heeding such warnings to the letter would result in dpi-awareness being set to "false" more often than not, as it is the safest mode. You can't go wrong with it.
rgreen#
Correction on "dpi-awareness not yet set, which defaults to "false" on older systems". It defaults to "true", except on XP. Ambiguously dpi-aware "true" on older systems does not mean they can detect the dpi setting, or so it seems.

`System::Call 'USER32::GetDpiForSystem()i.r2'` returns an error. I think there was much confusion in the early days as what dpi-aware actually meant. I should know, being one of those confused.
Anders#
How many times do I have to say it? Use the plug-in, not GetDpiForSystem. GetDpiForSystem is 20 years newer than XP.