From 1ae9effa6feea6f95bfcabce31ecc200aac3b4d9 Mon Sep 17 00:00:00 2001 From: Lorenz Cuno Klopfenstein Date: Thu, 1 Jul 2010 23:33:29 +0200 Subject: [PATCH] Refactored HookMethods: OnTopReplica always hooks to shell events. Added WindowKeeper that keeps track of cloned windows and checks whether they are destroyed. --- OnTopReplica/MainForm.cs | 21 +++++++---- OnTopReplica/MainForm_ChildForms.cs | 2 +- OnTopReplica/MainForm_MenuEvents.cs | 6 +-- OnTopReplica/MessagePumpManager.cs | 14 +++++++ .../GroupSwitchManager.cs | 20 +--------- .../MessagePumpProcessors/WindowKeeper.cs | 37 +++++++++++++++++++ OnTopReplica/Native/HookMethods.cs | 15 +++++++- OnTopReplica/OnTopReplica.csproj | 10 ++--- OnTopReplica/OnTopReplica.exe.manifest | 2 +- 9 files changed, 90 insertions(+), 37 deletions(-) create mode 100644 OnTopReplica/MessagePumpProcessors/WindowKeeper.cs diff --git a/OnTopReplica/MainForm.cs b/OnTopReplica/MainForm.cs index f392373..6235b72 100644 --- a/OnTopReplica/MainForm.cs +++ b/OnTopReplica/MainForm.cs @@ -19,7 +19,6 @@ namespace OnTopReplica { //Window manager WindowManager _windowManager = new WindowManager(); - WindowHandle _lastWindowHandle = null; //Message pump extension MessagePumpManager _msgPumpManager = new MessagePumpManager(); @@ -297,20 +296,20 @@ namespace OnTopReplica { /// Region of the window to clone. public void SetThumbnail(WindowHandle handle, StoredRegion region) { try { - _lastWindowHandle = handle; + CurrentThumbnailWindowHandle = handle; _thumbnailPanel.SetThumbnailHandle(handle); if (region != null) _thumbnailPanel.SelectedRegion = region.Rect; else _thumbnailPanel.ConstrainToRegion = false; + + //Set aspect ratio (this will resize the form), do not refresh if in fullscreen + SetAspectRatio(_thumbnailPanel.ThumbnailOriginalSize, !IsFullscreen); } catch (Exception ex) { ThumbnailError(ex, false, Strings.ErrorUnableToCreateThumbnail); } - - //Set aspect ratio (this will resize the form), do not refresh if in fullscreen - SetAspectRatio(_thumbnailPanel.ThumbnailOriginalSize, !IsFullscreen); } /// @@ -328,7 +327,7 @@ namespace OnTopReplica { if (handles.Count == 1) return; - _lastWindowHandle = null; + CurrentThumbnailWindowHandle = null; _msgPumpManager.Get().EnableGroupMode(handles); } @@ -337,7 +336,7 @@ namespace OnTopReplica { /// public void UnsetThumbnail() { //Unset handle - _lastWindowHandle = null; + CurrentThumbnailWindowHandle = null; _thumbnailPanel.UnsetThumbnail(); //Disable aspect ratio @@ -446,6 +445,14 @@ namespace OnTopReplica { } } + /// + /// Retrieves the window handle of the currently cloned thumbnail. + /// + public WindowHandle CurrentThumbnailWindowHandle { + get; + private set; + } + #endregion } diff --git a/OnTopReplica/MainForm_ChildForms.cs b/OnTopReplica/MainForm_ChildForms.cs index 86e0328..524e6a7 100644 --- a/OnTopReplica/MainForm_ChildForms.cs +++ b/OnTopReplica/MainForm_ChildForms.cs @@ -118,7 +118,7 @@ namespace OnTopReplica { } void Thumbnail_CloneClick(object sender, CloneClickEventArgs e) { - Win32Helper.InjectFakeMouseClick(_lastWindowHandle.Handle, e); + Win32Helper.InjectFakeMouseClick(CurrentThumbnailWindowHandle.Handle, e); } } diff --git a/OnTopReplica/MainForm_MenuEvents.cs b/OnTopReplica/MainForm_MenuEvents.cs index 361558e..6a0feb6 100644 --- a/OnTopReplica/MainForm_MenuEvents.cs +++ b/OnTopReplica/MainForm_MenuEvents.cs @@ -31,7 +31,7 @@ namespace OnTopReplica { private void Menu_Windows_opening(object sender, CancelEventArgs e) { _windowManager.Refresh(WindowManager.EnumerationMode.TaskWindows); WindowListHelper.PopulateMenu(this, _windowManager, (ToolStrip)sender, - _lastWindowHandle, new EventHandler(Menu_Windows_itemclick)); + CurrentThumbnailWindowHandle, new EventHandler(Menu_Windows_itemclick)); } void Menu_Windows_itemclick(object sender, EventArgs e) { @@ -54,11 +54,11 @@ namespace OnTopReplica { } private void Menu_Switch_click(object sender, EventArgs e) { - if (_lastWindowHandle == null) + if (CurrentThumbnailWindowHandle == null) return; Program.Platform.HideForm(this); - Native.WindowManagerMethods.SetForegroundWindow(_lastWindowHandle.Handle); + Native.WindowManagerMethods.SetForegroundWindow(CurrentThumbnailWindowHandle.Handle); } private void Menu_GroupSwitchMode_click(object sender, EventArgs e) { diff --git a/OnTopReplica/MessagePumpManager.cs b/OnTopReplica/MessagePumpManager.cs index b0f07da..4e4abfa 100644 --- a/OnTopReplica/MessagePumpManager.cs +++ b/OnTopReplica/MessagePumpManager.cs @@ -3,17 +3,22 @@ using System.Collections.Generic; using System.Text; using System.Reflection; using System.Windows.Forms; +using OnTopReplica.Native; namespace OnTopReplica { class MessagePumpManager : IDisposable { Dictionary _processors = new Dictionary(); + public MainForm Form { get; private set; } + /// /// Instantiates all message pump processors and registers them on the main form. /// /// public void Initialize(MainForm form) { + Form = form; + foreach (var t in Assembly.GetExecutingAssembly().GetTypes()) { if (typeof(IMessagePumpProcessor).IsAssignableFrom(t) && !t.IsAbstract) { var instance = (IMessagePumpProcessor)Activator.CreateInstance(t); @@ -26,6 +31,11 @@ namespace OnTopReplica { #endif } } + + //Register window shell hook + if (!HookMethods.RegisterShellHookWindow(form.Handle)) { + Console.Error.WriteLine("Failed to register shell hook window."); + } } /// @@ -49,6 +59,10 @@ namespace OnTopReplica { #region IDisposable Members public void Dispose() { + if (!HookMethods.DeregisterShellHookWindow(Form.Handle)) { + Console.Error.WriteLine("Failed to deregister sheel hook window."); + } + foreach (var processor in _processors.Values) { processor.Dispose(); } diff --git a/OnTopReplica/MessagePumpProcessors/GroupSwitchManager.cs b/OnTopReplica/MessagePumpProcessors/GroupSwitchManager.cs index edcccef..3bcbc45 100644 --- a/OnTopReplica/MessagePumpProcessors/GroupSwitchManager.cs +++ b/OnTopReplica/MessagePumpProcessors/GroupSwitchManager.cs @@ -12,13 +12,6 @@ namespace OnTopReplica.MessagePumpProcessors { class GroupSwitchManager : BaseMessagePumpProcessor { - public GroupSwitchManager() { - _hookMsgId = HookMethods.RegisterWindowMessage("SHELLHOOK"); - if (_hookMsgId == 0) - Console.Error.WriteLine("Failed to register SHELLHOOK Windows message."); - } - - uint _hookMsgId; bool _active = false; List _lruHandles; @@ -30,14 +23,6 @@ namespace OnTopReplica.MessagePumpProcessors { if (handles == null || handles.Count == 0) return; - //Enable new hook - if (!_active) { - if (!HookMethods.RegisterShellHookWindow(Form.Handle)) { - Console.Error.WriteLine("Failed to register shell hook window."); - return; - } - } - //Okey dokey, will now track handles TrackHandles(handles); _active = true; @@ -65,9 +50,6 @@ namespace OnTopReplica.MessagePumpProcessors { if (!_active) return; - if (!HookMethods.DeregisterShellHookWindow(Form.Handle)) - Console.Error.WriteLine("Failed to deregister shell hook window."); - _lruHandles = null; _active = false; } @@ -76,7 +58,7 @@ namespace OnTopReplica.MessagePumpProcessors { /// Processes the message pump. /// public override void Process(Message msg) { - if (_active && msg.Msg == _hookMsgId) { + if (_active && msg.Msg == HookMethods.WM_SHELLHOOKMESSAGE) { int hookCode = msg.WParam.ToInt32(); if (hookCode == HookMethods.HSHELL_WINDOWACTIVATED || hookCode == HookMethods.HSHELL_RUDEAPPACTIVATED) { diff --git a/OnTopReplica/MessagePumpProcessors/WindowKeeper.cs b/OnTopReplica/MessagePumpProcessors/WindowKeeper.cs new file mode 100644 index 0000000..846ff69 --- /dev/null +++ b/OnTopReplica/MessagePumpProcessors/WindowKeeper.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Forms; +using OnTopReplica.Native; + +namespace OnTopReplica.MessagePumpProcessors { + + /// + /// Listens for shell events and closes the thumbnail if a cloned window is destroyed. + /// + class WindowKeeper : BaseMessagePumpProcessor { + + public override void Process(Message msg) { + if (Form.CurrentThumbnailWindowHandle != null && + msg.Msg == HookMethods.WM_SHELLHOOKMESSAGE) { + int hookCode = msg.WParam.ToInt32(); + if (hookCode == HookMethods.HSHELL_WINDOWDESTROYED) { + //Check whether the destroyed window is the one we were cloning + IntPtr destroyedHandle = msg.LParam; + if (destroyedHandle == Form.CurrentThumbnailWindowHandle.Handle) { + //Disable group switch mode, since a window of the group has been destroyed + Form.MessagePumpManager.Get().Disable(); + + //Disable cloning + Form.UnsetThumbnail(); + } + } + } + } + + protected override void Shutdown() { + + } + } + +} diff --git a/OnTopReplica/Native/HookMethods.cs b/OnTopReplica/Native/HookMethods.cs index 6677196..3b6f7fc 100644 --- a/OnTopReplica/Native/HookMethods.cs +++ b/OnTopReplica/Native/HookMethods.cs @@ -10,16 +10,29 @@ namespace OnTopReplica.Native { /// static class HookMethods { + static HookMethods() { + WM_SHELLHOOKMESSAGE = RegisterWindowMessage("SHELLHOOK"); + if (WM_SHELLHOOKMESSAGE == 0) + Console.Error.WriteLine("Failed to register SHELLHOOK Windows message."); + } + + public static int WM_SHELLHOOKMESSAGE { + get; + private set; + } + public const int HSHELL_WINDOWACTIVATED = 4; public const int HSHELL_RUDEAPPACTIVATED = HSHELL_WINDOWACTIVATED | HSHELL_HIGHBIT; const int HSHELL_HIGHBIT = 0x8000; + public const int HSHELL_WINDOWDESTROYED = 2; + public const int HSHELL_WINDOWCREATED = 1; /// /// Registers the WM_ID for a window message. /// /// Name of the window message. [DllImport("User32.dll")] - public static extern uint RegisterWindowMessage(string wndMessageName); + public static extern int RegisterWindowMessage(string wndMessageName); /// /// Registers a window as a shell hook window. diff --git a/OnTopReplica/OnTopReplica.csproj b/OnTopReplica/OnTopReplica.csproj index feb2094..f1a9371 100644 --- a/OnTopReplica/OnTopReplica.csproj +++ b/OnTopReplica/OnTopReplica.csproj @@ -13,7 +13,7 @@ v2.0 512 Assets\icon-new.ico - true + false true Internet OnTopReplica.Program @@ -31,7 +31,7 @@ publish\ true - Web + Disk true Background 1 @@ -39,16 +39,15 @@ true false true - http://www.klopfenstein.net/public/Uploads/ontopreplica/ http://www.klopfenstein.net/public/Uploads/ontopreplica/ http://www.klopfenstein.net/lorenz.aspx/ontopreplica en OnTopReplica Lorenz Cuno Klopfenstein - true publish.htm false - 0 + true + 1 3.0.0.%2a false true @@ -145,6 +144,7 @@ + diff --git a/OnTopReplica/OnTopReplica.exe.manifest b/OnTopReplica/OnTopReplica.exe.manifest index 38c7a87..d7a6e03 100644 --- a/OnTopReplica/OnTopReplica.exe.manifest +++ b/OnTopReplica/OnTopReplica.exe.manifest @@ -20,7 +20,7 @@ - + Lightweight clone of a window.