Skip to content

Commit 3769c1f

Browse files
committed
Delete the TimerHook -- didn't need it after all.
Remove TimerHook's entry from the IoC. Clean up of Kernel32 and User32, removing unused declares. Make calls to Kernel32 in HotKey compliant with documentation and explicitly checking the error status for all Kernel32 calls.
1 parent 730da6d commit 3769c1f

File tree

6 files changed

+21
-263
lines changed

6 files changed

+21
-263
lines changed

RetailCoder.VBE/Common/Hotkeys/Hotkey.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Runtime.InteropServices;
34
using System.Windows.Forms;
45
using Rubberduck.Common.WinAPI;
56
using NLog;
@@ -32,7 +33,7 @@ public Hotkey(IntPtr hWndVbe, string key, CommandBase command, Keys secondKey =
3233
public Keys Combo { get; }
3334
public Keys SecondKey { get; }
3435
public bool IsTwoStepHotkey { get; }
35-
public bool IsAttached { get; private set; }
36+
public bool IsAttached => HotkeyInfo.HookId != IntPtr.Zero;
3637

3738
public event EventHandler<HookEventArgs> MessageReceived;
3839

@@ -68,10 +69,17 @@ public void Detach()
6869
return;
6970
}
7071

71-
User32.UnregisterHotKey(_hWndVbe, HotkeyInfo.HookId);
72+
if (!User32.UnregisterHotKey(_hWndVbe, HotkeyInfo.HookId))
73+
{
74+
Logger.Warn($"Error calling UnregisterHotKey; the error was {Marshal.GetLastWin32Error()}; going to delete the atom anyway... The memory may leak.");
75+
}
76+
Kernel32.SetLastError(Kernel32.ERROR_SUCCESS);
7277
Kernel32.GlobalDeleteAtom(HotkeyInfo.HookId);
73-
74-
IsAttached = false;
78+
var lastError = Marshal.GetLastWin32Error();
79+
if (lastError != Kernel32.ERROR_SUCCESS)
80+
{
81+
Logger.Warn($"Error calling DeleteGlobalAtom; the error was {lastError} and the id was {HotkeyInfo.HookId}.");
82+
}
7583
ClearCommandShortcutText();
7684
}
7785

@@ -83,14 +91,19 @@ private void HookKey(Keys key, uint shift)
8391
}
8492

8593
var hookId = (IntPtr)Kernel32.GlobalAddAtom(Guid.NewGuid().ToString());
94+
if (hookId != IntPtr.Zero)
95+
{
96+
Logger.Warn($"Error calling GlobalAddAtom; the error was {Marshal.GetLastWin32Error()}; aborting the HookKey operation...");
97+
return;
98+
}
99+
86100
var success = User32.RegisterHotKey(_hWndVbe, hookId, shift, (uint)key);
87101
if (!success)
88102
{
89103
Logger.Debug(RubberduckUI.CommonHotkey_KeyNotRegistered, key);
90104
}
91105

92106
HotkeyInfo = new HotkeyInfo(hookId, Combo);
93-
IsAttached = true;
94107
}
95108

96109
private void SetCommandShortcutText()
@@ -108,8 +121,7 @@ private void ClearCommandShortcutText()
108121
command.ShortcutText = string.Empty;
109122
}
110123
}
111-
112-
124+
113125
private static readonly IDictionary<char,uint> Modifiers = new Dictionary<char, uint>
114126
{
115127
{ '+', (uint)KeyModifier.SHIFT },

RetailCoder.VBE/Common/TimerHook.cs

Lines changed: 0 additions & 115 deletions
This file was deleted.

RetailCoder.VBE/Common/WinAPI/Kernel32.cs

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,6 @@ public static class Kernel32
2929
/// <returns>The function always returns (ATOM) 0.</returns>
3030
[DllImport("kernel32.dll", SetLastError=true, ExactSpelling=true)]
3131
public static extern ushort GlobalDeleteAtom(IntPtr nAtom);
32-
33-
/// <summary>
34-
/// Retrieves a module handle for the specified module.
35-
/// The module must have been loaded by the calling process.
36-
/// </summary>
37-
/// <param name="lpModuleName">The name of the loaded module (either a .dll or .exe file).
38-
/// If the file name extension is omitted, the default library extension .dll is appended.
39-
/// The file name string can include a trailing point character (.) to indicate that the module name has no extension.
40-
/// The string does not have to specify a path. When specifying a path, be sure to use backslashes (\), not forward slashes (/).
41-
/// The name is compared (case independently) to the names of modules currently mapped into the address space of the calling process.</param>
42-
/// <returns>If the function succeeds, the return value is a handle to the specified module.
43-
/// If the function fails, the return value is NULL. To get extended error information, call GetLastError.</returns>
44-
/// <remarks>The returned handle is not global or inheritable. It cannot be duplicated or used by another process.
45-
/// This function must be used carefully in a multithreaded application. There is no guarantee that the module handle remains valid between the time this function returns the handle and the time it is used.
46-
/// For example, suppose that a thread retrieves a module handle, but before it uses the handle, a second thread frees the module.
47-
/// If the system loads another module, it could reuse the module handle that was recently freed.
48-
/// Therefore, the first thread would have a handle to a different module than the one intended.
49-
/// </remarks>
50-
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
51-
public static extern IntPtr GetModuleHandle(string lpModuleName);
5232

5333
/// <summary>
5434
/// Sets the last-error code for the calling thread.

RetailCoder.VBE/Common/WinAPI/User32.cs

Lines changed: 1 addition & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -44,120 +44,7 @@ public static class User32
4444
[DllImport("user32.dll", SetLastError = true)]
4545
[return: MarshalAs(UnmanagedType.Bool)]
4646
public static extern bool UnregisterHotKey(IntPtr hWnd, IntPtr id);
47-
48-
[DllImport("user32.dll")]
49-
public static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
50-
51-
[DllImport("user32.dll")]
52-
public static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
53-
public delegate IntPtr WndProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
54-
55-
/// <summary>
56-
/// Retrieves a handle to the foreground window (the window with which the user is currently working).
57-
/// The system assigns a slightly higher priority to the thread that creates the foreground window than it does to other threads.
58-
/// </summary>
59-
/// <returns>The return value is a handle to the foreground window.
60-
/// The foreground window can be NULL in certain circumstances, such as when a window is losing activation.</returns>
61-
[DllImport("user32.dll")]
62-
public static extern IntPtr GetForegroundWindow();
63-
64-
/// <summary>
65-
/// Retrieves the name of the class to which the specified window belongs.
66-
/// </summary>
67-
/// <param name="hWnd">A handle to the window and, indirectly, the class to which the window belongs.</param>
68-
/// <param name="lpClassName">The class name string (maximum 256 characters).</param>
69-
/// <param name="nMaxCount">The length of the lpClassName buffer, in characters.
70-
/// The buffer must be large enough to include the terminating null character; otherwise, the class name string is truncated to nMaxCount-1 characters.</param>
71-
/// <returns>If the function succeeds, the return value is the number of characters copied to the buffer, not including the terminating null character.
72-
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.</returns>
73-
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
74-
public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
75-
76-
/// <summary>
77-
/// Retrieves the identifier of the thread that created the specified window and, optionally,
78-
/// the identifier of the process that created the window.
79-
/// </summary>
80-
/// <param name="hWnd">A handle to the window.</param>
81-
/// <param name="lpdwProcessId">A pointer to a variable that receives the process identifier.
82-
/// If this parameter is not NULL, GetWindowThreadProcessId copies the identifier of the process to the variable; otherwise, it does not.</param>
83-
/// <returns>The return value is the identifier of the thread that created the window.</returns>
84-
[DllImport("user32.dll", SetLastError = true)]
85-
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
86-
87-
/// <summary>
88-
/// Retrieves the identifier of the thread that created the specified window.
89-
/// </summary>
90-
/// <param name="hWnd">A handle to the window.</param>
91-
/// <param name="processId">IntPtr.Zero</param>
92-
/// <returns></returns>
93-
[DllImport("user32.dll")]
94-
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
95-
96-
/// <summary>
97-
/// Creates a timer with the specified time-out value.
98-
/// </summary>
99-
/// <param name="hWnd">A handle to the window to be associated with the timer.
100-
/// This window must be owned by the calling thread.
101-
/// If a NULL value for hWnd is passed in along with an nIDEvent of an existing timer,
102-
/// that timer will be replaced in the same way that an existing non-NULL hWnd timer will be.</param>
103-
/// <param name="nIDEvent">A nonzero timer identifier.
104-
/// If the hWnd parameter is NULL, and the nIDEvent does not match an existing timer then it is ignored and a new timer ID is generated.
105-
/// If the hWnd parameter is not NULL and the window specified by hWnd already has a timer with the value nIDEvent,
106-
/// then the existing timer is replaced by the new timer. When SetTimer replaces a timer, the timer is reset.
107-
/// Therefore, a message will be sent after the current time-out value elapses, but the previously set time-out value is ignored.
108-
/// If the call is not intended to replace an existing timer, nIDEvent should be 0 if the hWnd is NULL.</param>
109-
/// <param name="uElapse">The time-out value, in milliseconds.</param>
110-
/// <param name="lpTimerFunc">A pointer to the function to be notified when the time-out value elapses.
111-
/// For more information about the function, see TimerProc. If lpTimerFunc is NULL, the system posts a WM_TIMER message to the application queue.
112-
/// The hwnd member of the message's MSG structure contains the value of the hWnd parameter.</param>
113-
/// <returns>If the function succeeds and the hWnd parameter is NULL, the return value is an integer identifying the new timer.
114-
/// An application can pass this value to the KillTimer function to destroy the timer.
115-
/// If the function succeeds and the hWnd parameter is not NULL, then the return value is a nonzero integer.
116-
/// An application can pass the value of the nIDEvent parameter to the KillTimer function to destroy the timer.
117-
/// If the function fails to create a timer, the return value is zero. To get extended error information, call GetLastError.</returns>
118-
[DllImport("user32.dll", ExactSpelling = true)]
119-
public static extern IntPtr SetTimer(IntPtr hWnd, IntPtr nIDEvent, uint uElapse, TimerProc lpTimerFunc);
120-
public delegate void TimerProc(IntPtr hWnd, WindowLongFlags uMsg, IntPtr nIDEvent, uint dwTime);
121-
122-
/// <summary>
123-
/// Creates a timer with the specified time-out value.
124-
/// </summary>
125-
/// <param name="hWnd">A handle to the window to be associated with the timer.
126-
/// This window must be owned by the calling thread.
127-
/// If a NULL value for hWnd is passed in along with an nIDEvent of an existing timer,
128-
/// that timer will be replaced in the same way that an existing non-NULL hWnd timer will be.</param>
129-
/// <param name="nIDEvent">A nonzero timer identifier.
130-
/// If the hWnd parameter is NULL, and the nIDEvent does not match an existing timer then it is ignored and a new timer ID is generated.
131-
/// If the hWnd parameter is not NULL and the window specified by hWnd already has a timer with the value nIDEvent,
132-
/// then the existing timer is replaced by the new timer. When SetTimer replaces a timer, the timer is reset.
133-
/// Therefore, a message will be sent after the current time-out value elapses, but the previously set time-out value is ignored.
134-
/// If the call is not intended to replace an existing timer, nIDEvent should be 0 if the hWnd is NULL.</param>
135-
/// <param name="uElapse">The time-out value, in milliseconds.</param>
136-
/// <param name="lpTimerFunc">A pointer to the function to be notified when the time-out value elapses.
137-
/// For more information about the function, see TimerProc. If lpTimerFunc is NULL, the system posts a WM_TIMER message to the application queue.
138-
/// The hwnd member of the message's MSG structure contains the value of the hWnd parameter.</param>
139-
/// <returns>If the function succeeds and the hWnd parameter is NULL, the return value is an integer identifying the new timer.
140-
/// An application can pass this value to the KillTimer function to destroy the timer.
141-
/// If the function succeeds and the hWnd parameter is not NULL, then the return value is a nonzero integer.
142-
/// An application can pass the value of the nIDEvent parameter to the KillTimer function to destroy the timer.
143-
/// If the function fails to create a timer, the return value is zero. To get extended error information, call GetLastError.</returns>
144-
[DllImport("user32.dll", ExactSpelling = true)]
145-
public static extern IntPtr SetTimer(IntPtr hWnd, IntPtr nIDEvent, uint uElapse, IntPtr lpTimerFunc);
146-
147-
/// <summary>
148-
/// Destroys the specified timer.
149-
/// </summary>
150-
/// <param name="hWnd">A handle to the window associated with the specified timer.
151-
/// This value must be the same as the hWnd value passed to the SetTimer function that created the timer.</param>
152-
/// <param name="uIDEvent">The timer to be destroyed.
153-
/// If the window handle passed to SetTimer is valid, this parameter must be the same as the nIDEvent value passed to SetTimer.
154-
/// If the application calls SetTimer with hWnd set to NULL, this parameter must be the timer identifier returned by SetTimer.</param>
155-
/// <returns>If the function succeeds, the return value is nonzero.
156-
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.</returns>
157-
[DllImport("user32.dll", ExactSpelling = true)]
158-
[return: MarshalAs(UnmanagedType.Bool)]
159-
public static extern bool KillTimer(IntPtr hWnd, IntPtr uIDEvent);
160-
47+
16148
[DllImport("user32.dll", CharSet = CharSet.Auto)]
16249
internal static extern IntPtr SendMessage(IntPtr hWnd, WM msg, IntPtr wParam, IntPtr lParam);
16350

RetailCoder.VBE/Root/RubberduckIoCInstaller.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -636,12 +636,7 @@ private static void RegisterSmartIndenter(IWindsorContainer container)
636636
private void RegisterWindowsHooks(IWindsorContainer container)
637637
{
638638
var mainWindowHwnd = (IntPtr) _vbe.MainWindow.HWnd;
639-
640-
container.Register(Component.For<IAttachable>()
641-
.ImplementedBy<TimerHook>()
642-
.DependsOn(Dependency.OnValue<IntPtr>(mainWindowHwnd))
643-
.LifestyleSingleton());
644-
639+
645640
container.Register(Component.For<IRubberduckHooks>()
646641
.ImplementedBy<RubberduckHooks>()
647642
.DependsOn(Dependency.OnValue<IntPtr>(mainWindowHwnd))

RetailCoder.VBE/Rubberduck.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,6 @@
338338
<Compile Include="Common\VariableNameValidator.cs" />
339339
<Compile Include="Common\WinAPI\RegistryAccess.cs" />
340340
<Compile Include="Common\RubberduckHooks.cs" />
341-
<Compile Include="Common\TimerHook.cs" />
342341
<Compile Include="Common\WinAPI\POINT.cs" />
343342
<Compile Include="Common\WinAPI\Kernel32.cs" />
344343
<Compile Include="Common\KeyHookEventArgs.cs" />

0 commit comments

Comments
 (0)