Skip to content

Commit e800d88

Browse files
authored
Merge pull request #4331 from mansellan/Firehose
WM Firehose improvements (debug builds only)
2 parents 4df7207 + 92766c1 commit e800d88

File tree

7 files changed

+113
-53
lines changed

7 files changed

+113
-53
lines changed

Rubberduck.VBEEditor/Events/VBENativeServices.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,21 @@ public static void VbeEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr
8080
{
8181
if (Suspend || hwnd == IntPtr.Zero || _vbe.IsWrappingNullReference) { return; }
8282

83-
#if THIRSTY_DUCK && DEBUG
83+
var windowType = hwnd.ToWindowType();
84+
85+
#if (DEBUG && (THIRSTY_DUCK || THIRSTY_DUCK_EVT))
86+
8487
//This is an output window firehose, viewer discretion is advised.
85-
if (idObject != (int)ObjId.Cursor) { Debug.WriteLine("Hwnd: {0:X4} - EventType {1:X4}, idObject {2}, idChild {3}", (int)hwnd, eventType, idObject, idChild); }
88+
if (idObject != (int)ObjId.Cursor)
89+
{
90+
var windowClassName = hwnd.ToClassName();
91+
if (!WinEventMap.Lookup.TryGetValue((int)eventType, out var eventName))
92+
{
93+
eventName = "Unknown";
94+
}
95+
Debug.WriteLine($"EVT: 0x{eventType:X4} ({eventName}) Hwnd 0x{(int)hwnd:X4} ({windowClassName}), idObject {idObject}, idChild {idChild}");
96+
}
8697
#endif
87-
var windowType = hwnd.ToWindowType();
8898

8999
if (windowType == WindowType.IntelliSense)
90100
{

Rubberduck.VBEEditor/Rubberduck.VBEditor.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@
9898
<Compile Include="SafeComWrappers\SafeRedirectedEventedComWrapper.cs" />
9999
<Compile Include="SafeComWrappers\VB\Abstract\IHostApplication.cs" />
100100
<Compile Include="SafeComWrappers\VB\Enums\VBEKind.cs" />
101+
<Compile Include="Utility\EnumHelper.cs" />
101102
<Compile Include="Utility\UiContext.cs" />
102103
<Compile Include="VBERuntime\IVBERuntime.cs" />
103104
<Compile Include="VBERuntime\Settings\IVBESettings.cs" />
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.Linq;
5+
6+
namespace Rubberduck.VBEditor.Utility
7+
{
8+
public static class EnumHelper
9+
{
10+
public static Dictionary<TUnderlying, string> ToDictionary<TEnum, TUnderlying>()
11+
{
12+
var enumType = typeof(TEnum);
13+
var underlyingType = typeof(TUnderlying);
14+
var actualUnderlyingType = Enum.GetUnderlyingType(enumType);
15+
16+
Debug.Assert(enumType.IsEnum, $"Type '{enumType.Name}' is not an enum");
17+
Debug.Assert(actualUnderlyingType == underlyingType, $"Underlying type parameter '{underlyingType.Name}' does not match actual underlying type for enum '{enumType.Name}' ('{actualUnderlyingType.Name}')");
18+
19+
var dictionary = new Dictionary<TUnderlying, string>();
20+
21+
foreach (var fieldInfo in enumType.GetFields().Where(fi => fi.FieldType.IsEnum))
22+
{
23+
if (fieldInfo.GetCustomAttributes(typeof(ReflectionIgnoreAttribute), false).Any())
24+
{
25+
continue;
26+
}
27+
28+
var key = (TUnderlying) fieldInfo.GetRawConstantValue();
29+
30+
dictionary[key] = dictionary.ContainsKey(key)
31+
? $"{dictionary[key]} / {fieldInfo.Name}"
32+
: fieldInfo.Name;
33+
}
34+
35+
return dictionary;
36+
}
37+
}
38+
39+
public class ReflectionIgnoreAttribute : Attribute
40+
{
41+
}
42+
}

Rubberduck.VBEEditor/WindowsApi/DesignerWindowSubclass.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ public override int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr
2020
DispatchFocusEvent(FocusType.GotFocus);
2121
}
2222

23-
#if THIRSTY_DUCK && DEBUG
24-
//This is an output window firehose kind of like spy++. Prepare for some spam.
25-
Debug.WriteLine("WM: {0:X4}, wParam {1}, lParam {2}", msg, wParam, lParam);
26-
#endif
2723
return base.SubClassProc(hWnd, msg, wParam, lParam, uIdSubclass, dwRefData);
2824
}
2925
}

Rubberduck.VBEEditor/WindowsApi/SubclassingWindow.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Diagnostics;
33
using System.Runtime.InteropServices;
4+
using System.Text;
45
using NLog;
56

67
namespace Rubberduck.VBEditor.WindowsApi
@@ -85,6 +86,22 @@ public virtual int SubClassProc(IntPtr hWnd, IntPtr msg, IntPtr wParam, IntPtr l
8586
return DefSubclassProc(hWnd, msg, wParam, lParam);
8687
}
8788

89+
#if (DEBUG && (THIRSTY_DUCK || THIRSTY_DUCK_WM))
90+
//This is an output window firehose kind of like spy++. Prepare for some spam.
91+
var windowClassName = ToClassName(hWnd);
92+
if (WM_MAP.Lookup.TryGetValue((uint) msg, out var wmName))
93+
{
94+
wmName = $"WM_{wmName}";
95+
}
96+
else
97+
{
98+
wmName = "Unknown";
99+
}
100+
101+
102+
Debug.WriteLine($"MSG: 0x{(uint)msg:X4} ({wmName}), Hwnd 0x{(uint)hWnd:X4} ({windowClassName}), wParam 0x{(uint)wParam:X4}, lParam 0x{(uint)lParam:X4}");
103+
#endif
104+
88105
if ((uint) msg == (uint) WM.DESTROY)
89106
{
90107
Dispose();
@@ -107,5 +124,12 @@ protected virtual void Dispose(bool disposing)
107124

108125
_disposed = true;
109126
}
127+
128+
private static string ToClassName(IntPtr hwnd)
129+
{
130+
var name = new StringBuilder(User32.MaxGetClassNameBufferSize);
131+
User32.GetClassName(hwnd, name, name.Capacity);
132+
return name.ToString();
133+
}
110134
}
111135
}

Rubberduck.VBEEditor/WindowsApi/WM.cs

Lines changed: 18 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Collections.Generic;
3+
using Rubberduck.VBEditor.Utility;
24

35
namespace Rubberduck.VBEditor.WindowsApi
46
{
@@ -395,6 +397,7 @@ public enum WM : uint
395397
/// <summary>
396398
/// This message filters for keyboard messages.
397399
/// </summary>
400+
[ReflectionIgnore]
398401
KEYFIRST = 0x0100,
399402
/// <summary>
400403
/// The WM_KEYDOWN message is posted to the window with the keyboard focus when a nonsystem key is pressed. A nonsystem key is a key that is pressed when the ALT key is not pressed.
@@ -436,6 +439,7 @@ public enum WM : uint
436439
/// <summary>
437440
/// This message filters for keyboard messages.
438441
/// </summary>
442+
[ReflectionIgnore]
439443
KEYLAST = 0x0109,
440444
/// <summary>
441445
/// Sent immediately before the IME generates the composition string as a result of a keystroke. A window receives this message through its WindowProc function.
@@ -449,6 +453,8 @@ public enum WM : uint
449453
/// Sent to an application when the IME changes composition status as a result of a keystroke. A window receives this message through its WindowProc function.
450454
/// </summary>
451455
IME_COMPOSITION = 0x010F,
456+
457+
[ReflectionIgnore]
452458
IME_KEYLAST = 0x010F,
453459
/// <summary>
454460
/// The WM_INITDIALOG message is sent to the dialog box procedure immediately before a dialog box is displayed. Dialog box procedures typically use this message to initialize controls and carry out any other initialization tasks that affect the appearance of the dialog box.
@@ -557,6 +563,7 @@ public enum WM : uint
557563
/// <summary>
558564
/// Use WM_MOUSEFIRST to specify the first mouse message. Use the PeekMessage() Function.
559565
/// </summary>
566+
[ReflectionIgnore]
560567
MOUSEFIRST = 0x0200,
561568
/// <summary>
562569
/// The WM_MOUSEMOVE message is posted to a window when the cursor moves. If the mouse is not captured, the message is posted to the window that contains the cursor. Otherwise, the message is posted to the window that has captured the mouse.
@@ -621,6 +628,7 @@ public enum WM : uint
621628
/// <summary>
622629
/// Use WM_MOUSELAST to specify the last mouse message. Used with PeekMessage() Function.
623630
/// </summary>
631+
[ReflectionIgnore]
624632
MOUSELAST = 0x020E,
625633
/// <summary>
626634
/// The WM_PARENTNOTIFY message is sent to the parent of a child window when the child window is created or destroyed, or when the user clicks a mouse button while the cursor is over the child window. When the child window is being created, the system sends WM_PARENTNOTIFY just before the CreateWindow or CreateWindowEx function that creates the window returns. When the child window is being destroyed, the system sends the message before any processing to destroy the window takes place.
@@ -903,10 +911,12 @@ public enum WM : uint
903911
/// <summary>
904912
/// The WM_APP constant is used by applications to help define private messages, usually of the form WM_APP+X, where X is an integer value.
905913
/// </summary>
914+
[ReflectionIgnore]
906915
APP = 0x8000,
907916
/// <summary>
908917
/// The WM_USER constant is used by applications to help define private messages for use by private window classes, usually of the form WM_USER+X, where X is an integer value.
909918
/// </summary>
919+
[ReflectionIgnore]
910920
USER = 0x0400,
911921

912922
/// <summary>
@@ -930,51 +940,14 @@ public enum WM : uint
930940
/// Private message to signal focus RD shutdown. No parameters.
931941
/// </summary>
932942
RUBBERDUCK_SINKING = USER + 0x0D1E,
943+
}
933944

934-
/// <summary>
935-
/// The accessibility state has changed.
936-
/// </summary>
937-
HSHELL_ACCESSIBILITYSTATE = 11,
938-
/// <summary>
939-
/// The shell should activate its main window.
940-
/// </summary>
941-
HSHELL_ACTIVATESHELLWINDOW = 3,
942-
/// <summary>
943-
/// The user completed an input event (for example, pressed an application command button on the mouse or an application command key on the keyboard), and the application did not handle the WM_APPCOMMAND message generated by that input.
944-
/// If the Shell procedure handles the WM_COMMAND message, it should not call CallNextHookEx. See the Return Value section for more information.
945-
/// </summary>
946-
HSHELL_APPCOMMAND = 12,
947-
/// <summary>
948-
/// A window is being minimized or maximized. The system needs the coordinates of the minimized rectangle for the window.
949-
/// </summary>
950-
HSHELL_GETMINRECT = 5,
951-
/// <summary>
952-
/// Keyboard language was changed or a new keyboard layout was loaded.
953-
/// </summary>
954-
HSHELL_LANGUAGE = 8,
955-
/// <summary>
956-
/// The title of a window in the task bar has been redrawn.
957-
/// </summary>
958-
HSHELL_REDRAW = 6,
959-
/// <summary>
960-
/// The user has selected the task list. A shell application that provides a task list should return TRUE to prevent Windows from starting its task list.
961-
/// </summary>
962-
HSHELL_TASKMAN = 7,
963-
/// <summary>
964-
/// A top-level, unowned window has been created. The window exists when the system calls this hook.
965-
/// </summary>
966-
HSHELL_WINDOWCREATED = 1,
967-
/// <summary>
968-
/// A top-level, unowned window is about to be destroyed. The window still exists when the system calls this hook.
969-
/// </summary>
970-
HSHELL_WINDOWDESTROYED = 2,
971-
/// <summary>
972-
/// The activation has changed to a different top-level, unowned window.
973-
/// </summary>
974-
HSHELL_WINDOWACTIVATED = 4,
975-
/// <summary>
976-
/// A top-level window is being replaced. The window exists when the system calls this hook.
977-
/// </summary>
978-
HSHELL_WINDOWREPLACED = 13
945+
#if (DEBUG && (THIRSTY_DUCK || THIRSTY_DUCK_WM))
946+
947+
public static class WM_MAP
948+
{
949+
public static readonly Dictionary<uint, string> Lookup = EnumHelper.ToDictionary<WM, uint>();
979950
}
951+
952+
#endif
980953
}

Rubberduck.VBEEditor/WindowsApi/WinEvent.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
namespace Rubberduck.VBEditor.WindowsApi
1+
using System.Collections.Generic;
2+
using Rubberduck.VBEditor.Utility;
3+
4+
namespace Rubberduck.VBEditor.WindowsApi
25
{
36
public enum WinEvent
47
{
8+
[ReflectionIgnore]
59
Min = 0x0001,
610
SystemSound = 0x0001,
711
SystemAlert = 0x0002,
@@ -64,6 +68,7 @@ public enum WinEvent
6468
ObjectEnd = 0x80FF,
6569
AiaStart = 0xA000,
6670
AiaEnd = 0xAFFF,
71+
[ReflectionIgnore]
6772
Max = 0x7FFFFFFF
6873
}
6974

@@ -85,4 +90,13 @@ public enum ObjId
8590
QueryClasNameIdx = -12,
8691
NativeOM = -16
8792
}
93+
94+
#if (THIRSTY_DUCK || THIRSTY_DUCK_EVT)
95+
96+
public static class WinEventMap
97+
{
98+
public static readonly Dictionary<int, string> Lookup = EnumHelper.ToDictionary<WinEvent, int>();
99+
}
100+
101+
#endif
88102
}

0 commit comments

Comments
 (0)