Skip to content

Commit 9a85d7e

Browse files
committed
Update LocationWatcher to try to address GC-collected delegate crash
1 parent f96cda5 commit 9a85d7e

File tree

4 files changed

+63
-13
lines changed

4 files changed

+63
-13
lines changed

Source/ExcelDna.IntelliSense/IntelliSenseServer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace ExcelDna.IntelliSense
3030
// REMEMBER: COM events are not necessarily safe macro contexts.
3131
public static class IntelliSenseServer
3232
{
33-
const string ServerVersion = "1.7.2"; // TODO: Define and manage this somewhere else
33+
const string ServerVersion = "1.7.3"; // TODO: Define and manage this somewhere else
3434

3535
// NOTE: Do not change these constants in custom versions.
3636
// They are part of the co-operative safety mechanism allowing different add-ins providing IntelliSense to work together safely.

Source/ExcelDna.IntelliSense/UIMonitor/WinEvents.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.ComponentModel;
34
using System.Diagnostics;
45
using System.Runtime.InteropServices;
@@ -63,7 +64,7 @@ public enum WinEvent : uint
6364
EVENT_OBJECT_DESTROY = 0x8001, // hwnd ID idChild is destroyed item
6465
EVENT_OBJECT_SHOW = 0x8002, // hwnd ID idChild is shown item
6566
EVENT_OBJECT_HIDE = 0x8003, // hwnd ID idChild is hidden item
66-
EVENT_OBJECT_REORDER = 0x8004, // hwnd ID idChild is parent of zordering children
67+
EVENT_OBJECT_REORDER = 0x8004, // hwnd ID idChild is parent of zordering children // NOTE: Gets fired constantly when an Excel function is selected in the function list
6768
EVENT_OBJECT_FOCUS = 0x8005, // hwnd ID idChild is focused item
6869
EVENT_OBJECT_SELECTION = 0x8006, // hwnd ID idChild is selected item (if only one), or idChild is OBJID_WINDOW if complex
6970
EVENT_OBJECT_SELECTIONADD = 0x8007, // hwnd ID idChild is item added
@@ -121,6 +122,8 @@ public enum WinEventObjectId : int
121122
readonly WinEvent _eventMin;
122123
readonly WinEvent _eventMax;
123124

125+
public Dictionary<WinEvent, Action<WinEvent>> DirectCallEvents = new Dictionary<WinEvent, Action<WinEvent>>();
126+
124127
// Can be called on any thread, but installed by calling into the main thread, and will only start receiving events then
125128
public WinEventHook(WinEvent eventMin, WinEvent eventMax, SynchronizationContext syncContextAuto, SynchronizationContext syncContextMain, IntPtr hWndFilterOrZero)
126129
{
@@ -184,9 +187,16 @@ void UninstallWinEventHook(object _)
184187
void HandleWinEvent(IntPtr hWinEventHook, WinEvent eventType, IntPtr hWnd,
185188
WinEventObjectId idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
186189
{
187-
// Debug.Print($"++++++++++++++ WinEvent Received: {eventType} on thread {Thread.CurrentThread.ManagedThreadId} from thread {dwEventThread} +++++++++++++++++++++++++++");
190+
Debug.Print($"++++++++++++++ WinEvent Received: {eventType} on thread {Thread.CurrentThread.ManagedThreadId} from thread {dwEventThread} +++++++++++++++++++++++++++");
188191
try
189192
{
193+
// Some events we want to handle immediately on the main thread, particularly for the move strat / end that we use for the on-demand hook
194+
if (DirectCallEvents.TryGetValue(eventType, out var action))
195+
{
196+
action(eventType);
197+
return;
198+
}
199+
190200
if (_hWndFilterOrZero != IntPtr.Zero && hWnd != _hWndFilterOrZero)
191201
return;
192202

Source/ExcelDna.IntelliSense/UIMonitor/WindowLocationWatcher.cs

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,38 +27,71 @@ public class WindowLocationWatcher : IDisposable
2727
// and then hook again upon encountering EVENT_SYSTEM_MOVESIZEEND (see UnhookFromLocationChangeUponDraggingExcelMainWindow).
2828
public WindowLocationWatcher(IntPtr hWnd, SynchronizationContext syncContextAuto, SynchronizationContext syncContextMain)
2929
{
30+
Debug.Print("===========>>>>>>>> WindowsLocationWatcher <<<<<<<<<<<<===========");
31+
3032
_hWnd = hWnd;
3133
_syncContextAuto = syncContextAuto;
3234
_syncContextMain = syncContextMain;
33-
_windowMoveSizeHook = new WinEventHook(WinEventHook.WinEvent.EVENT_SYSTEM_MOVESIZESTART, WinEventHook.WinEvent.EVENT_SYSTEM_MOVESIZEEND, _syncContextAuto, syncContextMain, _hWnd);
34-
_windowMoveSizeHook.WinEventReceived += _windowMoveSizeHook_WinEventReceived;
35+
36+
_syncContextMain.Post(_ => SetUpHooks(), null);
37+
}
38+
39+
void SetUpHooks()
40+
{
41+
_windowMoveSizeHook = new WinEventHook(WinEventHook.WinEvent.EVENT_SYSTEM_MOVESIZESTART, WinEventHook.WinEvent.EVENT_SYSTEM_MOVESIZEEND, _syncContextAuto, _syncContextMain, _hWnd);
42+
// _windowMoveSizeHook.WinEventReceived += _windowMoveSizeHook_WinEventReceived;
43+
_windowMoveSizeHook.DirectCallEvents[WinEventHook.WinEvent.EVENT_SYSTEM_MOVESIZESTART] = _ =>
44+
{
45+
// Runs on the main thread
46+
ClearLocationChangeEventListener();
47+
};
48+
_windowMoveSizeHook.DirectCallEvents[WinEventHook.WinEvent.EVENT_SYSTEM_MOVESIZEEND] = _ =>
49+
{
50+
// Runs on the main thread
51+
SetUpLocationChangeEventListener();
52+
_syncContextAuto.Post(__ => NotifyLocationChanged(), null);
53+
};
3554

3655
SetUpLocationChangeEventListener();
3756
}
3857

3958
void SetUpLocationChangeEventListener()
4059
{
41-
60+
Debug.Assert(Thread.CurrentThread.ManagedThreadId == 1);
61+
Debug.Assert(_locationChangeEventHook == null);
4262
_locationChangeEventHook = new WinEventHook(WinEventHook.WinEvent.EVENT_OBJECT_LOCATIONCHANGE, WinEventHook.WinEvent.EVENT_OBJECT_LOCATIONCHANGE, _syncContextAuto, _syncContextMain, IntPtr.Zero);
4363
_locationChangeEventHook.WinEventReceived += _windowMoveSizeHook_WinEventReceived;
4464
}
4565

66+
void ClearLocationChangeEventListener()
67+
{
68+
Debug.Assert(Thread.CurrentThread.ManagedThreadId == 1);
69+
if (_locationChangeEventHook != null)
70+
{
71+
_locationChangeEventHook.Dispose();
72+
_locationChangeEventHook = null;
73+
}
74+
}
75+
4676
// This allows us to temporarily stop listening to EVENT_OBJECT_LOCATIONCHANGE events when the user is dragging the Excel main window.
4777
// Otherwise we are going to bump into https://github.com/Excel-DNA/IntelliSense/issues/123. The rest of the time we need to stay
4878
// hooked to EVENT_OBJECT_LOCATIONCHANGE for IntelliSense to work correctly (see https://github.com/Excel-DNA/IntelliSense/issues/124).
4979
void UnhookFromLocationChangeUponDraggingExcelMainWindow(WinEventHook.WinEventArgs e)
5080
{
5181
if (e.EventType == WinEventHook.WinEvent.EVENT_SYSTEM_MOVESIZESTART)
5282
{
53-
_syncContextMain.Post(_ => _locationChangeEventHook?.Dispose(), null);
83+
Debug.WriteLine("===========>>>>>>>> EVENT_SYSTEM_MOVESIZESTART <<<<<<<<<<<<===========");
84+
_syncContextMain.Post(_ => ClearLocationChangeEventListener(), null);
5485
}
5586

5687
if (e.EventType == WinEventHook.WinEvent.EVENT_SYSTEM_MOVESIZEEND)
5788
{
89+
Debug.WriteLine("===========>>>>>>>> EVENT_SYSTEM_MOVESIZEEND <<<<<<<<<<<<===========");
5890
_syncContextMain.Post(_ => SetUpLocationChangeEventListener(), null);
5991
}
6092
}
6193

94+
// Runs on the automation thread
6295
void _windowMoveSizeHook_WinEventReceived(object sender, WinEventHook.WinEventArgs winEventArgs)
6396
{
6497
#if DEBUG
@@ -67,6 +100,11 @@ void _windowMoveSizeHook_WinEventReceived(object sender, WinEventHook.WinEventAr
67100

68101
UnhookFromLocationChangeUponDraggingExcelMainWindow(winEventArgs);
69102

103+
NotifyLocationChanged();
104+
}
105+
106+
void NotifyLocationChanged()
107+
{
70108
LocationChanged?.Invoke(this, EventArgs.Empty);
71109
}
72110

@@ -85,6 +123,7 @@ public void Dispose()
85123
_locationChangeEventHook.Dispose();
86124
_locationChangeEventHook = null;
87125
}
126+
Debug.Print("===========>>>>>>>> WindowsLocationWatcher Disposed <<<<<<<<<<<<===========");
88127
}
89128
}
90129
}

Source/ExcelDna.IntelliSense/UIMonitor/WindowWatcher.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,15 @@ public WindowWatcher(SynchronizationContext syncContextAuto, SynchronizationCont
154154
// EVENT_OBJECT_DESTROY
155155
// EVENT_OBJECT_SHOW
156156
// EVENT_OBJECT_HIDE
157-
// EVENT_OBJECT_REORDER
157+
// >>>>>>>>>>>>>>>>>>>>>>> EVENT_OBJECT_REORDER <<<<<<<<<<<<<<<<< NOT THIS ONE
158158
// EVENT_OBJECT_FOCUS
159159
// EVENT_OBJECT_SELECTION
160-
// EVENT_OBJECT_SELECTIONADD
161-
// EVENT_OBJECT_SELECTIONREMOVE
162-
// EVENT_OBJECT_SELECTIONWITHIN
163-
// EVENT_OBJECT_STATECHANGE (0x800A = 32778)
164-
_windowStateChangeHooks.Add(new WinEventHook(WinEventHook.WinEvent.EVENT_OBJECT_CREATE, WinEventHook.WinEvent.EVENT_OBJECT_STATECHANGE, syncContextAuto, syncContextMain, IntPtr.Zero));
160+
// >>>>>>>>>>>>>>>>>>>>>>> EVENT_OBJECT_SELECTIONADD <<<<<<<<<<<<<<<<< NOT THIS ONE
161+
// >>>>>>>>>>>>>>>>>>>>>>> EVENT_OBJECT_SELECTIONREMOVE <<<<<<<<<<<<<<<<< NOT THIS ONE
162+
// >>>>>>>>>>>>>>>>>>>>>>> EVENT_OBJECT_SELECTIONWITHIN <<<<<<<<<<<<<<<<< NOT THIS ONE
163+
// >>>>>>>>>>>>>>>>>>>>>>> EVENT_OBJECT_STATECHANGE (0x800A = 32778) <<<<<<<<<<<<<<<<< NOT THIS ONE
164+
_windowStateChangeHooks.Add(new WinEventHook(WinEventHook.WinEvent.EVENT_OBJECT_CREATE, WinEventHook.WinEvent.EVENT_OBJECT_HIDE, syncContextAuto, syncContextMain, IntPtr.Zero));
165+
_windowStateChangeHooks.Add(new WinEventHook(WinEventHook.WinEvent.EVENT_OBJECT_FOCUS, WinEventHook.WinEvent.EVENT_OBJECT_SELECTION, syncContextAuto, syncContextMain, IntPtr.Zero));
165166
_windowStateChangeHooks.Add(new WinEventHook(WinEventHook.WinEvent.EVENT_SYSTEM_CAPTURESTART, WinEventHook.WinEvent.EVENT_SYSTEM_CAPTURESTART, syncContextAuto, syncContextMain, IntPtr.Zero));
166167

167168
foreach (var windowStateChangeHook in _windowStateChangeHooks)

0 commit comments

Comments
 (0)