diff --git a/src/ManagedShell.AppBar/AppBarWindow.cs b/src/ManagedShell.AppBar/AppBarWindow.cs index 8bc7d664..91095354 100644 --- a/src/ManagedShell.AppBar/AppBarWindow.cs +++ b/src/ManagedShell.AppBar/AppBarWindow.cs @@ -480,8 +480,9 @@ protected virtual IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lPa DpiHelper.DpiScale = DpiScale; } - // suppress this if we are opening, because we're getting this message as a result of positioning - if (!IsOpening) + // if we are opening, we're getting this message as a result of positioning + // if we are an AppBar, that code will fix our position, so skip in that case to prevent infinite resizing. + if (!IsOpening || AppBarMode != AppBarMode.Normal) { ProcessScreenChange(ScreenSetupReason.DpiChange); } diff --git a/src/ManagedShell.AppBar/FullScreenApp.cs b/src/ManagedShell.AppBar/FullScreenApp.cs index f0bc7505..6d9e89f5 100644 --- a/src/ManagedShell.AppBar/FullScreenApp.cs +++ b/src/ManagedShell.AppBar/FullScreenApp.cs @@ -8,5 +8,6 @@ public class FullScreenApp public IntPtr hWnd; public ScreenInfo screen; public NativeMethods.Rect rect; + public string title; } } \ No newline at end of file diff --git a/src/ManagedShell.AppBar/FullScreenHelper.cs b/src/ManagedShell.AppBar/FullScreenHelper.cs index 78cf0815..05ad6c08 100644 --- a/src/ManagedShell.AppBar/FullScreenHelper.cs +++ b/src/ManagedShell.AppBar/FullScreenHelper.cs @@ -1,10 +1,10 @@ using ManagedShell.Common.Helpers; using ManagedShell.Common.Logging; +using ManagedShell.WindowsTasks; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Text; using System.Windows.Forms; using System.Windows.Threading; using static ManagedShell.Interop.NativeMethods; @@ -13,22 +13,46 @@ namespace ManagedShell.AppBar { public sealed class FullScreenHelper : IDisposable { - private readonly DispatcherTimer fullscreenCheck; + private readonly DispatcherTimer _fullscreenCheck; + private readonly TasksService _tasksService; public ObservableCollection FullScreenApps = new ObservableCollection(); - public FullScreenHelper() + public FullScreenHelper(TasksService tasksService) { - fullscreenCheck = new DispatcherTimer(DispatcherPriority.Background, System.Windows.Application.Current.Dispatcher) + _tasksService = tasksService; + + if (_tasksService != null && EnvironmentHelper.IsWindows8OrBetter) + { + // On Windows 8 and newer, TasksService will tell us when windows enter and exit full screen + _tasksService.FullScreenEntered += TasksService_Event; + _tasksService.FullScreenLeft += TasksService_Event; + _tasksService.MonitorChanged += TasksService_Event; + _tasksService.DesktopActivated += TasksService_Event; + _tasksService.WindowActivated += TasksService_Event; + return; + } + + _fullscreenCheck = new DispatcherTimer(DispatcherPriority.Background, System.Windows.Application.Current.Dispatcher) { Interval = new TimeSpan(0, 0, 0, 0, 100) }; - fullscreenCheck.Tick += FullscreenCheck_Tick; - fullscreenCheck.Start(); + _fullscreenCheck.Tick += FullscreenCheck_Tick; + _fullscreenCheck.Start(); + } + + private void TasksService_Event(object sender, EventArgs e) + { + updateFullScreenWindows(); } private void FullscreenCheck_Tick(object sender, EventArgs e) + { + updateFullScreenWindows(); + } + + private void updateFullScreenWindows() { IntPtr hWnd = GetForegroundWindow(); @@ -47,7 +71,7 @@ private void FullscreenCheck_Tick(object sender, EventArgs e) continue; } - if (appCurrentState != null && app.hWnd != hWnd && + if (appCurrentState != null && app.hWnd != hWnd && app.screen.DeviceName == appCurrentState.screen.DeviceName && Screen.FromHandle(hWnd).DeviceName != appCurrentState.screen.DeviceName) { @@ -62,9 +86,9 @@ private void FullscreenCheck_Tick(object sender, EventArgs e) // remove any changed windows we found if (removeApps.Count > 0) { - ShellLogger.Debug("Removing full screen app(s)"); foreach (FullScreenApp existingApp in removeApps) { + ShellLogger.Debug($"FullScreenHelper: Removing full screen app {existingApp.hWnd} ({existingApp.title})"); FullScreenApps.Remove(existingApp); } } @@ -75,7 +99,7 @@ private void FullscreenCheck_Tick(object sender, EventArgs e) FullScreenApp appNew = getFullScreenApp(hWnd); if (appNew != null) { - ShellLogger.Debug("Adding full screen app"); + ShellLogger.Debug($"FullScreenHelper: Adding full screen app {appNew.hWnd} ({appNew.title})"); FullScreenApps.Add(appNew); } } @@ -118,10 +142,9 @@ private FullScreenApp getFullScreenApp(IntPtr hWnd) return null; } - // make sure this is not the shell desktop - StringBuilder cName = new StringBuilder(256); - GetClassName(hWnd, cName, cName.Capacity); - if (cName.ToString() == "Progman" || cName.ToString() == "WorkerW") + // Make sure this isn't explicitly marked as being non-rude + IntPtr isNonRudeHwnd = GetProp(hWnd, "NonRudeHWND"); + if (isNonRudeHwnd != IntPtr.Zero) { return null; } @@ -137,8 +160,26 @@ private FullScreenApp getFullScreenApp(IntPtr hWnd) } } + ApplicationWindow win = new ApplicationWindow(null, hWnd); + if (!EnvironmentHelper.IsWindows8OrBetter) + { + // make sure this is not the shell desktop + // In Windows 8 and newer, the NonRudeHWND property is set and this is not needed + if (win.ClassName == "Progman" || win.ClassName == "WorkerW") + { + return null; + } + } + + // make sure this is not a transparent window + int styles = win.ExtendedWindowStyles; + if ((styles & (int)ExtendedWindowStyles.WS_EX_LAYERED) != 0 && ((styles & (int)ExtendedWindowStyles.WS_EX_TRANSPARENT) != 0 || (styles & (int)ExtendedWindowStyles.WS_EX_NOACTIVATE) != 0)) + { + return null; + } + // this is a full screen app on this screen - return new FullScreenApp { hWnd = hWnd, screen = screen, rect = rect }; + return new FullScreenApp { hWnd = hWnd, screen = screen, rect = rect, title = win.Title }; } } @@ -148,7 +189,16 @@ private FullScreenApp getFullScreenApp(IntPtr hWnd) private void ResetScreenCache() { // use reflection to empty screens cache - typeof(Screen).GetField("screens", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).SetValue(null, null); + const System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic; + var fi = typeof(Screen).GetField("screens", flags) ?? typeof(Screen).GetField("s_screens", flags); + + if (fi == null) + { + ShellLogger.Warning("FullScreenHelper: Unable to reset screens cache"); + return; + } + + fi.SetValue(null, null); } public void NotifyScreensChanged() @@ -158,7 +208,17 @@ public void NotifyScreensChanged() public void Dispose() { - fullscreenCheck.Stop(); + _fullscreenCheck?.Stop(); + + if (_tasksService != null && EnvironmentHelper.IsWindows8OrBetter) + { + _tasksService.FullScreenEntered -= TasksService_Event; + _tasksService.FullScreenLeft -= TasksService_Event; + _tasksService.MonitorChanged -= TasksService_Event; + _tasksService.DesktopActivated -= TasksService_Event; + _tasksService.WindowActivated -= TasksService_Event; + return; + } } } } diff --git a/src/ManagedShell.AppBar/ManagedShell.AppBar.csproj b/src/ManagedShell.AppBar/ManagedShell.AppBar.csproj index 4100d692..674f46bd 100644 --- a/src/ManagedShell.AppBar/ManagedShell.AppBar.csproj +++ b/src/ManagedShell.AppBar/ManagedShell.AppBar.csproj @@ -9,6 +9,7 @@ + diff --git a/src/ManagedShell.Common/SupportingClasses/NativeWindowEx.cs b/src/ManagedShell.Common/SupportingClasses/NativeWindowEx.cs index 75ad186f..fc6022b6 100644 --- a/src/ManagedShell.Common/SupportingClasses/NativeWindowEx.cs +++ b/src/ManagedShell.Common/SupportingClasses/NativeWindowEx.cs @@ -4,14 +4,19 @@ namespace ManagedShell.Common.SupportingClasses { public class NativeWindowEx : NativeWindow { - public delegate void MessageReceivedEventHandler(Message m); + public delegate void MessageReceivedEventHandler(ref Message m, ref bool handled); public event MessageReceivedEventHandler MessageReceived; protected override void WndProc(ref Message m) { - base.WndProc(ref m); - MessageReceived?.Invoke(m); + bool handled = false; + MessageReceived?.Invoke(ref m, ref handled); + + if (!handled) + { + base.WndProc(ref m); + } } public override void CreateHandle(CreateParams cp) diff --git a/src/ManagedShell.Common/SupportingClasses/ShellWindow.cs b/src/ManagedShell.Common/SupportingClasses/ShellWindow.cs index 47b4bdb1..673cf624 100644 --- a/src/ManagedShell.Common/SupportingClasses/ShellWindow.cs +++ b/src/ManagedShell.Common/SupportingClasses/ShellWindow.cs @@ -23,7 +23,7 @@ public ShellWindow() cp.Y = SystemInformation.VirtualScreen.Top; CreateHandle(cp); - MessageReceived += WndProc; + MessageReceived += ShellWndProc; NativeMethods.SetWindowLong(Handle, NativeMethods.GWL_EXSTYLE, NativeMethods.GetWindowLong(Handle, NativeMethods.GWL_EXSTYLE) & ~(int)NativeMethods.ExtendedWindowStyles.WS_EX_NOACTIVATE); @@ -47,7 +47,7 @@ public void Dispose() NativeMethods.DestroyWindow(Handle); } - private void WndProc(Message msg) + private void ShellWndProc(ref Message msg, ref bool handled) { // Window procedure for the native window // Because other desktop windows are children, we need to pass them some received events. diff --git a/src/ManagedShell.Interop/NativeMethods.User32.cs b/src/ManagedShell.Interop/NativeMethods.User32.cs index 12ea37e5..fe8de3e4 100644 --- a/src/ManagedShell.Interop/NativeMethods.User32.cs +++ b/src/ManagedShell.Interop/NativeMethods.User32.cs @@ -237,7 +237,7 @@ public struct COPYDATASTRUCT public struct SHELLHOOKINFO { public IntPtr hwnd; - public Rect rc; + public ShortRect rc; } [StructLayout(LayoutKind.Sequential)] @@ -1700,6 +1700,9 @@ public enum WindowShowStyle : uint [return: MarshalAs(UnmanagedType.Bool)] public static extern bool IsWindowVisible(IntPtr hWnd); + [DllImport(User32_DllName)] + public static extern bool IsWindowEnabled(IntPtr hWnd); + [DllImport(User32_DllName)] public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); @@ -1898,6 +1901,9 @@ public struct keyboardHookStruct [DllImport(User32_DllName, SetLastError = true)] public static extern IntPtr RemoveProp(IntPtr hWnd, string lpString); + [DllImport(User32_DllName, SetLastError = true)] + public static extern IntPtr GetProp(IntPtr hWnd, string lpString); + public enum TBPFLAG { TBPF_NOPROGRESS = 0, @@ -2910,7 +2916,19 @@ public enum HSHELL : uint /// ENDTASK = 10, FLASH = (REDRAW | HSHELL_HIGHBIT), - RUDEAPPACTIVATED = (WINDOWACTIVATED | HSHELL_HIGHBIT) + RUDEAPPACTIVATED = (WINDOWACTIVATED | HSHELL_HIGHBIT), + /// + /// A window has moved to another monitor. Windows 8 and newer only. + /// + MONITORCHANGED = 16, + /// + /// A window has become full-screen. Windows 8 and newer only. + /// + FULLSCREENENTER = 53, + /// + /// A window has left full-screen. Windows 8 and newer only. + /// + FULLSCREENEXIT = 54 } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] diff --git a/src/ManagedShell.Interop/NativeMethods.cs b/src/ManagedShell.Interop/NativeMethods.cs index f6d9043b..43d12352 100644 --- a/src/ManagedShell.Interop/NativeMethods.cs +++ b/src/ManagedShell.Interop/NativeMethods.cs @@ -29,6 +29,30 @@ public Rect(int left, int top, int right, int bottom) public int Height => Bottom - Top; } + /// + /// Used by HSHELL_GETMINRECT + /// + [StructLayout(LayoutKind.Sequential)] + public struct ShortRect + { + public ShortRect(short left, short top, short right, short bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + public short Left; + public short Top; + public short Right; + public short Bottom; + + public int Width => Right - Left; + + public int Height => Bottom - Top; + } + public struct POINT { public POINT(long x, long y) diff --git a/src/ManagedShell.WindowsTasks/ApplicationWindow.cs b/src/ManagedShell.WindowsTasks/ApplicationWindow.cs index fade969f..436d4543 100644 --- a/src/ManagedShell.WindowsTasks/ApplicationWindow.cs +++ b/src/ManagedShell.WindowsTasks/ApplicationWindow.cs @@ -20,6 +20,10 @@ public class ApplicationWindow : IEquatable, INotifyPropertyC private readonly TasksService _tasksService; StringBuilder titleBuilder = new StringBuilder(TITLE_LENGTH); + public delegate void GetButtonRectEventHandler(ref NativeMethods.ShortRect rect); + + public event GetButtonRectEventHandler GetButtonRect; + public ApplicationWindow(TasksService tasksService, IntPtr handle) { _tasksService = tasksService; @@ -120,6 +124,39 @@ public string Category } } + private string _className; + + public string ClassName + { + get + { + if (_className == null) + { + setClassName(); + } + + return _className; + } + } + + private void setClassName() + { + string className = ""; + try + { + StringBuilder cName = new StringBuilder(256); + NativeMethods.GetClassName(Handle, cName, cName.Capacity); + className = cName.ToString(); + } + catch { } + + if (_className != className) + { + _className = className; + OnPropertyChanged("ClassName"); + } + } + private string _title; public string Title @@ -324,6 +361,8 @@ public bool CanAddToTaskbar } } + public bool CanMinimize => (WindowStyles & (int)NativeMethods.WindowStyles.WS_MINIMIZEBOX) != 0 && NativeMethods.IsWindowEnabled(Handle); + private bool? _showInTaskbar; // True if this window should be shown in the taskbar @@ -373,10 +412,7 @@ private bool getShowInTaskbar() } // UWP shell windows that are not cloaked should be hidden from the taskbar, too. - StringBuilder cName = new StringBuilder(256); - NativeMethods.GetClassName(Handle, cName, cName.Capacity); - string className = cName.ToString(); - if (className == "ApplicationFrameWindow" || className == "Windows.UI.Core.CoreWindow" || className == "StartMenuSizingFrame") + if (ClassName == "ApplicationFrameWindow" || ClassName == "Windows.UI.Core.CoreWindow" || ClassName == "StartMenuSizingFrame") { if ((ExtendedWindowStyles & (int)NativeMethods.ExtendedWindowStyles.WS_EX_WINDOWEDGE) == 0) { @@ -384,7 +420,7 @@ private bool getShowInTaskbar() return false; } } - else if (!EnvironmentHelper.IsWindows10OrBetter && (className == "ImmersiveBackgroundWindow" || className == "SearchPane" || className == "NativeHWNDHost" || className == "Shell_CharmWindow" || className == "ImmersiveLauncher") && WinFileName.ToLower().Contains("explorer.exe")) + else if (!EnvironmentHelper.IsWindows10OrBetter && (ClassName == "ImmersiveBackgroundWindow" || ClassName == "SearchPane" || ClassName == "NativeHWNDHost" || ClassName == "Shell_CharmWindow" || ClassName == "ImmersiveLauncher") && WinFileName.ToLower().Contains("explorer.exe")) { ShellLogger.Debug($"ApplicationWindow: Hiding immersive shell window {Title}"); return false; @@ -525,6 +561,13 @@ internal void SetMonitor() HMonitor = NativeMethods.MonitorFromWindow(Handle, NativeMethods.MONITOR_DEFAULTTONEAREST); } + internal NativeMethods.ShortRect GetButtonRectFromShell() + { + NativeMethods.ShortRect rect = new NativeMethods.ShortRect(); + GetButtonRect?.Invoke(ref rect); + return rect; + } + public void SetOverlayIcon(IntPtr hIcon) { if (hIcon == IntPtr.Zero) @@ -601,12 +644,14 @@ public void BringToFront() public void Minimize() { - if ((WindowStyles & (int)NativeMethods.WindowStyles.WS_MINIMIZEBOX) != 0) + if (!CanMinimize) { - NativeMethods.GetWindowThreadProcessId(Handle, out uint procId); - NativeMethods.AllowSetForegroundWindow(procId); - NativeMethods.PostMessage(Handle, (int)NativeMethods.WM.SYSCOMMAND, (IntPtr)NativeMethods.SC_MINIMIZE, IntPtr.Zero); + return; } + + NativeMethods.GetWindowThreadProcessId(Handle, out uint procId); + NativeMethods.AllowSetForegroundWindow(procId); + NativeMethods.PostMessage(Handle, (int)NativeMethods.WM.SYSCOMMAND, (IntPtr)NativeMethods.SC_MINIMIZE, IntPtr.Zero); } public void Restore() diff --git a/src/ManagedShell.WindowsTasks/TasksService.cs b/src/ManagedShell.WindowsTasks/TasksService.cs index cc921e9f..dfe20e1c 100644 --- a/src/ManagedShell.WindowsTasks/TasksService.cs +++ b/src/ManagedShell.WindowsTasks/TasksService.cs @@ -1,6 +1,5 @@ using ManagedShell.Common.Helpers; using ManagedShell.Common.Logging; -using ManagedShell.Interop; using System; using System.Collections.ObjectModel; using System.Diagnostics; @@ -19,8 +18,11 @@ public class TasksService : DependencyObject, IDisposable { public static readonly IconSize DEFAULT_ICON_SIZE = IconSize.Small; - public event EventHandler WindowActivated; + public event EventHandler WindowActivated; public event EventHandler DesktopActivated; + public event EventHandler FullScreenEntered; + public event EventHandler FullScreenLeft; + public event EventHandler MonitorChanged; private NativeWindowEx _HookWin; private object _windowsLock = new object(); @@ -119,9 +121,10 @@ internal void Initialize(bool withMultiMonTracking) } } - if (withMultiMonTracking) + if (withMultiMonTracking && !EnvironmentHelper.IsWindows8OrBetter) { // set event hook for move events + // In Windows 8 and newer, use HSHELL_MONITORCHANGED instead moveEventProc = MoveEventCallback; if (moveEventHook == IntPtr.Zero) @@ -266,14 +269,16 @@ private ApplicationWindow addWindow(IntPtr hWnd, ApplicationWindow.WindowState i if (initialState != ApplicationWindow.WindowState.Inactive) win.State = initialState; // add window unless we need to validate it is eligible to show in taskbar - if (!sanityCheck || win.CanAddToTaskbar) Windows.Add(win); + if (!sanityCheck || win.CanAddToTaskbar) + { + Windows.Add(win); + ShellLogger.Debug($"TasksService: Added window {hWnd} ({win.Title})"); + } // Only send TaskbarButtonCreated if we are shell, and if OS is not Server Core // This is because if Explorer is running, it will send the message, so we don't need to if (EnvironmentHelper.IsAppRunningAsShell) sendTaskbarButtonCreatedMessage(win.Handle); - ShellLogger.Debug($"TasksService: Added window {hWnd} ({win.Title})"); - return win; } @@ -307,8 +312,10 @@ private void redrawWindow(ApplicationWindow win) } } - private void ShellWinProc(Message msg) + private void ShellWinProc(ref Message msg, ref bool handled) { + Message msgCopy = msg; + handled = true; if (msg.Msg == WM_SHELLHOOKMESSAGE) { try @@ -318,13 +325,13 @@ private void ShellWinProc(Message msg) switch ((HSHELL)msg.WParam.ToInt32()) { case HSHELL.WINDOWCREATED: - if (!Windows.Any(i => i.Handle == msg.LParam)) + if (!Windows.Any(i => i.Handle == msgCopy.LParam)) { addWindow(msg.LParam); } else { - ApplicationWindow win = Windows.First(wnd => wnd.Handle == msg.LParam); + ApplicationWindow win = Windows.First(wnd => wnd.Handle == msgCopy.LParam); win.UpdateProperties(); } break; @@ -334,9 +341,9 @@ private void ShellWinProc(Message msg) break; case HSHELL.WINDOWREPLACING: - if (Windows.Any(i => i.Handle == msg.LParam)) + if (Windows.Any(i => i.Handle == msgCopy.LParam)) { - ApplicationWindow win = Windows.First(wnd => wnd.Handle == msg.LParam); + ApplicationWindow win = Windows.First(wnd => wnd.Handle == msgCopy.LParam); win.State = ApplicationWindow.WindowState.Inactive; win.SetShowInTaskbar(); } @@ -361,9 +368,9 @@ private void ShellWinProc(Message msg) { ApplicationWindow win = null; - if (Windows.Any(i => i.Handle == msg.LParam)) + if (Windows.Any(i => i.Handle == msgCopy.LParam)) { - win = Windows.First(wnd => wnd.Handle == msg.LParam); + win = Windows.First(wnd => wnd.Handle == msgCopy.LParam); win.State = ApplicationWindow.WindowState.Active; win.SetShowInTaskbar(); ShellLogger.Debug($"TasksService: Activated window {win.Handle} ({win.Title})"); @@ -381,7 +388,7 @@ private void ShellWinProc(Message msg) wind.SetShowInTaskbar(); } - WindowActivatedEventArgs args = new WindowActivatedEventArgs + WindowEventArgs args = new WindowEventArgs { Window = win }; @@ -396,9 +403,9 @@ private void ShellWinProc(Message msg) break; case HSHELL.FLASH: - if (Windows.Any(i => i.Handle == msg.LParam)) + if (Windows.Any(i => i.Handle == msgCopy.LParam)) { - ApplicationWindow win = Windows.First(wnd => wnd.Handle == msg.LParam); + ApplicationWindow win = Windows.First(wnd => wnd.Handle == msgCopy.LParam); if (win.State != ApplicationWindow.WindowState.Active) { @@ -421,17 +428,10 @@ private void ShellWinProc(Message msg) removeWindow(msg.LParam); break; - case HSHELL.GETMINRECT: - SHELLHOOKINFO winHandle = (SHELLHOOKINFO)Marshal.PtrToStructure(msg.LParam, typeof(SHELLHOOKINFO)); - winHandle.rc = new NativeMethods.Rect { Bottom = 100, Left = 0, Right = 100, Top = 0 }; - Marshal.StructureToPtr(winHandle, msg.LParam, true); - msg.Result = winHandle.hwnd; - return; // return here so the result isnt reset to DefWindowProc - case HSHELL.REDRAW: - if (Windows.Any(i => i.Handle == msg.LParam)) + if (Windows.Any(i => i.Handle == msgCopy.LParam)) { - ApplicationWindow win = Windows.First(wnd => wnd.Handle == msg.LParam); + ApplicationWindow win = Windows.First(wnd => wnd.Handle == msgCopy.LParam); if (win.State == ApplicationWindow.WindowState.Flashing) { @@ -446,6 +446,48 @@ private void ShellWinProc(Message msg) } break; + case HSHELL.MONITORCHANGED: + if (Windows.Any(i => i.Handle == msgCopy.LParam)) + { + ApplicationWindow win = Windows.First(wnd => wnd.Handle == msgCopy.LParam); + win.SetMonitor(); + ShellLogger.Debug($"TasksService: Monitor changed for {win.Handle} ({win.Title})"); + + WindowEventArgs args = new WindowEventArgs + { + Window = win + }; + + MonitorChanged?.Invoke(this, args); + } + break; + + case HSHELL.FULLSCREENENTER: + FullScreenEntered?.Invoke(this, new EventArgs()); + break; + + case HSHELL.FULLSCREENEXIT: + FullScreenLeft?.Invoke(this, new EventArgs()); + break; + + case HSHELL.GETMINRECT: + SHELLHOOKINFO minRectInfo = Marshal.PtrToStructure(msg.LParam); + if (Windows.Any(i => i.Handle == minRectInfo.hwnd)) + { + ApplicationWindow win = Windows.First(wnd => wnd.Handle == minRectInfo.hwnd); + minRectInfo.rc = win.GetButtonRectFromShell(); + + if (minRectInfo.rc.Width <= 0 && minRectInfo.rc.Height <= 0) + { + break; + } + Marshal.StructureToPtr(minRectInfo, msg.LParam, false); + msg.Result = (IntPtr)1; + ShellLogger.Debug($"TasksService: MinRect {minRectInfo.rc.Width}x{minRectInfo.rc.Height} provided for {win.Handle} ({win.Title})"); + return; // return here so the result isnt reset to DefWindowProc + } + break; + // TaskMan needs to return true if we provide our own task manager to prevent explorers. // case HSHELL.TASKMAN: // SingletonLogger.Instance.Info("TaskMan Message received."); @@ -483,7 +525,16 @@ private void ShellWinProc(Message msg) return; case (int)WM.USER + 60: // MarkFullscreenWindow + // Also sends WM_SHELLHOOK message ShellLogger.Debug("TasksService: ITaskbarList: MarkFullscreenWindow HWND:" + msg.LParam + " Entering? " + msg.WParam); + if (msg.WParam == IntPtr.Zero) + { + FullScreenLeft?.Invoke(this, new EventArgs()); + } + else + { + FullScreenEntered?.Invoke(this, new EventArgs()); + } msg.Result = IntPtr.Zero; return; case (int)WM.USER + 64: @@ -493,7 +544,7 @@ private void ShellWinProc(Message msg) win = new ApplicationWindow(this, msg.WParam); if (Windows.Contains(win)) { - win = Windows.First(wnd => wnd.Handle == msg.WParam); + win = Windows.First(wnd => wnd.Handle == msgCopy.WParam); win.ProgressValue = (int)msg.LParam; } @@ -506,7 +557,7 @@ private void ShellWinProc(Message msg) win = new ApplicationWindow(this, msg.WParam); if (Windows.Contains(win)) { - win = Windows.First(wnd => wnd.Handle == msg.WParam); + win = Windows.First(wnd => wnd.Handle == msgCopy.WParam); win.ProgressState = (TBPFLAG)msg.LParam; } @@ -559,7 +610,7 @@ private void ShellWinProc(Message msg) win = new ApplicationWindow(this, msg.WParam); if (Windows.Contains(win)) { - win = Windows.First(wnd => wnd.Handle == msg.WParam); + win = Windows.First(wnd => wnd.Handle == msgCopy.WParam); win.SetOverlayIcon(msg.LParam); } @@ -582,7 +633,7 @@ private void ShellWinProc(Message msg) win = new ApplicationWindow(this, msg.WParam); if (Windows.Contains(win)) { - win = Windows.First(wnd => wnd.Handle == msg.WParam); + win = Windows.First(wnd => wnd.Handle == msgCopy.WParam); win.SetOverlayIconDescription(msg.LParam); } @@ -599,7 +650,7 @@ private void ShellWinProc(Message msg) } } - msg.Result = DefWindowProc(msg.HWnd, msg.Msg, msg.WParam, msg.LParam); + handled = false; } private void MoveEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) diff --git a/src/ManagedShell.WindowsTasks/WindowActivatedEventArgs.cs b/src/ManagedShell.WindowsTasks/WindowEventArgs.cs similarity index 67% rename from src/ManagedShell.WindowsTasks/WindowActivatedEventArgs.cs rename to src/ManagedShell.WindowsTasks/WindowEventArgs.cs index 92cbf1ff..c25bd483 100644 --- a/src/ManagedShell.WindowsTasks/WindowActivatedEventArgs.cs +++ b/src/ManagedShell.WindowsTasks/WindowEventArgs.cs @@ -2,7 +2,7 @@ namespace ManagedShell.WindowsTasks { - public class WindowActivatedEventArgs : EventArgs + public class WindowEventArgs : EventArgs { public ApplicationWindow Window; } diff --git a/src/ManagedShell/ShellConfig.cs b/src/ManagedShell/ShellConfig.cs index 455bddcc..15336c47 100644 --- a/src/ManagedShell/ShellConfig.cs +++ b/src/ManagedShell/ShellConfig.cs @@ -24,6 +24,7 @@ public struct ShellConfig /// /// Controls whether the tasks service will be multi-mon aware when using AutoStartTasksService.
/// This keeps the HMonitor property of each ApplicationWindow updated.
+ /// This setting applies only to Windows 7. The tasks service is always multi-mon aware on Windows 8 and newer.
///
/// By default, this is enabled. ///
diff --git a/src/ManagedShell/ShellManager.cs b/src/ManagedShell/ShellManager.cs index d13d61ee..5290c675 100644 --- a/src/ManagedShell/ShellManager.cs +++ b/src/ManagedShell/ShellManager.cs @@ -46,7 +46,7 @@ public ShellManager(ShellConfig config) Tasks = new Tasks(TasksService); } - FullScreenHelper = new FullScreenHelper(); + FullScreenHelper = new FullScreenHelper(TasksService); ExplorerHelper = new ExplorerHelper(NotificationArea); AppBarManager = new AppBarManager(ExplorerHelper);