WindowsInteropHelper.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Drawing; 7 using System.Runtime.InteropServices; 8 using System.Text; 9 using System.Windows; 10 using System.Windows.Forms; 11 using System.Windows.Interop; 12 using System.Windows.Media; 13 14 using Point = System.Windows.Point; 15 16 namespace PowerLauncher.Helper 17 { 18 [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "Matching COM")] 19 public static class WindowsInteropHelper 20 { 21 private const int GWL_STYLE = -16; // WPF's Message code for Title Bar's Style 22 private const int GWL_EX_STYLE = -20; 23 private const int WS_SYSMENU = 0x80000; // WPF's Message code for System Menu 24 private const int WS_EX_TOOLWINDOW = 0x00000080; 25 private static IntPtr _hwnd_shell; 26 private static IntPtr _hwnd_desktop; 27 28 // Accessors for shell and desktop handlers 29 // Will set the variables once and then will return them 30 private static IntPtr HWND_SHELL 31 { 32 get 33 { 34 return _hwnd_shell != IntPtr.Zero ? _hwnd_shell : _hwnd_shell = NativeMethods.GetShellWindow(); 35 } 36 } 37 38 private static IntPtr HWND_DESKTOP 39 { 40 get 41 { 42 return _hwnd_desktop != IntPtr.Zero ? _hwnd_desktop : _hwnd_desktop = NativeMethods.GetDesktopWindow(); 43 } 44 } 45 46 [StructLayout(LayoutKind.Sequential)] 47 internal struct INPUT 48 { 49 public INPUTTYPE Type; 50 public InputUnion Data; 51 52 public static int Size 53 { 54 get { return Marshal.SizeOf(typeof(INPUT)); } 55 } 56 } 57 58 [StructLayout(LayoutKind.Explicit)] 59 [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching COM")] 60 internal struct InputUnion 61 { 62 [FieldOffset(0)] 63 internal MOUSEINPUT mi; 64 [FieldOffset(0)] 65 internal KEYBDINPUT ki; 66 [FieldOffset(0)] 67 internal HARDWAREINPUT hi; 68 } 69 70 [StructLayout(LayoutKind.Sequential)] 71 [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching COM")] 72 internal struct MOUSEINPUT 73 { 74 internal int dx; 75 internal int dy; 76 internal int mouseData; 77 internal uint dwFlags; 78 internal uint time; 79 internal UIntPtr dwExtraInfo; 80 } 81 82 [StructLayout(LayoutKind.Sequential)] 83 [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching COM")] 84 internal struct KEYBDINPUT 85 { 86 internal short wVk; 87 internal short wScan; 88 internal uint dwFlags; 89 internal int time; 90 internal UIntPtr dwExtraInfo; 91 } 92 93 [StructLayout(LayoutKind.Sequential)] 94 [System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.NamingRules", "SA1307:Accessible fields should begin with upper-case letter", Justification = "Matching COM")] 95 internal struct HARDWAREINPUT 96 { 97 internal int uMsg; 98 internal short wParamL; 99 internal short wParamH; 100 } 101 102 internal enum INPUTTYPE : uint 103 { 104 INPUTMOUSE = 0, 105 INPUTKEYBOARD = 1, 106 INPUTHARDWARE = 2, 107 } 108 109 private const string WindowClassConsole = "ConsoleWindowClass"; 110 private const string WindowClassWinTab = "Flip3D"; 111 private const string WindowClassProgman = "Progman"; 112 private const string WindowClassWorkerW = "WorkerW"; 113 114 public static bool IsWindowFullscreen() 115 { 116 // First, check to see if a game is fullscreen, if so, we definitely have 117 // a full-screen window 118 UserNotificationState state; 119 if (Marshal.GetExceptionForHR(NativeMethods.SHQueryUserNotificationState(out state)) == null && 120 state == UserNotificationState.QUNS_RUNNING_D3D_FULL_SCREEN) 121 { 122 return true; 123 } 124 125 // get current active window 126 IntPtr hWnd = NativeMethods.GetForegroundWindow(); 127 128 if (hWnd != IntPtr.Zero && !hWnd.Equals(IntPtr.Zero)) 129 { 130 // if current active window is NOT desktop or shell 131 if (!(hWnd.Equals(HWND_DESKTOP) || hWnd.Equals(HWND_SHELL))) 132 { 133 StringBuilder sb = new StringBuilder(256); 134 _ = NativeMethods.GetClassName(hWnd, sb, sb.Capacity); 135 string windowClass = sb.ToString(); 136 137 // for Win+Tab (Flip3D) 138 if (windowClass == WindowClassWinTab) 139 { 140 return false; 141 } 142 143 _ = NativeMethods.GetWindowRect(hWnd, out RECT appBounds); 144 145 // for console (ConsoleWindowClass), we have to check for negative dimensions 146 if (windowClass == WindowClassConsole) 147 { 148 return appBounds.Top < 0 && appBounds.Bottom < 0; 149 } 150 151 // for desktop (Progman or WorkerW, depends on the system), we have to check 152 if (windowClass == WindowClassProgman || windowClass == WindowClassWorkerW) 153 { 154 IntPtr hWndDesktop = NativeMethods.FindWindowEx(hWnd, IntPtr.Zero, "SHELLDLL_DefView", null); 155 hWndDesktop = NativeMethods.FindWindowEx(hWndDesktop, IntPtr.Zero, "SysListView32", "FolderView"); 156 if (hWndDesktop != IntPtr.Zero && !hWndDesktop.Equals(IntPtr.Zero)) 157 { 158 return false; 159 } 160 } 161 162 Rectangle screenBounds = Screen.FromHandle(hWnd).Bounds; 163 if ((appBounds.Bottom - appBounds.Top) == screenBounds.Height && (appBounds.Right - appBounds.Left) == screenBounds.Width) 164 { 165 return true; 166 } 167 } 168 } 169 170 return false; 171 } 172 173 /// <summary> 174 /// disable windows toolbar's control box 175 /// this will also disable system menu with Alt+Space hotkey 176 /// </summary> 177 public static void DisableControlBox(Window win) 178 { 179 var hwnd = new WindowInteropHelper(win).Handle; 180 _ = NativeMethods.SetWindowLong(hwnd, GWL_STYLE, NativeMethods.GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU); 181 } 182 183 /// <summary> 184 /// Set WS_EX_TOOLWINDOW to make FancyZones ignoring the Window 185 /// </summary> 186 internal static void SetToolWindowStyle(Window win) 187 { 188 var hwnd = new WindowInteropHelper(win).Handle; 189 _ = NativeMethods.SetWindowLong(hwnd, GWL_EX_STYLE, NativeMethods.GetWindowLong(hwnd, GWL_EX_STYLE) | WS_EX_TOOLWINDOW); 190 } 191 192 /// <summary> 193 /// Transforms pixels to Device Independent Pixels used by WPF 194 /// </summary> 195 /// <param name="visual">current window, required to get presentation source</param> 196 /// <param name="unitX">horizontal position in pixels</param> 197 /// <param name="unitY">vertical position in pixels</param> 198 /// <returns>point containing device independent pixels</returns> 199 public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY) 200 { 201 var matrix = GetCompositionTarget(visual).TransformFromDevice; 202 203 return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY)); 204 } 205 206 private static CompositionTarget GetCompositionTarget(Visual visual) 207 { 208 var presentationSource = PresentationSource.FromVisual(visual); 209 if (presentationSource != null) 210 { 211 return presentationSource.CompositionTarget; 212 } 213 else 214 { 215 using var hwndSource = new HwndSource(default); 216 return hwndSource.CompositionTarget; 217 } 218 } 219 220 [StructLayout(LayoutKind.Sequential)] 221 internal struct RECT 222 { 223 public int Left; 224 public int Top; 225 public int Right; 226 public int Bottom; 227 } 228 } 229 }