Refactored HookMethods: OnTopReplica always hooks to shell events.

Added WindowKeeper that keeps track of cloned windows and checks whether they are destroyed.
This commit is contained in:
Lorenz Cuno Klopfenstein 2010-07-01 23:33:29 +02:00
parent 8973532765
commit 1ae9effa6f
9 changed files with 90 additions and 37 deletions

View file

@ -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 {
/// <param name="region">Region of the window to clone.</param>
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);
}
/// <summary>
@ -328,7 +327,7 @@ namespace OnTopReplica {
if (handles.Count == 1)
return;
_lastWindowHandle = null;
CurrentThumbnailWindowHandle = null;
_msgPumpManager.Get<MessagePumpProcessors.GroupSwitchManager>().EnableGroupMode(handles);
}
@ -337,7 +336,7 @@ namespace OnTopReplica {
/// </summary>
public void UnsetThumbnail() {
//Unset handle
_lastWindowHandle = null;
CurrentThumbnailWindowHandle = null;
_thumbnailPanel.UnsetThumbnail();
//Disable aspect ratio
@ -446,6 +445,14 @@ namespace OnTopReplica {
}
}
/// <summary>
/// Retrieves the window handle of the currently cloned thumbnail.
/// </summary>
public WindowHandle CurrentThumbnailWindowHandle {
get;
private set;
}
#endregion
}

View file

@ -118,7 +118,7 @@ namespace OnTopReplica {
}
void Thumbnail_CloneClick(object sender, CloneClickEventArgs e) {
Win32Helper.InjectFakeMouseClick(_lastWindowHandle.Handle, e);
Win32Helper.InjectFakeMouseClick(CurrentThumbnailWindowHandle.Handle, e);
}
}

View file

@ -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) {

View file

@ -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<Type, IMessagePumpProcessor> _processors = new Dictionary<Type, IMessagePumpProcessor>();
public MainForm Form { get; private set; }
/// <summary>
/// Instantiates all message pump processors and registers them on the main form.
/// </summary>
/// <param name="form"></param>
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.");
}
}
/// <summary>
@ -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();
}

View file

@ -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<WindowHandleWrapper> _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.
/// </summary>
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) {

View file

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using OnTopReplica.Native;
namespace OnTopReplica.MessagePumpProcessors {
/// <summary>
/// Listens for shell events and closes the thumbnail if a cloned window is destroyed.
/// </summary>
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<GroupSwitchManager>().Disable();
//Disable cloning
Form.UnsetThumbnail();
}
}
}
}
protected override void Shutdown() {
}
}
}

View file

@ -10,16 +10,29 @@ namespace OnTopReplica.Native {
/// </summary>
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;
/// <summary>
/// Registers the WM_ID for a window message.
/// </summary>
/// <param name="wndMessageName">Name of the window message.</param>
[DllImport("User32.dll")]
public static extern uint RegisterWindowMessage(string wndMessageName);
public static extern int RegisterWindowMessage(string wndMessageName);
/// <summary>
/// Registers a window as a shell hook window.

View file

@ -13,7 +13,7 @@
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ApplicationIcon>Assets\icon-new.ico</ApplicationIcon>
<IsWebBootstrapper>true</IsWebBootstrapper>
<IsWebBootstrapper>false</IsWebBootstrapper>
<GenerateManifests>true</GenerateManifests>
<TargetZone>Internet</TargetZone>
<StartupObject>OnTopReplica.Program</StartupObject>
@ -31,7 +31,7 @@
<UpgradeBackupLocation />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Web</InstallFrom>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>true</UpdateEnabled>
<UpdateMode>Background</UpdateMode>
<UpdateInterval>1</UpdateInterval>
@ -39,16 +39,15 @@
<UpdatePeriodically>true</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<InstallUrl>http://www.klopfenstein.net/public/Uploads/ontopreplica/</InstallUrl>
<UpdateUrl>http://www.klopfenstein.net/public/Uploads/ontopreplica/</UpdateUrl>
<SupportUrl>http://www.klopfenstein.net/lorenz.aspx/ontopreplica</SupportUrl>
<TargetCulture>en</TargetCulture>
<ProductName>OnTopReplica</ProductName>
<PublisherName>Lorenz Cuno Klopfenstein</PublisherName>
<CreateWebPageOnPublish>true</CreateWebPageOnPublish>
<WebPage>publish.htm</WebPage>
<OpenBrowserOnPublish>false</OpenBrowserOnPublish>
<ApplicationRevision>0</ApplicationRevision>
<AutorunEnabled>true</AutorunEnabled>
<ApplicationRevision>1</ApplicationRevision>
<ApplicationVersion>3.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
@ -145,6 +144,7 @@
<Compile Include="MessagePumpProcessors\GroupSwitchManager.cs" />
<Compile Include="IMessagePumpProcessor.cs" />
<Compile Include="MessagePumpProcessors\BaseMessagePumpProcessor.cs" />
<Compile Include="MessagePumpProcessors\WindowKeeper.cs" />
<Compile Include="Native\ErrorMethods.cs" />
<Compile Include="Native\HookMethods.cs" />
<Compile Include="Native\HotKeyMethods.cs" />

View file

@ -20,7 +20,7 @@
</application>
</compatibility>
<assemblyIdentity processorArchitecture="*" type="win32" name="OnTopReplica"/>
<!--<assemblyIdentity processorArchitecture="*" type="win32" name="OnTopReplica"/>-->
<description>Lightweight clone of a window.</description>
<!--