如何获得每个监视器的比例因子,例如1, 1.25, 1.5
How to get scaling factor for each monitor, e.g. 1, 1.25, 1.5
我知道以前有人问过这个问题,但我已经尝试了我找到的所有答案,其中 none 似乎对我有用。答案似乎在单个监视器上工作,或者需要 window 句柄,或者在 WPF 应用程序中。我有一个没有 UI 的 C# class 库,它是从不同的语言一起调用的。
我被要求确定比例因子,例如1、1.25、1.5 等,用于连接到 C# class 库中当前 PC 的每个显示器。
我还需要提供每台显示器的分辨率和色深。注册表确实保存 DpiValue
,无论如何,在 Windows 10 下
Computer\HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings
但是我不知道如何将它们映射到 Screen
以获得
中返回的匹配分辨率
System.Windows.Forms.Screen.AllScreens
那么有没有人有办法获得这些信息?
我认为您可以像这样获得每个显示器的比例因子。
public void GetScalingFactor()
{
List<double> physicalWidths = new List<double>();
//Get physical width for each monitor
ManagementObjectSearcher searcher = new ManagementObjectSearcher("\root\wmi", "SELECT * FROM WmiMonitorBasicDisplayParams");
foreach (ManagementObject monitor in searcher.Get())
{
//Get the physical width (inch)
double width = (byte)monitor["MaxHorizontalImageSize"] / 2.54;
physicalWidths.Add(width);
}
//Get screen info for each monitor
Screen[] screenList = Screen.AllScreens;
int i = 0;
foreach (Screen screen in screenList)
{
//Get the physical width (pixel)
double physicalWidth;
if (i < physicalWidths.Count)
{
//Get the DPI
uint x, y;
GetDpi(screen, DpiType.Effective, out x, out y);
//Convert inch to pixel
physicalWidth = physicalWidths[i] * x;
}
else
{
physicalWidth = SystemParameters.PrimaryScreenWidth;
}
i++;
//Calculate the scaling
double scaling = 100 * (physicalWidth / screen.Bounds.Width);
double scalingFactor = physicalWidth / screen.Bounds.Width;
//Output the result
Console.WriteLine(scalingFactor);
}
}
并且您还需要添加这些代码以用于获取显示器 DPI(这些代码来自 , thanks @Koopakiller):
public void GetDpi(Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/dd145062(v=vs.85).aspx
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In]System.Drawing.Point pt, [In]uint dwFlags);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511(v=vs.85).aspx
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
我相信我终于(经过长时间的搜索)找到了一个可行的答案,它甚至可以在我的高 DPI Surface Book 2 屏幕上运行。我已经尽可能多地对其进行了测试,到目前为止它总是返回正确的值。
我是这样做的,感谢过去发布代码片段的人,我从中收集了这些代码片段。
首先你需要一个结构来调用user32.dll
中的EnumDisplaySettings
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
private const int CCHDEVICENAME = 0x20;
private const int CCHFORMNAME = 0x20;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public int dmPositionX;
public int dmPositionY;
public ScreenOrientation dmDisplayOrientation;
public int dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
然后需要声明外部函数调用
[DllImport("user32.dll")]
public static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);
然后你需要用它来计算屏幕缩放比例
Screen[] screenList = Screen.AllScreens;
foreach (Screen screen in screenList)
{
DEVMODE dm = new DEVMODE();
dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));
EnumDisplaySettings(screen.DeviceName, -1, ref dm);
var scalingFactor = Math.Round(Decimal.Divide(dm.dmPelsWidth, screen.Bounds.Width), 2);
}
希望其他人觉得这有用。
不幸的是,user3225503 的答案似乎不起作用(不再?)
我的场景:WIN10 20H2,具有 dpi 感知“PerMonitor”的 WPF 应用程序,Framework 4.7.2,2 个具有不同分辨率和不同屏幕缩放比例的显示器(“恐怖场景”):
DEVMODE 结构的 dm.dmPelsWidth 成员始终具有我的显示器的物理分辨率,因此缩放比例始终为 1.0。
我们想要的只是恢复我们的程序及其 windows 就像我们在上次会话中留下的那样,对吗?多亏了 MS,这似乎非常困难!
但另一种方法似乎有效:
在应用程序的清单文件中打开每个显示器的 dpi 感知:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect :
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
始终使用 GetPlacement 和 SetPlacement win32-api 调用 storing/restoring window 个展示位置
SetPlacement 将设置错误的对话框 width/height 如果对话框在辅助显示器上并且每个显示器具有不同的缩放比例。因此,我们需要一个新的因子,取决于每个显示器的缩放因子,以在 window:
的加载事件中更正此问题
事件代码:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
- 代码示例中还有一些好东西,例如WINDOWPLACEMENT 结构的序列化。不要忘记在您的应用程序设置中创建一个成员“Placement”!告诉我这是否适合你:
示例代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Windows;
namespace DpiApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
var plc = ScreenExtensions.GetPlacement(shwnd.Handle);
Properties.Settings.Default.Placement = plc.ToString();
Properties.Settings.Default.Save();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
}
public static class ScreenExtensions
{
public const string User32 = "user32.dll";
public const string shcore = "Shcore.dll";
public static void GetDpi(this System.Windows.Forms.Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
public static double GetScalingForPoint(System.Drawing.Point aPoint)
{
var mon = MonitorFromPoint(aPoint, 2/*MONITOR_DEFAULTTONEAREST*/);
uint dpiX, dpiY;
GetDpiForMonitor(mon, DpiType.Effective, out dpiX, out dpiY);
return (double)dpiX / 96.0;
}
[DllImport(User32)]
private static extern IntPtr MonitorFromPoint([In] System.Drawing.Point pt, [In] uint dwFlags);
[DllImport(shcore)]
private static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);
[DllImport(User32, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport(User32, CharSet = CharSet.Auto, SetLastError = true)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
public static WINDOWPLACEMENT GetPlacement(IntPtr hWnd)
{
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);
GetWindowPlacement(hWnd, ref placement);
return placement;
}
public static bool SetPlacement(IntPtr hWnd, WINDOWPLACEMENT aPlacement)
{
bool erg = SetWindowPlacement(hWnd, ref aPlacement);
return erg;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINTSTRUCT
{
public int x;
public int y;
public POINTSTRUCT(int x, int y)
{
this.x = x;
this.y = y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public RECT(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public RECT(Rect r)
{
this.left = (int)r.Left;
this.top = (int)r.Top;
this.right = (int)r.Right;
this.bottom = (int)r.Bottom;
}
public static RECT FromXYWH(int x, int y, int width, int height)
{
return new RECT(x, y, x + width, y + height);
}
public Size Size
{
get { return new Size(this.right - this.left, this.bottom - this.top); }
}
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT
{
public int length;
public uint flags;
public uint showCmd;
public POINTSTRUCT ptMinPosition;
public POINTSTRUCT ptMaxPosition;
public RECT rcNormalPosition;
public override string ToString()
{
byte[] StructBytes = RawSerialize(this);
return System.Convert.ToBase64String(StructBytes);
}
public void ReadFromBase64String(string aB64)
{
byte[] b64 = System.Convert.FromBase64String(aB64);
var NewWP = ReadStruct<WINDOWPLACEMENT>(b64, 0);
length = NewWP.length;
flags = NewWP.flags;
showCmd = NewWP.showCmd;
ptMinPosition.x = NewWP.ptMinPosition.x;
ptMinPosition.y = NewWP.ptMinPosition.y;
ptMaxPosition.x = NewWP.ptMaxPosition.x;
ptMaxPosition.y = NewWP.ptMaxPosition.y;
rcNormalPosition.left = NewWP.rcNormalPosition.left;
rcNormalPosition.top = NewWP.rcNormalPosition.top;
rcNormalPosition.right = NewWP.rcNormalPosition.right;
rcNormalPosition.bottom = NewWP.rcNormalPosition.bottom;
}
static public T ReadStruct<T>(byte[] aSrcBuffer, int aOffset)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
Buffer.BlockCopy(aSrcBuffer, aOffset, buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
static public T ReadStruct<T>(Stream fs)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
fs.Read(buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
public static byte[] RawSerialize(object anything)
{
int rawsize = Marshal.SizeOf(anything);
byte[] rawdata = new byte[rawsize];
GCHandle handle = GCHandle.Alloc(rawdata, GCHandleType.Pinned);
Marshal.StructureToPtr(anything, handle.AddrOfPinnedObject(), false);
handle.Free();
return rawdata;
}
}
}
}
我知道以前有人问过这个问题,但我已经尝试了我找到的所有答案,其中 none 似乎对我有用。答案似乎在单个监视器上工作,或者需要 window 句柄,或者在 WPF 应用程序中。我有一个没有 UI 的 C# class 库,它是从不同的语言一起调用的。
我被要求确定比例因子,例如1、1.25、1.5 等,用于连接到 C# class 库中当前 PC 的每个显示器。
我还需要提供每台显示器的分辨率和色深。注册表确实保存 DpiValue
,无论如何,在 Windows 10 下
Computer\HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings
但是我不知道如何将它们映射到 Screen
以获得
System.Windows.Forms.Screen.AllScreens
那么有没有人有办法获得这些信息?
我认为您可以像这样获得每个显示器的比例因子。
public void GetScalingFactor()
{
List<double> physicalWidths = new List<double>();
//Get physical width for each monitor
ManagementObjectSearcher searcher = new ManagementObjectSearcher("\root\wmi", "SELECT * FROM WmiMonitorBasicDisplayParams");
foreach (ManagementObject monitor in searcher.Get())
{
//Get the physical width (inch)
double width = (byte)monitor["MaxHorizontalImageSize"] / 2.54;
physicalWidths.Add(width);
}
//Get screen info for each monitor
Screen[] screenList = Screen.AllScreens;
int i = 0;
foreach (Screen screen in screenList)
{
//Get the physical width (pixel)
double physicalWidth;
if (i < physicalWidths.Count)
{
//Get the DPI
uint x, y;
GetDpi(screen, DpiType.Effective, out x, out y);
//Convert inch to pixel
physicalWidth = physicalWidths[i] * x;
}
else
{
physicalWidth = SystemParameters.PrimaryScreenWidth;
}
i++;
//Calculate the scaling
double scaling = 100 * (physicalWidth / screen.Bounds.Width);
double scalingFactor = physicalWidth / screen.Bounds.Width;
//Output the result
Console.WriteLine(scalingFactor);
}
}
并且您还需要添加这些代码以用于获取显示器 DPI(这些代码来自
public void GetDpi(Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/dd145062(v=vs.85).aspx
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In]System.Drawing.Point pt, [In]uint dwFlags);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511(v=vs.85).aspx
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
我相信我终于(经过长时间的搜索)找到了一个可行的答案,它甚至可以在我的高 DPI Surface Book 2 屏幕上运行。我已经尽可能多地对其进行了测试,到目前为止它总是返回正确的值。
我是这样做的,感谢过去发布代码片段的人,我从中收集了这些代码片段。
首先你需要一个结构来调用user32.dll
中的EnumDisplaySettings [StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
private const int CCHDEVICENAME = 0x20;
private const int CCHFORMNAME = 0x20;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public int dmPositionX;
public int dmPositionY;
public ScreenOrientation dmDisplayOrientation;
public int dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
然后需要声明外部函数调用
[DllImport("user32.dll")]
public static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);
然后你需要用它来计算屏幕缩放比例
Screen[] screenList = Screen.AllScreens;
foreach (Screen screen in screenList)
{
DEVMODE dm = new DEVMODE();
dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));
EnumDisplaySettings(screen.DeviceName, -1, ref dm);
var scalingFactor = Math.Round(Decimal.Divide(dm.dmPelsWidth, screen.Bounds.Width), 2);
}
希望其他人觉得这有用。
不幸的是,user3225503 的答案似乎不起作用(不再?)
我的场景:WIN10 20H2,具有 dpi 感知“PerMonitor”的 WPF 应用程序,Framework 4.7.2,2 个具有不同分辨率和不同屏幕缩放比例的显示器(“恐怖场景”):
DEVMODE 结构的 dm.dmPelsWidth 成员始终具有我的显示器的物理分辨率,因此缩放比例始终为 1.0。
我们想要的只是恢复我们的程序及其 windows 就像我们在上次会话中留下的那样,对吗?多亏了 MS,这似乎非常困难!
但另一种方法似乎有效:
在应用程序的清单文件中打开每个显示器的 dpi 感知:
<application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <!-- The combination of below two tags have the following effect : 1) Per-Monitor for >= Windows 10 Anniversary Update 2) System < Windows 10 Anniversary Update --> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> PerMonitor</dpiAwareness> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> </windowsSettings>
始终使用 GetPlacement 和 SetPlacement win32-api 调用 storing/restoring window 个展示位置
SetPlacement 将设置错误的对话框 width/height 如果对话框在辅助显示器上并且每个显示器具有不同的缩放比例。因此,我们需要一个新的因子,取决于每个显示器的缩放因子,以在 window:
的加载事件中更正此问题
事件代码:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
- 代码示例中还有一些好东西,例如WINDOWPLACEMENT 结构的序列化。不要忘记在您的应用程序设置中创建一个成员“Placement”!告诉我这是否适合你:
示例代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Windows;
namespace DpiApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
var plc = ScreenExtensions.GetPlacement(shwnd.Handle);
Properties.Settings.Default.Placement = plc.ToString();
Properties.Settings.Default.Save();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
}
public static class ScreenExtensions
{
public const string User32 = "user32.dll";
public const string shcore = "Shcore.dll";
public static void GetDpi(this System.Windows.Forms.Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
public static double GetScalingForPoint(System.Drawing.Point aPoint)
{
var mon = MonitorFromPoint(aPoint, 2/*MONITOR_DEFAULTTONEAREST*/);
uint dpiX, dpiY;
GetDpiForMonitor(mon, DpiType.Effective, out dpiX, out dpiY);
return (double)dpiX / 96.0;
}
[DllImport(User32)]
private static extern IntPtr MonitorFromPoint([In] System.Drawing.Point pt, [In] uint dwFlags);
[DllImport(shcore)]
private static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);
[DllImport(User32, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport(User32, CharSet = CharSet.Auto, SetLastError = true)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
public static WINDOWPLACEMENT GetPlacement(IntPtr hWnd)
{
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);
GetWindowPlacement(hWnd, ref placement);
return placement;
}
public static bool SetPlacement(IntPtr hWnd, WINDOWPLACEMENT aPlacement)
{
bool erg = SetWindowPlacement(hWnd, ref aPlacement);
return erg;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINTSTRUCT
{
public int x;
public int y;
public POINTSTRUCT(int x, int y)
{
this.x = x;
this.y = y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public RECT(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public RECT(Rect r)
{
this.left = (int)r.Left;
this.top = (int)r.Top;
this.right = (int)r.Right;
this.bottom = (int)r.Bottom;
}
public static RECT FromXYWH(int x, int y, int width, int height)
{
return new RECT(x, y, x + width, y + height);
}
public Size Size
{
get { return new Size(this.right - this.left, this.bottom - this.top); }
}
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT
{
public int length;
public uint flags;
public uint showCmd;
public POINTSTRUCT ptMinPosition;
public POINTSTRUCT ptMaxPosition;
public RECT rcNormalPosition;
public override string ToString()
{
byte[] StructBytes = RawSerialize(this);
return System.Convert.ToBase64String(StructBytes);
}
public void ReadFromBase64String(string aB64)
{
byte[] b64 = System.Convert.FromBase64String(aB64);
var NewWP = ReadStruct<WINDOWPLACEMENT>(b64, 0);
length = NewWP.length;
flags = NewWP.flags;
showCmd = NewWP.showCmd;
ptMinPosition.x = NewWP.ptMinPosition.x;
ptMinPosition.y = NewWP.ptMinPosition.y;
ptMaxPosition.x = NewWP.ptMaxPosition.x;
ptMaxPosition.y = NewWP.ptMaxPosition.y;
rcNormalPosition.left = NewWP.rcNormalPosition.left;
rcNormalPosition.top = NewWP.rcNormalPosition.top;
rcNormalPosition.right = NewWP.rcNormalPosition.right;
rcNormalPosition.bottom = NewWP.rcNormalPosition.bottom;
}
static public T ReadStruct<T>(byte[] aSrcBuffer, int aOffset)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
Buffer.BlockCopy(aSrcBuffer, aOffset, buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
static public T ReadStruct<T>(Stream fs)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
fs.Read(buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
public static byte[] RawSerialize(object anything)
{
int rawsize = Marshal.SizeOf(anything);
byte[] rawdata = new byte[rawsize];
GCHandle handle = GCHandle.Alloc(rawdata, GCHandleType.Pinned);
Marshal.StructureToPtr(anything, handle.AddrOfPinnedObject(), false);
handle.Free();
return rawdata;
}
}
}
}