├── .gitattributes
├── .gitignore
├── .nuget
├── NuGet.Config
├── NuGet.exe
└── NuGet.targets
├── COPYING
├── Content
└── DisableSystemDpiAwareness.cs
├── PerMonitorDpi.csproj
├── PerMonitorDpi.nuspec
├── PerMonitorDpi.sln
├── PerMonitorDpiBehavior.cs
├── Properties
└── AssemblyInfo.cs
├── README.md
└── SafeNativeMethods.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 |
4 | # Folder config file
5 | Desktop.ini
6 |
7 | ## Ignore Visual Studio temporary files, build results, and
8 | ## files generated by popular Visual Studio add-ons.
9 |
10 | # User-specific files
11 | *.suo
12 | *.user
13 |
14 | # Build results
15 | bin/[Dd]ebug/
16 | bin/[Rr]elease/
17 | *_i.c
18 | *_p.c
19 | *.ilk
20 | *.meta
21 | *.obj
22 | *.pch
23 | bin/**/*.pdb
24 | *.pgc
25 | *.pgd
26 | *.rsp
27 | *.sbr
28 | *.tlb
29 | *.tli
30 | *.tlh
31 | *.tmp
32 | *.vspscc
33 | .builds
34 |
35 | # Visual C++ cache files
36 | ipch/
37 | *.aps
38 | *.ncb
39 | *.opensdf
40 | *.sdf
41 |
42 | # Visual Studio profiler
43 | *.psess
44 | *.vsp
45 |
46 | # ReSharper is a .NET coding add-in
47 | _ReSharper*
48 |
49 | # Click-Once directory
50 | publish
51 |
52 | # Others
53 | [Bb]in
54 | [Oo]bj
55 | sql
56 | TestResults
57 | *.Cache
58 | ClientBin
59 | stylecop.*
60 | ~$*
61 | *.dbmdl
62 | Generated_Code #added for RIA/Silverlight projects
63 |
64 | # Custom
65 |
66 | packages
67 | Release/
68 |
69 | Akavache_Mono.userprefs
70 |
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/anaisbetts/PerMonitorDpi/63570f78d9a3ff032bcd0d8a50169af1a57c2090/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/.nuget/NuGet.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildProjectDirectory)\..\
5 |
6 |
7 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
8 | $([System.IO.Path]::Combine($(ProjectDir), "packages.config"))
9 | $([System.IO.Path]::Combine($(SolutionDir), "packages"))
10 |
11 |
12 | $(SolutionDir).nuget
13 | packages.config
14 | $(SolutionDir)packages
15 |
16 |
17 | $(NuGetToolsPath)\nuget.exe
18 | "$(NuGetExePath)"
19 | mono --runtime=v4.0.30319 $(NuGetExePath)
20 |
21 | $(TargetDir.Trim('\\'))
22 |
23 |
24 | ""
25 |
26 |
27 | false
28 |
29 |
30 | false
31 |
32 |
33 | $(NuGetCommand) install "$(PackagesConfig)" -source $(PackageSources) -o "$(PackagesDir)"
34 | $(NuGetCommand) pack "$(ProjectPath)" -p Configuration=$(Configuration) -o "$(PackageOutputDir)" -symbols
35 |
36 |
37 |
38 | RestorePackages;
39 | $(BuildDependsOn);
40 |
41 |
42 |
43 |
44 | $(BuildDependsOn);
45 | BuildPackage;
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
57 |
58 |
61 |
62 |
63 |
64 |
66 |
67 |
70 |
71 |
--------------------------------------------------------------------------------
/COPYING:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Paul Betts
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Content/DisableSystemDpiAwareness.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Media;
2 |
3 | // Disable WPF's built-in scaling, we're doing it ourselves
4 | [assembly:DisableDpiAwareness]
--------------------------------------------------------------------------------
/PerMonitorDpi.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {80E935CF-D6F1-4144-A744-F33E5ADDD9D2}
8 | Library
9 | Properties
10 | PerMonitorDpi
11 | PerMonitorDpi
12 | v4.0
13 | 512
14 |
15 |
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
61 |
--------------------------------------------------------------------------------
/PerMonitorDpi.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PerMonitorDpi
5 | 1.1.1
6 | Paul Betts
7 | false
8 | WPF Behavior to transparently add per-monitor DPI support to your app
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/PerMonitorDpi.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.30110.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PerMonitorDpi", "PerMonitorDpi.csproj", "{80E935CF-D6F1-4144-A744-F33E5ADDD9D2}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {80E935CF-D6F1-4144-A744-F33E5ADDD9D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {80E935CF-D6F1-4144-A744-F33E5ADDD9D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {80E935CF-D6F1-4144-A744-F33E5ADDD9D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {80E935CF-D6F1-4144-A744-F33E5ADDD9D2}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/PerMonitorDpiBehavior.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.Globalization;
5 | using System.Windows;
6 | using System.Windows.Input;
7 | using System.Windows.Interop;
8 | using System.Windows.Media;
9 | using System.Runtime.InteropServices;
10 | using System.Drawing;
11 |
12 | namespace PerMonitorDPI
13 | {
14 | public static class MonitorDpi
15 | {
16 | static bool? isHighDpiMethodSupported = null;
17 | public static bool IsHighDpiMethodSupported()
18 | {
19 | if (isHighDpiMethodSupported != null) return isHighDpiMethodSupported.Value;
20 |
21 | isHighDpiMethodSupported = SafeNativeMethods.DoesWin32MethodExist("shcore.dll", "SetProcessDpiAwareness");
22 |
23 | return isHighDpiMethodSupported.Value;
24 | }
25 |
26 | public static double GetScaleRatioForWindow(IntPtr hWnd)
27 | {
28 | var wpfDpi = 96.0 * PresentationSource.FromVisual(Application.Current.MainWindow).CompositionTarget.TransformToDevice.M11;
29 |
30 | if (IsHighDpiMethodSupported() == false)
31 | {
32 | // Use System DPI
33 | return wpfDpi / 96.0;
34 | }
35 | else
36 | {
37 | var monitor = SafeNativeMethods.MonitorFromWindow(hWnd, MonitorOpts.MONITOR_DEFAULTTONEAREST);
38 |
39 | uint dpiX; uint dpiY;
40 | SafeNativeMethods.GetDpiForMonitor(monitor, MonitorDpiType.MDT_EFFECTIVE_DPI, out dpiX, out dpiY);
41 |
42 | return ((double)dpiX) / wpfDpi;
43 | }
44 | }
45 |
46 | public static double GetScaleRatioForWindow(Window This)
47 | {
48 | var hwndSource = PresentationSource.FromVisual(This) as HwndSource;
49 | return GetScaleRatioForWindow(hwndSource.Handle);
50 | }
51 | }
52 |
53 | public class PerMonitorDpiBehavior
54 | {
55 | HwndSource hwndSource;
56 | IntPtr hwnd;
57 | double currentDpiRatio;
58 |
59 | Window AssociatedObject;
60 |
61 | static PerMonitorDpiBehavior()
62 | {
63 | if (MonitorDpi.IsHighDpiMethodSupported())
64 | {
65 | // NB: We need to call this early before we start doing any
66 | // fiddling with window coordinates / geometry
67 | SafeNativeMethods.SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.PROCESS_PER_MONITOR_DPI_AWARE);
68 | }
69 | }
70 |
71 | public PerMonitorDpiBehavior(Window mainWindow)
72 | {
73 | AssociatedObject = mainWindow;
74 | mainWindow.Loaded += (o, e) => OnAttached();
75 | mainWindow.Closing += (o, e) => OnDetaching();
76 | }
77 |
78 | protected void OnAttached()
79 | {
80 | if (AssociatedObject.IsInitialized)
81 | {
82 | AddHwndHook();
83 | }
84 | else
85 | {
86 | AssociatedObject.SourceInitialized += AssociatedObject_SourceInitialized;
87 | }
88 |
89 | // NB: This allows us to drag-drop URLs from IE11, which would
90 | // normally fail because we run at Medium integrity and most of
91 | // IE runs at Low or AppContainer level.
92 | EnableDragDropFromLowPrivUIPIProcesses();
93 | }
94 |
95 | protected void OnDetaching()
96 | {
97 | RemoveHwndHook();
98 | }
99 |
100 | void AddHwndHook()
101 | {
102 | hwndSource = PresentationSource.FromVisual(AssociatedObject) as HwndSource;
103 | hwndSource.AddHook(HwndHook);
104 | hwnd = new WindowInteropHelper(AssociatedObject).Handle;
105 | }
106 |
107 | void RemoveHwndHook()
108 | {
109 | AssociatedObject.SourceInitialized -= AssociatedObject_SourceInitialized;
110 | hwndSource.RemoveHook(HwndHook);
111 | }
112 |
113 | void AssociatedObject_SourceInitialized(object sender, EventArgs e)
114 | {
115 | AddHwndHook();
116 |
117 | currentDpiRatio = MonitorDpi.GetScaleRatioForWindow(AssociatedObject);
118 | UpdateDpiScaling(currentDpiRatio);
119 | }
120 |
121 | static void EnableDragDropFromLowPrivUIPIProcesses()
122 | {
123 | // UIPI was introduced on Vista
124 | if (Environment.OSVersion.Version.Major < 6) return;
125 | var msgs = new uint[]
126 | {
127 | 0x233, // WM_DROPFILES
128 | 0x48, // WM_COPYDATA
129 | 0x49, // NOBODY KNOWS BUT EVERYONE SAYS TO DO IT
130 | };
131 |
132 | foreach (var msg in msgs)
133 | {
134 | SafeNativeMethods.ChangeWindowMessageFilter(msg, ChangeWindowMessageFilterFlags.Add);
135 | }
136 | }
137 |
138 | [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "GitHub.Extensions.Windows.Native.UnsafeNativeMethods.DwmExtendFrameIntoClientArea(System.IntPtr,GitHub.Extensions.Windows.Native.MARGINS@)")]
139 | IntPtr HwndHook(IntPtr hWnd, int message, IntPtr wParam, IntPtr lParam, ref bool handled)
140 | {
141 | switch (message)
142 | {
143 | case NativeConstants.WM_DPICHANGED:
144 | var rect = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT));
145 |
146 | SafeNativeMethods.SetWindowPos(hWnd, IntPtr.Zero,
147 | rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
148 | SetWindowPosFlags.DoNotChangeOwnerZOrder | SetWindowPosFlags.DoNotActivate | SetWindowPosFlags.IgnoreZOrder);
149 |
150 | var newDpiRatio = MonitorDpi.GetScaleRatioForWindow(AssociatedObject);
151 | if (newDpiRatio != currentDpiRatio) UpdateDpiScaling(newDpiRatio);
152 |
153 | break;
154 | }
155 |
156 | return IntPtr.Zero;
157 | }
158 |
159 | void UpdateDpiScaling(double newDpiRatio)
160 | {
161 | currentDpiRatio = newDpiRatio;
162 |
163 | var firstChild = (Visual)VisualTreeHelper.GetChild(AssociatedObject, 0);
164 | firstChild.SetValue(Window.LayoutTransformProperty, new ScaleTransform(currentDpiRatio, currentDpiRatio));
165 | }
166 |
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("PerMonitorDpi")]
9 | [assembly: AssemblyDescription("WPF Behavior to transparently add per-monitor DPI support to your app")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("PerMonitorDpi")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("e6fda85e-730c-4315-9b49-b16d00bc2607")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.1.1")]
36 | [assembly: AssemblyFileVersion("1.1.1")]
37 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## PerMonitorDpi
2 |
3 | Enable Windows 8.1+ Per-Monitor DPI support for Desktop WPF Apps. Instead of attempting to understand [this long MSDN article](http://msdn.microsoft.com/en-us/library/windows/desktop/ee308410(v=vs.85).aspx), take advantage of my personal suffering and use this instead:
4 |
5 | ```sh
6 | Install-Package PerMonitorDpi
7 | ```
8 |
9 | ### How to Use
10 |
11 | ```cs
12 | public MainWindow()
13 | {
14 | new PerMonitorDpiBehavior(this);
15 | }
16 | ```
17 |
18 | To observe the difference, attach a normal monitor to a Surface Pro 2 or other Retina-DPI monitor, then move your window between the two monitors. Per-Monitor DPI apps will stay sharp, normal apps will have blurred text on the Retina monitor.
19 |
20 | ### What happens on older versions of Windows?
21 |
22 | The Right Thing™ :) Older versions of Windows will use the system-wide DPI information instead.
23 |
--------------------------------------------------------------------------------
/SafeNativeMethods.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Runtime.ConstrainedExecution;
6 | using System.Runtime.InteropServices;
7 | using System.Runtime.Versioning;
8 | using System.Security;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 | using System.Windows;
12 |
13 | namespace PerMonitorDPI
14 | {
15 | static class NativeConstants
16 | {
17 | public const int GCLP_HBRBACKGROUND = -0x0A;
18 | public const int WM_NCCALCSIZE = 0x83;
19 | public const int WM_NCPAINT = 0x85;
20 | public const int WM_NCACTIVATE = 0x86;
21 | public const int WM_GETMINMAXINFO = 0x24;
22 | public const int WM_SETTINGCHANGE = 0x001A;
23 | public const int WM_DPICHANGED = 0x02E0;
24 | }
25 |
26 | enum ChangeWindowMessageFilterFlags : uint
27 | {
28 | Add = 1, Remove = 2
29 | }
30 |
31 | enum PROCESS_DPI_AWARENESS
32 | {
33 | PROCESS_DPI_UNAWARE = 0,
34 | PROCESS_SYSTEM_DPI_AWARE = 1,
35 | PROCESS_PER_MONITOR_DPI_AWARE = 2
36 | }
37 |
38 | [Flags]
39 | enum SetWindowPosFlags : uint
40 | {
41 | AsynchronousWindowPosition = 0x4000,
42 | DeferErase = 0x2000,
43 | DrawFrame = 0x0020,
44 | FrameChanged = 0x0020,
45 | HideWindow = 0x0080,
46 | DoNotActivate = 0x0010,
47 | DoNotCopyBits = 0x0100,
48 | IgnoreMove = 0x0002,
49 | DoNotChangeOwnerZOrder = 0x0200,
50 | DoNotRedraw = 0x0008,
51 | DoNotReposition = 0x0200,
52 | DoNotSendChangingEvent = 0x0400,
53 | IgnoreResize = 0x0001,
54 | IgnoreZOrder = 0x0004,
55 | ShowWindow = 0x0040,
56 | }
57 |
58 | enum DeviceCaps
59 | {
60 | VERTRES = 10,
61 | DESKTOPVERTRES = 117,
62 | }
63 |
64 | enum MonitorOpts : uint
65 | {
66 | MONITOR_DEFAULTTONULL = 0x00000000,
67 | MONITOR_DEFAULTTOPRIMARY = 0x00000001,
68 | MONITOR_DEFAULTTONEAREST = 0x00000002,
69 | }
70 |
71 | enum MonitorDpiType
72 | {
73 | MDT_EFFECTIVE_DPI = 0,
74 | MDT_ANGULAR_DPI = 1,
75 | MDT_RAW_DPI = 2,
76 | }
77 |
78 | ///
79 | /// This class suppresses stack walks for unmanaged code permission.
80 | /// (System.Security.SuppressUnmanagedCodeSecurityAttribute is applied to this class.)
81 | /// This class is for methods that are safe for anyone to call. Callers of these methods
82 | /// are not required to do a full security review to ensure that the usage is secure
83 | /// because the methods are harmless
84 | ///
85 | ///
86 | /// Methods that simply query for information or state that isn't sensitive can be moved
87 | /// here.
88 | ///
89 | [SuppressUnmanagedCodeSecurity]
90 | static class SafeNativeMethods
91 | {
92 | [DllImport("user32.dll")]
93 | internal static extern bool ChangeWindowMessageFilter(uint msg, ChangeWindowMessageFilterFlags flags);
94 |
95 | [DllImport("shcore.dll")]
96 | internal static extern uint SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness);
97 |
98 | [DllImport("user32.dll")]
99 | [return: MarshalAs(UnmanagedType.Bool)]
100 | internal static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
101 |
102 | [DllImport("gdi32.dll")]
103 | internal static extern int GetDeviceCaps(IntPtr hdc, DeviceCaps nIndex);
104 |
105 | [DllImport("user32.dll")]
106 | internal static extern IntPtr MonitorFromWindow(IntPtr hwnd, MonitorOpts dwFlags);
107 |
108 | [DllImport("shcore.dll")]
109 | internal static extern uint GetDpiForMonitor(IntPtr hmonitor, MonitorDpiType dpiType, out uint dpiX, out uint dpiY);
110 |
111 |
112 | // Note: this methods are taken from https://raw.githubusercontent.com/Microsoft/referencesource/9da503f9ef21e8d1f2905c78d4e3e5cbb3d6f85a/mscorlib/microsoft/win32/win32native.cs
113 |
114 | // Note - do NOT use this to call methods. Use P/Invoke, which will
115 | // do much better things w.r.t. marshaling, pinning memory, security
116 | // stuff, better interactions with thread aborts, etc. This is used
117 | // solely by DoesWin32MethodExist for avoiding try/catch EntryPointNotFoundException
118 | // in scenarios where an OS Version check is insufficient
119 | [DllImport("kernel32.dll", CharSet = CharSet.Ansi, BestFitMapping = false, SetLastError = true, ExactSpelling = true)]
120 | [ResourceExposure(ResourceScope.None)]
121 | private static extern IntPtr GetProcAddress(IntPtr hModule, String methodName);
122 |
123 | [DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Unicode, SetLastError = true)]
124 | [ResourceExposure(ResourceScope.Process)]
125 | private static extern IntPtr LoadLibrary(string libFilename);
126 |
127 | [DllImport("kernel32.dll", CharSet = System.Runtime.InteropServices.CharSet.Unicode, SetLastError = true)]
128 | [ResourceExposure(ResourceScope.Process)]
129 | private static extern bool FreeLibrary(IntPtr hModule);
130 |
131 |
132 | [System.Security.SecurityCritical] // auto-generated
133 | internal static bool DoesWin32MethodExist(String moduleName, String methodName)
134 | {
135 | IntPtr hModule = LoadLibrary(moduleName);
136 |
137 | if (hModule == IntPtr.Zero)
138 | {
139 | Debug.Assert(hModule != IntPtr.Zero, "LoadLibrary failed. API must not be available");
140 | return false;
141 | }
142 |
143 | IntPtr functionPointer = GetProcAddress(hModule, methodName);
144 |
145 | FreeLibrary(hModule);
146 | return (functionPointer != IntPtr.Zero);
147 | }
148 | }
149 |
150 | [Serializable]
151 | [StructLayout(LayoutKind.Sequential, Pack = 0)]
152 | struct RECT
153 | {
154 | public int left;
155 | public int top;
156 | public int right;
157 | public int bottom;
158 | public static readonly RECT Empty;
159 |
160 | public int Width
161 | {
162 | get { return Math.Abs(right - left); } // Abs needed for BIDI OS
163 | }
164 |
165 | public int Height
166 | {
167 | get { return bottom - top; }
168 | }
169 |
170 | public RECT(int left, int top, int right, int bottom)
171 | {
172 | this.left = left;
173 | this.top = top;
174 | this.right = right;
175 | this.bottom = bottom;
176 | }
177 |
178 | public RECT(RECT rcSrc)
179 | {
180 | left = rcSrc.left;
181 | top = rcSrc.top;
182 | right = rcSrc.right;
183 | bottom = rcSrc.bottom;
184 | }
185 |
186 | public bool IsEmpty
187 | {
188 | get
189 | {
190 | // BUGBUG : On Bidi OS (hebrew arabic) left > right
191 | return left >= right || top >= bottom;
192 | }
193 | }
194 |
195 | /// Return a user friendly representation of this struct
196 | public override string ToString()
197 | {
198 | if (this == Empty)
199 | {
200 | return "RECT {Empty}";
201 | }
202 | return "RECT { left : " + left + " / top : " + top + " / right : " + right + " / bottom : " + bottom + " }";
203 | }
204 |
205 | /// Determine if 2 RECT are equal (deep compare)
206 | public override bool Equals(object obj)
207 | {
208 | if (!(obj is RECT))
209 | {
210 | return false;
211 | }
212 | return (this == (RECT)obj);
213 | }
214 |
215 | /// Return the HashCode for this struct (not garanteed to be unique)
216 | public override int GetHashCode()
217 | {
218 | return left.GetHashCode() + top.GetHashCode() + right.GetHashCode() + bottom.GetHashCode();
219 | }
220 |
221 | /// Determine if 2 RECT are equal (deep compare)
222 | public static bool operator ==(RECT rect1, RECT rect2)
223 | {
224 | return (rect1.left == rect2.left && rect1.top == rect2.top && rect1.right == rect2.right && rect1.bottom == rect2.bottom);
225 | }
226 |
227 | /// Determine if 2 RECT are different(deep compare)
228 | public static bool operator !=(RECT rect1, RECT rect2)
229 | {
230 | return !(rect1 == rect2);
231 | }
232 | }
233 | }
--------------------------------------------------------------------------------