Skip to content

Commit 8b8deab

Browse files
committed
added LL Mouse hook to evaluate menu state on right-click, but it's throwing NRE during COM reflection when it's attached.. no idea why
1 parent e026f8a commit 8b8deab

11 files changed

+133
-14
lines changed

RetailCoder.VBE/App.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Linq;
66
using System.Runtime.InteropServices.ComTypes;
77
using System.Windows.Forms;
8-
using System.Windows.Input;
98
using Microsoft.Vbe.Interop;
109
using NLog;
1110
using Rubberduck.Common;
@@ -61,6 +60,7 @@ public App(VBE vbe, IMessageBox messageBox,
6160
_hooks = hooks;
6261
_logger = LogManager.GetCurrentClassLogger();
6362

63+
_hooks.MessageReceived += _hooks_MessageReceived;
6464
_configService.SettingsChanged += _configService_SettingsChanged;
6565
_configService.LanguageChanged += ConfigServiceLanguageChanged;
6666
_parser.State.StateChanged += Parser_StateChanged;
@@ -81,6 +81,15 @@ public App(VBE vbe, IMessageBox messageBox,
8181
UiDispatcher.Initialize();
8282
}
8383

84+
private void _hooks_MessageReceived(object sender, HookEventArgs e)
85+
{
86+
if (sender is LowLevelMouseHook)
87+
{
88+
// right-click detected
89+
_appMenus.EvaluateCanExecute(_parser.State);
90+
}
91+
}
92+
8493
private void _configService_SettingsChanged(object sender, EventArgs e)
8594
{
8695
// also updates the ShortcutKey text
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using Rubberduck.Common.WinAPI;
3+
4+
namespace Rubberduck.Common
5+
{
6+
public class LowLevelMouseHook : IAttachable, IDisposable
7+
{
8+
private IntPtr HookCallback(int code, IntPtr wParam, IntPtr lParam)
9+
{
10+
if (code >= 0 && (WM)wParam == WM.RBUTTONDOWN)
11+
{
12+
OnRightClickCaptured();
13+
}
14+
return User32.CallNextHookEx(_hookId, code, wParam, lParam);
15+
}
16+
17+
public event EventHandler RightClickCaptured;
18+
private void OnRightClickCaptured()
19+
{
20+
var handler = RightClickCaptured;
21+
if (handler != null)
22+
{
23+
handler.Invoke(this, EventArgs.Empty);
24+
}
25+
}
26+
27+
//private const int WH_MOUSE_LL = 14;
28+
29+
private IntPtr _hookId = IntPtr.Zero;
30+
31+
public bool IsAttached { get; private set; }
32+
public event EventHandler<HookEventArgs> MessageReceived;
33+
public void Attach()
34+
{
35+
if (IsAttached)
36+
{
37+
return;
38+
}
39+
40+
_hookId = User32.SetWindowsHookEx(WindowsHook.MOUSE_LL, HookCallback, Kernel32.GetModuleHandle("user32"), 0);
41+
if (_hookId == IntPtr.Zero)
42+
{
43+
throw new System.ComponentModel.Win32Exception();
44+
}
45+
46+
IsAttached = true;
47+
}
48+
49+
public void Detach()
50+
{
51+
if (!IsAttached)
52+
{
53+
return;
54+
}
55+
56+
IsAttached = !User32.UnhookWindowsHookEx(_hookId);
57+
}
58+
59+
public void Dispose()
60+
{
61+
Detach();
62+
}
63+
}
64+
}

RetailCoder.VBE/Common/RubberduckHooks.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class RubberduckHooks : IRubberduckHooks
1919
private User32.WndProc _newWndProc;
2020

2121
private readonly IAttachable _timerHook;
22+
private readonly IAttachable _mouseHook;
2223
private readonly IGeneralConfigService _config;
2324
private readonly IList<IAttachable> _hooks = new List<IAttachable>();
2425

@@ -31,16 +32,19 @@ public RubberduckHooks(VBE vbe, IAttachable timerHook, IGeneralConfigService con
3132
_oldWndPointer = User32.SetWindowLong(_mainWindowHandle, (int)WindowLongFlags.GWL_WNDPROC, _newWndProc);
3233
_oldWndProc = (User32.WndProc)Marshal.GetDelegateForFunctionPointer(_oldWndPointer, typeof(User32.WndProc));
3334

34-
_timerHook = timerHook;
3535
_config = config;
36+
_timerHook = timerHook;
3637
_timerHook.MessageReceived += timerHook_MessageReceived;
3738
}
3839

40+
3941
public void HookHotkeys()
4042
{
4143
Detach();
4244
_hooks.Clear();
4345

46+
//AddHook(new LowLevelMouseHook()); // todo: understand how this line throws a NRE in COM reflection
47+
4448
var config = _config.LoadConfiguration();
4549
var settings = config.UserSettings.GeneralSettings.HotkeySettings;
4650
foreach (var hotkey in settings.Where(hotkey => hotkey.IsEnabled))
@@ -89,12 +93,17 @@ public void Attach()
8993

9094
private void hook_MessageReceived(object sender, HookEventArgs e)
9195
{
92-
if (sender is ILowLevelKeyboardHook)
96+
if (sender is LowLevelKeyboardHook)
9397
{
9498
// todo: handle 2-step hotkeys?
9599
return;
96100
}
97101

102+
if (sender is LowLevelMouseHook)
103+
{
104+
OnMessageReceived(sender, HookEventArgs.Empty);
105+
}
106+
98107
var hotkey = sender as IHotkey;
99108
if (hotkey != null)
100109
{

RetailCoder.VBE/Rubberduck.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,7 @@
298298
<Compile Include="Common\Hotkeys\IHotkey.cs" />
299299
<Compile Include="Common\ILowLevelKeyboardHook.cs" />
300300
<Compile Include="Common\IRubberduckHooks.cs" />
301+
<Compile Include="Common\LowLevelMouseHook.cs" />
301302
<Compile Include="Common\ModuleExporter.cs" />
302303
<Compile Include="Common\RubberduckHooks.cs" />
303304
<Compile Include="Common\TimerHook.cs" />

RetailCoder.VBE/UI/Command/MenuItems/CommandMenuItemBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public Func<string> Caption
3636
/// <remarks>Returns <c>true</c> if not overridden.</remarks>
3737
public virtual bool EvaluateCanExecute(RubberduckParserState state)
3838
{
39-
return true;
39+
return _command.CanExecute(state);
4040
}
4141

4242
public virtual bool BeginGroup { get { return false; } }
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Windows.Input;
2-
using Rubberduck.Parsing.VBA;
32
using Rubberduck.UI.Command.MenuItems.ParentMenus;
43

54
namespace Rubberduck.UI.Command.MenuItems
@@ -13,10 +12,5 @@ public ProjectExplorerRefactorRenameCommandMenuItem(ICommand command)
1312

1413
public override string Key { get { return "RefactorMenu_Rename"; } }
1514
public override int DisplayOrder { get { return (int)RefactoringsMenuItemDisplayOrder.RenameIdentifier; } }
16-
17-
public override bool EvaluateCanExecute(RubberduckParserState state)
18-
{
19-
return state.Status == ParserState.Ready;
20-
}
2115
}
2216
}

RetailCoder.VBE/UI/Command/Refactorings/CodePaneRefactorRenameCommand.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Rubberduck.VBEditor;
33
using Rubberduck.VBEditor.VBEInterfaces.RubberduckCodePane;
44
using System.Runtime.InteropServices;
5+
using NLog.Targets;
56
using Rubberduck.Common;
67
using Rubberduck.Parsing.Symbols;
78
using Rubberduck.Parsing.VBA;
@@ -24,6 +25,17 @@ public CodePaneRefactorRenameCommand(VBE vbe, RubberduckParserState state, IActi
2425
_wrapperWrapperFactory = wrapperWrapperFactory;
2526
}
2627

28+
public override bool CanExecute(object parameter)
29+
{
30+
if (Vbe.ActiveCodePane == null) { return false; }
31+
32+
var selection = Vbe.ActiveCodePane.GetSelection();
33+
var target = _state.AllDeclarations.FindTarget(selection);
34+
35+
return _state.Status == ParserState.Ready
36+
&& target != null;
37+
}
38+
2739
public override void Execute(object parameter)
2840
{
2941
if (Vbe.ActiveCodePane == null) { return; }

RetailCoder.VBE/UI/Command/Refactorings/FormDesignerRefactorRenameCommand.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ public FormDesignerRefactorRenameCommand(VBE vbe, RubberduckParserState state, I
2424
_wrapperWrapperFactory = wrapperWrapperFactory;
2525
}
2626

27+
public override bool CanExecute(object parameter)
28+
{
29+
return _state.Status == ParserState.Ready;
30+
}
31+
2732
public override void Execute(object parameter)
2833
{
2934
using (var view = new RenameDialog())

RetailCoder.VBE/UI/Command/Refactorings/RefactorExtractMethodCommand.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
using Microsoft.Vbe.Interop;
22
using System.Runtime.InteropServices;
3+
using Antlr4.Runtime;
4+
using Rubberduck.Common;
5+
using Rubberduck.Parsing;
36
using Rubberduck.Parsing.VBA;
47
using Rubberduck.Refactorings.ExtractMethod;
58
using Rubberduck.Settings;
69
using Rubberduck.VBEditor;
10+
using Rubberduck.VBEditor.VBEInterfaces.RubberduckCodePane;
711

812
namespace Rubberduck.UI.Command.Refactorings
913
{
@@ -18,14 +22,24 @@ public RefactorExtractMethodCommand(VBE vbe, RubberduckParserState state, IActiv
1822
_state = state;
1923
}
2024

25+
public override bool CanExecute(object parameter)
26+
{
27+
if (Vbe.ActiveCodePane == null)
28+
{
29+
return false;
30+
}
31+
32+
var selection = Vbe.ActiveCodePane.GetSelection();
33+
var target = _state.AllDeclarations.FindSelectedDeclaration(selection, DeclarationExtensions.ProcedureTypes, d => ((ParserRuleContext)d.Context.Parent).GetSelection());
34+
return _state.Status == ParserState.Ready && target != null;
35+
}
36+
2137
public override void Execute(object parameter)
2238
{
2339
var factory = new ExtractMethodPresenterFactory(Editor, _state.AllDeclarations);
2440
var refactoring = new ExtractMethodRefactoring(factory, Editor);
2541
refactoring.InvalidSelection += HandleInvalidSelection;
2642
refactoring.Refactor();
2743
}
28-
29-
public RubberduckHotkey Hotkey { get {return RubberduckHotkey.RefactorExtractMethod; } }
3044
}
3145
}

Rubberduck.Parsing/Symbols/ReferencedDeclarationsCollector.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,23 @@ public IEnumerable<Declaration> GetDeclarationsForReference(Reference reference)
9898

9999
ITypeLib typeLibrary;
100100
LoadTypeLibEx(path, REGKIND.REGKIND_NONE, out typeLibrary);
101+
if (typeLibrary == null)
102+
{
103+
yield break;
104+
}
101105

102106
var typeCount = typeLibrary.GetTypeInfoCount();
103107
for (var i = 0; i < typeCount; i++)
104108
{
105109
ITypeInfo info;
106-
typeLibrary.GetTypeInfo(i, out info);
110+
try
111+
{
112+
typeLibrary.GetTypeInfo(i, out info);
113+
}
114+
catch(NullReferenceException)
115+
{
116+
yield break;
117+
}
107118

108119
if (info == null)
109120
{

0 commit comments

Comments
 (0)