// Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. Code forked from Betsegaw Tadele's https://github.com/betsegaw/windowwalker/ using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; namespace Microsoft.Plugin.WindowWalker.Components { /// /// Represents a specific open window /// public class Window { /// /// Maximum size of a file name /// private const int MaximumFileNameLength = 1000; /// /// The list of owners of a window so that we don't have to /// constantly query for the process owning a specific window /// private static readonly Dictionary _handlesToProcessCache = new Dictionary(); /// /// The list of icons from process so that we don't have to keep /// loading them from disk /// private static readonly Dictionary _processIdsToIconsCache = new Dictionary(); /// /// The handle to the window /// private readonly IntPtr hwnd; /// /// Gets the title of the window (the string displayed at the top of the window) /// public string Title { get { int sizeOfTitle = InteropAndHelpers.GetWindowTextLength(hwnd); if (sizeOfTitle++ > 0) { StringBuilder titleBuffer = new StringBuilder(sizeOfTitle); InteropAndHelpers.GetWindowText(hwnd, titleBuffer, sizeOfTitle); return titleBuffer.ToString(); } else { return string.Empty; } } } /// /// Gets the handle to the window /// public IntPtr Hwnd { get { return hwnd; } } public uint ProcessID { get; set; } /// /// Gets returns the name of the process /// public string ProcessName { get { lock (_handlesToProcessCache) { if (_handlesToProcessCache.Count > 7000) { Debug.Print("Clearing Process Cache because it's size is " + _handlesToProcessCache.Count); _handlesToProcessCache.Clear(); } if (!_handlesToProcessCache.ContainsKey(Hwnd)) { var processName = GetProcessNameFromWindowHandle(Hwnd); if (processName.Length != 0) { _handlesToProcessCache.Add( Hwnd, processName.ToString().Split('\\').Reverse().ToArray()[0]); } else { _handlesToProcessCache.Add(Hwnd, string.Empty); } } if (_handlesToProcessCache[hwnd].ToLower() == "applicationframehost.exe") { new Task(() => { InteropAndHelpers.CallBackPtr callbackptr = new InteropAndHelpers.CallBackPtr((IntPtr hwnd, IntPtr lParam) => { var childProcessId = GetProcessIDFromWindowHandle(hwnd); if (childProcessId != this.ProcessID) { _handlesToProcessCache[Hwnd] = GetProcessNameFromWindowHandle(hwnd); return false; } else { return true; } }); InteropAndHelpers.EnumChildWindows(Hwnd, callbackptr, 0); }).Start(); } return _handlesToProcessCache[hwnd]; } } } /// /// Gets returns the name of the class for the window represented /// public string ClassName { get { StringBuilder windowClassName = new StringBuilder(300); InteropAndHelpers.GetClassName(Hwnd, windowClassName, windowClassName.MaxCapacity); return windowClassName.ToString(); } } /// /// Gets represents the Window Icon for the specified window /// public ImageSource WindowIcon { get { lock (_processIdsToIconsCache) { InteropAndHelpers.GetWindowThreadProcessId(Hwnd, out uint processId); if (!_processIdsToIconsCache.ContainsKey(processId)) { try { Process process = Process.GetProcessById((int)processId); Icon tempIcon = Icon.ExtractAssociatedIcon(process.Modules[0].FileName); _processIdsToIconsCache.Add(processId, Imaging.CreateBitmapSourceFromHIcon( tempIcon.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions())); } catch { BitmapImage failedImage = new BitmapImage(new Uri(@"Images\failedIcon.jpg", UriKind.Relative)); _processIdsToIconsCache.Add(processId, failedImage); } } return _processIdsToIconsCache[processId]; } } } /// /// Gets a value indicating whether is the window visible (might return false if it is a hidden IE tab) /// public bool Visible { get { return InteropAndHelpers.IsWindowVisible(Hwnd); } } /// /// Determines whether the specified window handle identifies an existing window. /// public bool IsWindow { get { return InteropAndHelpers.IsWindow(Hwnd); } } /// /// Get a value indicating whether is the window GWL_EX_STYLE is a toolwindow /// public bool IsToolWindow { get { return (InteropAndHelpers.GetWindowLong(Hwnd, InteropAndHelpers.GWL_EXSTYLE) & (uint)InteropAndHelpers.ExtendedWindowStyles.WS_EX_TOOLWINDOW) == (uint)InteropAndHelpers.ExtendedWindowStyles.WS_EX_TOOLWINDOW; } } /// /// Get a value indicating whether the window GWL_EX_STYLE is an appwindow /// public bool IsAppWindow { get { return (InteropAndHelpers.GetWindowLong(Hwnd, InteropAndHelpers.GWL_EXSTYLE) & (uint)InteropAndHelpers.ExtendedWindowStyles.WS_EX_APPWINDOW) == (uint)InteropAndHelpers.ExtendedWindowStyles.WS_EX_APPWINDOW; } } /// /// Get a value indicating whether the window has ITaskList_Deleted property /// public bool TaskListDeleted { get { return InteropAndHelpers.GetProp(Hwnd, "ITaskList_Deleted") != IntPtr.Zero; } } /// /// Get a value indicating whether the app is a cloaked UWP app /// public bool IsUWPCloaked { get { return (this.IsWindowCloaked() && this.ClassName == "ApplicationFrameWindow"); } } /// /// Determines whether the specified windows is the owner /// public bool IsOwner { get { return InteropAndHelpers.GetWindow(Hwnd, InteropAndHelpers.GetWindowCmd.GW_OWNER) != null; } } /// /// Gets a value indicating whether is the window cloaked. To detect UWP apps in background or win32 apps running in another virtual desktop /// public bool IsWindowCloaked() { int isCloaked = 0; const int DWMWA_CLOAKED = 14; InteropAndHelpers.DwmGetWindowAttribute(this.hwnd, DWMWA_CLOAKED, out isCloaked, sizeof(int)); return isCloaked != 0; } /// /// Gets a value indicating whether returns true if the window is minimized /// public bool Minimized { get { return GetWindowSizeState() == WindowSizeState.Minimized; } } /// /// Initializes a new instance of the class. /// Initializes a new Window representation /// /// the handle to the window we are representing public Window(IntPtr hwnd) { // TODO: Add verification as to whether the window handle is valid this.hwnd = hwnd; } /// /// Highlights a window to help the user identify the window that has been selected /// public void HighlightWindow() { throw new NotImplementedException(); } /// /// Switches dekstop focus to the window /// public void SwitchToWindow() { // The following block is necessary because // 1) There is a weird flashing behavior when trying // to use ShowWindow for switching tabs in IE // 2) SetForegroundWindow fails on minimized windows if (ProcessName.ToLower().Equals("iexplore.exe") || !Minimized) { InteropAndHelpers.SetForegroundWindow(Hwnd); } else { InteropAndHelpers.ShowWindow(Hwnd, InteropAndHelpers.ShowWindowCommands.Restore); } InteropAndHelpers.FlashWindow(Hwnd, true); } /// /// Converts the window name to string along with the process name /// /// The title of the window public override string ToString() { return Title + " (" + ProcessName.ToUpper() + ")"; } /// /// Returns what the window size is /// /// The state (minimized, maximized, etc..) of the window public WindowSizeState GetWindowSizeState() { InteropAndHelpers.GetWindowPlacement(Hwnd, out InteropAndHelpers.WINDOWPLACEMENT placement); switch (placement.ShowCmd) { case InteropAndHelpers.ShowWindowCommands.Normal: return WindowSizeState.Normal; case InteropAndHelpers.ShowWindowCommands.Minimize: case InteropAndHelpers.ShowWindowCommands.ShowMinimized: return WindowSizeState.Minimized; case InteropAndHelpers.ShowWindowCommands.Maximize: // No need for ShowMaximized here since its also of value 3 return WindowSizeState.Maximized; default: // throw new Exception("Don't know how to handle window state = " + placement.ShowCmd); return WindowSizeState.Unknown; } } /// /// Enum to simplify the state of the window /// public enum WindowSizeState { Normal, Minimized, Maximized, Unknown, } /// /// Gets the name of the process using the window handle /// /// The handle to the window /// A string representing the process name or an empty string if the function fails private string GetProcessNameFromWindowHandle(IntPtr hwnd) { uint processId = GetProcessIDFromWindowHandle(hwnd); ProcessID = processId; IntPtr processHandle = InteropAndHelpers.OpenProcess(InteropAndHelpers.ProcessAccessFlags.AllAccess, true, (int)processId); StringBuilder processName = new StringBuilder(MaximumFileNameLength); if (InteropAndHelpers.GetProcessImageFileName(processHandle, processName, MaximumFileNameLength) != 0) { return processName.ToString().Split('\\').Reverse().ToArray()[0]; } else { return string.Empty; } } /// /// Gets the process ID for the Window handle /// /// The handle to the window /// The process ID private uint GetProcessIDFromWindowHandle(IntPtr hwnd) { InteropAndHelpers.GetWindowThreadProcessId(hwnd, out uint processId); return processId; } } }