Skip to content

Commit bc47c69

Browse files
committed
Merge remote-tracking branch 'upstream/next' into rkapka-master
2 parents 4ae1a71 + 323f0ba commit bc47c69

File tree

79 files changed

+1986
-790
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+1986
-790
lines changed

RetailCoder.VBE/Common/Hotkeys/Hotkey.cs

Lines changed: 22 additions & 6 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,19 @@ 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 on hokey with id {HotkeyInfo.HookId} for command of type {Command.GetType()}; 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);
78+
var lastError = Marshal.GetLastWin32Error();
79+
if (lastError != Kernel32.ERROR_SUCCESS)
80+
{
81+
Logger.Warn($"Error calling DeleteGlobalAtom; the error was {lastError}, the id {HotkeyInfo.HookId} and the type of the associated command {Command.GetType()}.");
82+
}
7383

74-
IsAttached = false;
84+
HotkeyInfo = new HotkeyInfo(IntPtr.Zero, Combo);
7585
ClearCommandShortcutText();
7686
}
7787

@@ -83,14 +93,21 @@ private void HookKey(Keys key, uint shift)
8393
}
8494

8595
var hookId = (IntPtr)Kernel32.GlobalAddAtom(Guid.NewGuid().ToString());
96+
if (hookId == IntPtr.Zero)
97+
{
98+
Logger.Warn($"Error calling GlobalAddAtom; the error was {Marshal.GetLastWin32Error()}; aborting the HookKey operation...");
99+
return;
100+
}
101+
86102
var success = User32.RegisterHotKey(_hWndVbe, hookId, shift, (uint)key);
87103
if (!success)
88104
{
89105
Logger.Debug(RubberduckUI.CommonHotkey_KeyNotRegistered, key);
106+
return;
90107
}
91108

92109
HotkeyInfo = new HotkeyInfo(hookId, Combo);
93-
IsAttached = true;
110+
Logger.Trace($"Hotkey for the associated command {Command.GetType()} was registered with id {HotkeyInfo.HookId}");
94111
}
95112

96113
private void SetCommandShortcutText()
@@ -108,8 +125,7 @@ private void ClearCommandShortcutText()
108125
command.ShortcutText = string.Empty;
109126
}
110127
}
111-
112-
128+
113129
private static readonly IDictionary<char,uint> Modifiers = new Dictionary<char, uint>
114130
{
115131
{ '+', (uint)KeyModifier.SHIFT },

RetailCoder.VBE/Common/TimerHook.cs

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

RetailCoder.VBE/Common/WinAPI/Kernel32.cs

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,27 +29,14 @@ 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-
32+
3333
/// <summary>
34-
/// Retrieves a module handle for the specified module.
35-
/// The module must have been loaded by the calling process.
34+
/// Sets the last-error code for the calling thread.
3635
/// </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);
52-
36+
/// <param name="dwErrorCode">The last-error code for the thread.</param>
37+
[DllImport("kernel32.dll", SetLastError = true)]
38+
public static extern void SetLastError(uint dwErrorCode);
5339

40+
public static uint ERROR_SUCCESS = 0;
5441
}
5542
}

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/Extension.cs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using Rubberduck.Root;
1717
using Rubberduck.Settings;
1818
using Rubberduck.SettingsProvider;
19+
using Rubberduck.VBEditor.ComManagement;
1920
using Rubberduck.VBEditor.Events;
2021
using Rubberduck.VBEditor.SafeComWrappers.Abstract;
2122
using Windows = Rubberduck.VBEditor.SafeComWrappers.VBA.Windows;
@@ -41,6 +42,7 @@ public class _Extension : IDTExtensibility2
4142

4243
private IWindsorContainer _container;
4344
private App _app;
45+
private IComSafe _comSafe;
4446
private readonly Logger _logger = LogManager.GetCurrentClassLogger();
4547

4648
public void OnAddInsUpdate(ref Array custom) { }
@@ -50,6 +52,9 @@ public void OnConnection(object Application, ext_ConnectMode ConnectMode, object
5052
{
5153
try
5254
{
55+
ComSafeManager.ResetComSafe();
56+
_comSafe = ComSafeManager.GetCurrentComSafe();
57+
5358
if (Application is Microsoft.Vbe.Interop.VBE vbe1)
5459
{
5560
_ide = new VBEditor.SafeComWrappers.VBA.VBE(vbe1);
@@ -261,17 +266,12 @@ private void ShutdownAddIn()
261266
_container = null;
262267
}
263268

264-
if (_addin != null)
269+
if (_comSafe != null)
265270
{
266-
_logger.Log(LogLevel.Trace, "Disposing AddIn wrapper...");
267-
_addin.Dispose();
271+
_logger.Log(LogLevel.Trace, "Disposing COM safe...");
272+
_comSafe.Dispose();
273+
_comSafe = null;
268274
_addin = null;
269-
}
270-
271-
if (_ide != null)
272-
{
273-
_logger.Log(LogLevel.Trace, "Disposing VBE wrapper...");
274-
_ide.Dispose();
275275
_ide = null;
276276
}
277277

@@ -289,11 +289,7 @@ private void ShutdownAddIn()
289289
_logger.Log(LogLevel.Trace, "Unregistering AppDomain handlers....");
290290
currentDomain.AssemblyResolve -= LoadFromSameFolder;
291291
currentDomain.UnhandledException -= HandlAppDomainException;
292-
_logger.Log(LogLevel.Trace, "Done. Initiating garbage collection...");
293-
GC.Collect();
294-
_logger.Log(LogLevel.Trace, "Done. Waiting for pending finalizers...");
295-
GC.WaitForPendingFinalizers();
296-
_logger.Log(LogLevel.Trace, "Done. Shutdown completed. Quack!");
292+
_logger.Log(LogLevel.Trace, "Done. Main Shutdown completed. Toolwindows follow. Quack!");
297293
_isInitialized = false;
298294
}
299295
}

0 commit comments

Comments
 (0)