/ src / modules / launcher / PowerLauncher / Helper / WindowsInteropHelper.cs
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  }