Skip to content

Commit

Permalink
Dpi awareness support (#5)
Browse files Browse the repository at this point in the history
* Rework library for supporting dpi awareness:
- All values returns in wpf units
- Project update for StyleCop rules
- Bugfix for SystemInformation.VirtualScreen returning only primary screen

* Added test project for window position check (keep in mind that you should set WindowStyle="None" ResizeMode="NoResize" properties for window or you will get position problem. See dotnet/wpf#4127)

* Fix for MONITORINFOEX initialization after code cleanup

* Final changes

WindowHelper added; Some fixes for SystemInformation;

* code review

* simplify build batch using dotnet tool

* upgrade to netcoreapp3.1 because netcoreapp3.0 is out of support

* bump version to v1.1.0

Co-authored-by: andbayd <andrew@monosnap.com>
Co-authored-by: Michael Denny <micdenny@gmail.com>
  • Loading branch information
3 people authored Sep 23, 2021
1 parent 8908b86 commit 6714ca8
Show file tree
Hide file tree
Showing 17 changed files with 747 additions and 113 deletions.
14 changes: 14 additions & 0 deletions WpfScreenHelper.sln
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WpfScreenHelper.DpiTestWpfApp", "test\WpfScreenHelper.DpiTestWpfApp\WpfScreenHelper.DpiTestWpfApp.csproj", "{FB65A9A4-4376-4CBC-A53E-0415B8D41DBB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{9C9345A1-727D-4C83-98C8-BB3084B4CFD8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0F6F0B6D-1C32-4681-9B6C-0CE92D22C5D1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -24,10 +30,18 @@ Global
{8346EB2E-BFBB-4D63-B974-352A4D8C07F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8346EB2E-BFBB-4D63-B974-352A4D8C07F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8346EB2E-BFBB-4D63-B974-352A4D8C07F1}.Release|Any CPU.Build.0 = Release|Any CPU
{FB65A9A4-4376-4CBC-A53E-0415B8D41DBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FB65A9A4-4376-4CBC-A53E-0415B8D41DBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FB65A9A4-4376-4CBC-A53E-0415B8D41DBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FB65A9A4-4376-4CBC-A53E-0415B8D41DBB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8346EB2E-BFBB-4D63-B974-352A4D8C07F1} = {9C9345A1-727D-4C83-98C8-BB3084B4CFD8}
{FB65A9A4-4376-4CBC-A53E-0415B8D41DBB} = {0F6F0B6D-1C32-4681-9B6C-0CE92D22C5D1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BAF8370C-EA3E-4F99-A94C-FFC687353E0F}
EndGlobalSection
Expand Down
2 changes: 1 addition & 1 deletion build.bat
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ echo Removing old packages...
del Package\*.nupkg >nul 2>&1

echo Building...
"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" "WpfScreenHelper.sln" /property:Configuration=Release
dotnet build -c Release

echo.
echo ------------------------------------------------
Expand Down
16 changes: 16 additions & 0 deletions src/WpfScreenHelper/Enum/WindowPositions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace WpfScreenHelper.Enum
{
public enum WindowPositions
{
Center,
Left,
Top,
Right,
Bottom,
TopLeft,
TopRight,
BottomRight,
BottomLeft,
Maximize
}
}
1 change: 1 addition & 0 deletions src/WpfScreenHelper/ExternDll.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ internal class ExternDll
{
public const string User32 = "user32.dll";
public const string Gdi32 = "gdi32.dll";
public const string Shcore = "shcore.dll";
}
}
16 changes: 11 additions & 5 deletions src/WpfScreenHelper/MouseHelper.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
using System.Windows;

namespace WpfScreenHelper
namespace WpfScreenHelper
{
using System.Windows;

/// <summary>
/// Provides helper functions for mouse cursor.
/// </summary>
public static class MouseHelper
{
/// <summary>
/// Gets the position of the mouse cursor in screen coordinates.
/// </summary>
public static Point MousePosition
{
get
{
NativeMethods.POINT pt = new NativeMethods.POINT();
var pt = new NativeMethods.POINT();
NativeMethods.GetCursorPos(pt);
return new Point(pt.x, pt.y);
}
}
}
}
}
113 changes: 85 additions & 28 deletions src/WpfScreenHelper/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,98 @@ namespace WpfScreenHelper
{
internal static class NativeMethods
{
public delegate bool MonitorEnumProc(IntPtr monitor, IntPtr hdc, IntPtr lprcMonitor, IntPtr lParam);

public enum DpiType
{
EFFECTIVE = 0,
ANGULAR = 1,
RAW = 2
}

public enum PROCESS_DPI_AWARENESS
{
PROCESS_DPI_UNAWARE = 0,
PROCESS_SYSTEM_DPI_AWARE = 1,
PROCESS_PER_MONITOR_DPI_AWARE = 2
}

public enum SystemMetric
{
SM_CXSCREEN = 0,
SM_CYSCREEN = 1,
SM_XVIRTUALSCREEN = 76,
SM_YVIRTUALSCREEN = 77,
SM_CXVIRTUALSCREEN = 78,
SM_CYVIRTUALSCREEN = 79,
SM_CMONITORS = 80
}

public enum SPI : uint
{
/// <summary>
/// Retrieves the size of the work area on the primary display monitor. The work area is the portion of the screen not obscured
/// by the system taskbar or by application desktop toolbars. The pvParam parameter must point to a RECT structure that receives
/// the coordinates of the work area, expressed in virtual screen coordinates.
/// To get the work area of a monitor other than the primary display monitor, call the GetMonitorInfo function.
/// </summary>
SPI_GETWORKAREA = 0x0030
}

[Flags]
public enum SPIF
{
None = 0x00,
/// <summary>Writes the new system-wide parameter setting to the user profile.</summary>
SPIF_UPDATEINIFILE = 0x01,
/// <summary>Broadcasts the WM_SETTINGCHANGE message after updating the user profile.</summary>
SPIF_SENDCHANGE = 0x02,
/// <summary>Same as SPIF_SENDCHANGE.</summary>
SPIF_SENDWININICHANGE = 0x02
}

public const int SPI_GETWORKAREA = 48;

public static readonly HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero);

[DllImport(ExternDll.Shcore, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
public static extern int GetProcessDpiAwareness(IntPtr hprocess, out PROCESS_DPI_AWARENESS value);

[DllImport(ExternDll.Shcore, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
public static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);

[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
public static extern bool GetMonitorInfo(HandleRef hmonitor, [In, Out] MONITORINFOEX info);
public static extern bool GetMonitorInfo(HandleRef hmonitor, [In][Out] MONITORINFOEX info);

[DllImport(ExternDll.User32, ExactSpelling = true)]
[ResourceExposure(ResourceScope.None)]
public static extern bool EnumDisplayMonitors(HandleRef hdc, COMRECT rcClip, MonitorEnumProc lpfnEnum, IntPtr dwData);

[DllImport(ExternDll.User32, ExactSpelling = true)]
[ResourceExposure(ResourceScope.None)]
public static extern IntPtr MonitorFromWindow(HandleRef handle, int flags);

[DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
public static extern int GetSystemMetrics(int nIndex);
public static extern int GetSystemMetrics(SystemMetric nIndex);

[DllImport(ExternDll.User32, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
public static extern bool SystemParametersInfo(int nAction, int nParam, ref RECT rc, int nUpdate);
public static extern bool SystemParametersInfo(SPI nAction, int nParam, ref RECT rc, SPIF nUpdate);

[DllImport(ExternDll.User32, ExactSpelling = true)]
[ResourceExposure(ResourceScope.None)]
public static extern IntPtr MonitorFromPoint(POINTSTRUCT pt, int flags);

[DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
public static extern bool GetCursorPos([In, Out] POINT pt);
public static extern bool GetCursorPos([In][Out] POINT pt);

public static readonly HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero);

public delegate bool MonitorEnumProc(IntPtr monitor, IntPtr hdc, IntPtr lprcMonitor, IntPtr lParam);
[DllImport(ExternDll.User32, SetLastError = true)]
public static extern bool IsProcessDPIAware();

[StructLayout(LayoutKind.Sequential)]
public struct RECT
Expand All @@ -57,21 +118,18 @@ public RECT(int left, int top, int right, int 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;
left = (int)r.Left;
top = (int)r.Top;
right = (int)r.Right;
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); }
}
public Size Size => new Size(right - left, bottom - top);
}

// use this in cases where the Native API takes a POINT not a POINT*
Expand All @@ -81,6 +139,7 @@ public struct POINTSTRUCT
{
public int x;
public int y;

public POINTSTRUCT(int x, int y)
{
this.x = x;
Expand Down Expand Up @@ -116,30 +175,33 @@ public override string ToString()
public class MONITORINFOEX
{
internal int cbSize = Marshal.SizeOf(typeof(MONITORINFOEX));

internal RECT rcMonitor = new RECT();
internal RECT rcWork = new RECT();
internal int dwFlags = 0;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] internal char[] szDevice = new char[32];

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
internal char[] szDevice = new char[32];
}

[StructLayout(LayoutKind.Sequential)]
public class COMRECT
{
public int bottom;
public int left;
public int top;
public int right;
public int bottom;
public int top;

public COMRECT()
{
}

public COMRECT(Rect r)
{
this.left = (int)r.X;
this.top = (int)r.Y;
this.right = (int)r.Right;
this.bottom = (int)r.Bottom;
left = (int)r.X;
top = (int)r.Y;
right = (int)r.Right;
bottom = (int)r.Bottom;
}

public COMRECT(int left, int top, int right, int bottom)
Expand All @@ -160,10 +222,5 @@ public override string ToString()
return "Left = " + left + " Top " + top + " Right = " + right + " Bottom = " + bottom;
}
}

public const int SM_CMONITORS = 80,
SM_CXSCREEN = 0,
SM_CYSCREEN = 1,
SPI_GETWORKAREA = 48;
}
}
Loading

0 comments on commit 6714ca8

Please sign in to comment.