No announcement yet.

NSIS on HiDPI displays

  • Filter
  • Time
  • Show
Clear All
new posts

  • #46
    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.


    • #47
      Tests : ManifestDPIAware notset

      HTML Code:
      22000: get=PROCESS_SYSTEM_DPI_AWARE sysdpi=144 aware=1
      HTML Code:
      19044: get=PROCESS_SYSTEM_DPI_AWARE sysdpi=144 aware=1
      HTML Code:
      7601: get=0 sysdpi=error aware=1
      HTML Code:
      6002: get=0 sysdpi=error aware=1
      XP (virtual machine)
      HTML Code:
      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?

      HTML Code:
      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".


      • #48
        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?
        IntOp $PostCount $PostCount + 1


        • #49
          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).

          PHP Code:
          !macro DPIX UN
            StrCpy $DPI_SCALE 
          "DWM" # ; user variable
          System::Call 'SHCORE::GetProcessDpiAwareness(p0,*i0r1)'
          ${IfThen} $${|} StrCpy $1 PROCESS_SYSTEM_DPI_AWARE ${|}
          System::Call 'USER32::GetDpiForSystem()i.r2'
          System::Call 'USER32::IsProcessDPIAware()i.r3'
          example 6002get=0 sysdpi=error aware=(Vista)
          #MessageBox MB_OK "${TEMP1}: get=$1 sysdpi=$2 aware=$3"
          StrCpy $SYS_DPI $#
          StrCpy $DPI_AWARE $#

          ${If} $== "PROCESS_SYSTEM_DPI_AWARE" process okay
          ${AndIf} $== aware
          ${If} $== 192
          ${OrIf} $== 168
          ${OrIf} $== 144
          ${OrIf} $== 120
                  StrCpy $DPI_SCALE 
          "" #
          ${If} $== 192
          "/oname=$PluginsDir\modern-header.bmp" "xl${UN}.bmp" 192
          ${ElseIf} $== 168
          "/oname=$PluginsDir\modern-header.bmp" "l${UN}.bmp" 168
          ${ElseIf} $== 144
          "/oname=$PluginsDir\modern-header.bmp" "m${UN}.bmp" 144
          ${ElseIf} $== 120
          "/oname=$PluginsDir\modern-header.bmp" "n${UN}.bmp" 120
          IfNot$DPI_SCALE == "DWM"
          MUI_LOADANDXALIGNIMAGE "NoStretchNoCrop"
          PHP Code:
          Function .onInit
          DPI-aware on-switch provided ManifestDPIAware is "notset"
          GetWinVer ${TEMP1Build
          ${TEMP110240 0 jump 0 ; require Windows 10
          dpi font-scaling was eratic on earlier Windows
          ::Call 'USER32::SetProcessDPIAware()i.r0'

          Function onGUIInit_func
          Re-scaled bitmaps
          !insertmacro DPIX ""
          PHP Code:
          Function un.onInit
          ${TEMP110240 0 jump 0 Windows 10+
          System::Call 'USER32::SetProcessDPIAware()i.r0'

          Function un.onGUIInit_func
          Re-scaled bitmaps
          !insertmacro DPIX "UN"
          May it help someone.


          • #50
            The macro needs to know the plugins folder path, I forgot, at the top. Sorry. My bad.

            PHP Code:
            !define /redef PATH "$PLUGINSDIR\modern-header.bmp" ; for inst.exe and uninst.exe 


            • #51
              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.
              IntOp $PostCount $PostCount + 1


              • #52
                Good to know. Change 10240 to 14393?

                {erratic with R's}

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


                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'`. ?


                • #53
                  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".


                  • #54
                    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
                    IntOp $PostCount $PostCount + 1


                    • #55
                      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]

                      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.


                      • #56
                        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.


                        • #57
                          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.
                          IntOp $PostCount $PostCount + 1


                          • #58
                            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.


                            • #59
                              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.


                              • #60
                                How many times do I have to say it? Use the plug-in, not GetDpiForSystem. GetDpiForSystem is 20 years newer than XP.
                                IntOp $PostCount $PostCount + 1