Recently I worked on a XenDesktop project where RES ONE Workspace 2016 was implemented using Local Profiles and Zero Profiling on Windows 2012R2. End users wanted to be able to change the DPI scaling settings on demand within the Citrix session, as they were used to. This functionality was working for them because Roaming Profiles were being used. It is easy to capture these DPI settings using RES ONE Workspace, but applying them during user login using RES ONE Workspace has no effect. This is because these settings must be in place before the shell is loaded to get it to work.
One solution is to create a Active Directory GPO with GPP settings for the different DPI scaling settings and apply it by AD Group Membership. However, this would mean an increase of Servicedesk calls because end users would need to be put in the right AD group to have the DPI setting changed for them.
Therefore I decided to write a small AutoIt application to manage the DPI scaling settings so the end user can change it on their own. It works together with the Active Directory GPO GPP settings. Grab the AutoIt code at the end of this post!
How does DPI scaling work?
The DPI scaling factor is adjusted by setting two registry keys in HKCU\Control Panel\Desktop. These are Win8DpiScaling and LogPixels.
The DPI versus Scale ratio is 1,04166667. This results in the following values:
- 96 – 100%
- 120 – 125%
- 144 – 150%
- 192 – 200%
How does the DPI scaling application work?
In a nutshell, the application creates a text file in the users’ homefolder corresponding to the DPI scale choosen. At the next user logon it checks for the existence of this file in the users’ homefolder and applies the corresponding registry keys.
Start the application within a user session. On startup it reads the current DPI scaling setting from the user registry and displays it in the application.
Use the pulldown menu to select to desired DPI scale setting.
Click Ok.
The application then creates a text file in H:\Personal Settings\DPI. The text file is named accordingly the DPI setting choosen(e.g. Scaling150.txt for 150% DPI scale). Earlier created Scaling text files in this directory will be deleted so only one DPI scale text file is available and used during next logon.
After that it asks you to log out of the system and log back in so the change can become active.
When the end user starts up a new Citrix session the Active Directory GPO GPP setting comes into action. Upon login it checks for the existence of the created text file, and injects the needed registry key for the corresponding DPI scale.
And there you have it, giving back selfcontrol over the DPI scaling setting to the end users!
The needed registry keys
The decimal values used for the DPI scales are:
- 96 – 100%
- 120 – 125%
- 143 – 150%
- 192 – 200%
Please note that Decimal value 143 is in fact corresponding to a DPI scale of 149%. I found that certain applications had issues running at 150% DPI scale. I figured that 1% off wouldn’t be noticed by end users :-).
This solution works on a Windows 2012R2 server. I have not tested it on Windows 10 and 2016.
Grab the code here. The application is built using AutoIt version 3.7.3. I’m no coding expert, but it get’s the job done. As always, test it in your own environment before taking it into production.
#include <GUIConstantsEx.au3> #include <MsgBoxConstants.au3> #include <FileConstants.au3> #include <File.au3> Opt("GUIOnEventMode", 1) ; Change to OnEvent mode ; Here is the array Global $aArray[4] = ["100%", "125%", "150%", "200%"] ; And here we get the elements into a list $sList = "" For $i = 0 To UBound($aArray) - 1 $sList &= "|" & $aArray[$i] Next ; Create a GUI #include <GUIConstantsEx.au3> $hGUI = GUICreate("Set DPI Scaling", 400, 150) GUISetOnEvent($GUI_EVENT_CLOSE, "CLOSEButton") GUICtrlCreateLabel("Select the DPI scaling percentage and click Ok", 10, 50) Local $iOKButton = GUICtrlCreateButton("OK", 120, 80, 60) GUICtrlSetOnEvent($iOKButton, "OKButton") GUISetState(@SW_SHOW, $hGUI) ; Read current DPI scaling from registry and show in Label Local $CurrentDPI = RegRead("HKEY_CURRENT_USER\Control Panel\Desktop", "LogPixels") If $CurrentDPI = "" Then $CurrentDPIPercentage1 = 100 Else $CurrentDPIPercentage1 = ($CurrentDPI) * "1.04166667" EndIf ; Retrieve first 3 characters from the 1th position in the string. Local $CurrentDPIPercentage2 = StringMid($CurrentDPIPercentage1, 1, 3) GUICtrlCreateLabel("Current DPI scaling percentage:", 10, 20, 195, 20) GUICtrlCreateLabel("%", 230, 20, 10, 20) $CurrentDPILabel = GUICtrlCreateLabel($CurrentDPIPercentage2, 210, 20, 20, 20) ; Check for existing DPI Directory in Personal Settings, otherwise create directory Local $sFilePath = "H:\Personal Settings\DPI" Local $iFileExists = FileExists($sFilePath) If $iFileExists Then ; Do nothing Else DirCreate($sFilePath) EndIf Func DelScalingTXTFiles() ; Check for existing Scaling* .TXT files in H:\Personal Settings\DPI Directory. Delete them if any exist, otherwise skip section Local $iFileExists2 = FileExists($sFilePath & "\Scaling*.txt") If $iFileExists2 Then ; Add existing Scaling* .TXT files in Personal Settings\DPI Directory to Array and delete them Local $sSourceFilePath = "H:\Personal Settings\DPI\" $Array = _FileListToArray($sSourceFilePath, "Scaling*.txt") For $i = 1 to $Array[0] FileDelete($sSourceFilePath & $Array[$i]) Next Else ; Do nothing EndIf EndFunc ;==>DelScalingTXTFiles ; Create the combo $hCombo = GUICtrlCreateCombo("", 10, 80, 100, 20) ; And fill it GUICtrlSetData($hCombo, $sList) GUISetState() While 1 Sleep(100) ; Sleep to reduce CPU usage Switch GUIGetMsg() Case $GUI_EVENT_CLOSE Exit Case $hCombo EndSwitch WEnd Func OKButton() ; Note: At this point @GUI_CtrlId would equal $iOKButton, ; and @GUI_WinHandle would equal $hMainGUI If GUICtrlRead($hCombo) = "" Then MsgBox($MB_OK&$MB_ICONWARNING, "Please select percentage", "You have not selected a scaling percentage. Please choose one and click Ok. This program will now exit.") EndIf If GUICtrlRead($hCombo) = "100%" Then Call("DelScalingTXTFiles") FileOpen("H:\Personal Settings\DPI\" & "Scaling100.txt", 1) MsgBox($MB_OK&$MB_ICONINFORMATION, "DPI scaling adjusted", "The screen settings have been changed. Please logoff and start a new session to use the new settings.") EndIf If GUICtrlRead($hCombo) = "125%" Then Call("DelScalingTXTFiles") FileOpen("H:\Personal Settings\DPI\" & "Scaling125.txt", 1) MsgBox($MB_OK&$MB_ICONINFORMATION, "DPI scaling adjusted", "The screen settings have been changed. Please logoff and start a new session to use the new settings.") EndIf If GUICtrlRead($hCombo) = "150%" Then Call("DelScalingTXTFiles") FileOpen("H:\Personal Settings\DPI\" & "Scaling150.txt", 1) MsgBox($MB_OK&$MB_ICONINFORMATION, "DPI scaling adjusted", "The screen settings have been changed. Please logoff and start a new session to use the new settings.") EndIf If GUICtrlRead($hCombo) = "200%" Then Call("DelScalingTXTFiles") FileOpen("H:\Personal Settings\DPI\" & "Scaling200.txt", 1) MsgBox($MB_OK&$MB_ICONINFORMATION, "DPI scaling adjusted", "The screen settings have been changed. Please logoff and start a new session to use the new settings.") EndIf Exit EndFunc ;==>OKButton Func CLOSEButton() ; Note: At this point @GUI_CtrlId would equal $GUI_EVENT_CLOSE, ; and @GUI_WinHandle would equal $hMainGUI ;Un-comment next line if you want to show a reminder ;MsgBox($MB_OK, "Reminder", "Please logoff and start a new session if you changed the screen settings.") Exit EndFunc ;==>CLOSEButton