├── .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 | } --------------------------------------------------------------------------------