I want to open a powershell GUI Form of a specific size on a Windows 11 screen of 1920 x 1080 pixels. The system scaling in Settings is set to 125%. This cannot be an unusual configuration.
As a test, I try to open a form that is a quarter of the screen in size, as follows:
Add-Type -AssemblyName System.Windows.Forms
$form = New-Object System.Windows.Forms.Form
$form.width = 960
$form.height = 540
$GuiResponse = $Form.ShowDialog()
The problem is simple - this works perfectly in the Powershell ISE, but I cannot get it to work in the Powershell Console because the window is always significantly larger than it should be - I think because the console is performing some sort of reverse scaling.
I can of course do my own scaling and make the window proportionally smaller so that it is the right size when run from the console - but there's an additional associated issue in that any text written to the window is smudgy as it is being inappropriately scaled (at a guess).
There has been some discussion about this type of issue on Stack Overflow and elsewhere, but nothing I've tried resolves this. Yet it seems such an elementary problem, it surely has a simple solution?
CodePudding user response:
You can make your Windows Form DPI Aware via P/Invoke by calling the SetProcessDPIAware
function:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -TypeDefinition @'
public class DPIAware {
[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern bool SetProcessDPIAware();
}
'@
[System.Windows.Forms.Application]::EnableVisualStyles()
[void] [DPIAware]::SetProcessDPIAware()
$form = [System.Windows.Forms.Form]@{
Width = 960
Height = 540
}
$form.ShowDialog()
See Setting the default DPI awareness for a process for more relevant information.
If you would like to call the newer function SetProcessDpiAwarenessContext
which is the one recommended in the MS Docs but do note this is compatible starting from Windows 10, version 1607, here is how the implementation would look:
Add-Type -TypeDefinition @'
using System;
public class DPIAware {
public static readonly IntPtr DPI_AWARENESS_CONTEXT_UNAWARE = (IntPtr) (-1);
public static readonly IntPtr DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = (IntPtr) (-2);
public static readonly IntPtr DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = (IntPtr) (-3);
public static readonly IntPtr DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = (IntPtr) (-4);
public static readonly IntPtr DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED = (IntPtr) (-5);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
public static extern bool SetProcessDpiAwarenessContext(IntPtr Value);
}
'@
if(-not [DPIAware]::SetProcessDpiAwarenessContext([DPIAware]::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) {
throw [System.ComponentModel.Win32Exception]::new([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())
}