Skip to content

Commit 7a273d0

Browse files
committed
More threading clean-up
(Tooltip move is now broken)
1 parent 9c3cc23 commit 7a273d0

File tree

5 files changed

+150
-139
lines changed

5 files changed

+150
-139
lines changed

Source/ExcelDna.IntelliSense/IntelliSenseDisplay.cs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,13 @@ void RunUIAutomation()
6161
{
6262
_syncContextAuto = new SingleThreadSynchronizationContext();
6363

64+
// Create and hook together the various watchers
6465
_windowWatcher = new WindowWatcher(_syncContextAuto);
6566
_formulaEditWatcher = new FormulaEditWatcher(_windowWatcher, _syncContextAuto);
6667
_popupListWatcher = new PopupListWatcher(_windowWatcher, _syncContextAuto);
6768
// _selectDataSourceWatcher = new SelectDataSourceWatcher(_windowWatcher, _syncContextAuto);
6869

70+
// These are the events we're interested in for showing, hiding and updating the IntelliSense forms
6971
_windowWatcher.MainWindowChanged += _windowWatcher_MainWindowChanged;
7072
_popupListWatcher.SelectedItemChanged += _popupListWatcher_SelectedItemChanged;
7173
_formulaEditWatcher.StateChanged += _formulaEditWatcher_StateChanged;
@@ -75,30 +77,36 @@ void RunUIAutomation()
7577
_syncContextAuto.RunOnCurrentThread();
7678
}
7779

78-
// Runs on the auto thread
80+
#region Receive watcher events, and post to main thread
81+
// Runs on our automation thread
7982
void _windowWatcher_MainWindowChanged(object sender, EventArgs args)
8083
{
84+
Logger.Display.Verbose("! MainWindowChanged");
8185
_syncContextMain.Post(MainWindowChanged, null);
8286
}
8387

84-
// Runs on the auto thread
88+
// Runs on our automation thread
8589
void _popupListWatcher_SelectedItemChanged(object sender, EventArgs args)
8690
{
91+
Logger.Display.Verbose("! PopupList SelectedItemChanged");
8792
_syncContextMain.Post(PopupListSelectedItemChanged, null);
8893
}
8994

90-
// Runs on the auto thread
95+
// Runs on our automation thread
9196
void _formulaEditWatcher_StateChanged(object sender, FormulaEditWatcher.StateChangeEventArgs args)
9297
{
98+
Logger.Display.Verbose($"! FormulaEdit StateChanged ({args.StateChangeType})");
9399
_syncContextMain.Post(FormulaEditStateChanged, args.StateChangeType);
94100
}
101+
#endregion
95102

96103
// Runs on the main thread
97104
void MainWindowChanged(object _unused_)
98105
{
99106
// TODO: This is to guard against shutdown, but we should not have a race here
100107
// - shutdown should be on the main thread, as is this event handler.
101-
if (_windowWatcher == null) return;
108+
if (_windowWatcher == null)
109+
return;
102110

103111
// TODO: !!! Reset / re-parent ToolTipWindows
104112
Debug.Print($"IntelliSenseDisplay - MainWindowChanged - New window - {_windowWatcher.MainWindow:X}, Thread {Thread.CurrentThread.ManagedThreadId}");
@@ -128,7 +136,8 @@ void PopupListSelectedItemChanged(object _unused_)
128136
{
129137
Debug.Print($"IntelliSenseDisplay - PopupListSelectedItemChanged - New text - {_popupListWatcher?.SelectedItemText}, Thread {Thread.CurrentThread.ManagedThreadId}");
130138

131-
if (_popupListWatcher == null) return;
139+
if (_popupListWatcher == null)
140+
return;
132141
string functionName = _popupListWatcher.SelectedItemText;
133142

134143
IntelliSenseFunctionInfo functionInfo;
@@ -162,7 +171,8 @@ void FormulaEditStateChanged(object stateChangeTypeObj)
162171
var stateChangeType = (FormulaEditWatcher.StateChangeType)stateChangeTypeObj;
163172
// Check for watcher already disposed
164173
// CONSIDER: How to manage threading with disposal...?
165-
if (_formulaEditWatcher == null) return;
174+
if (_formulaEditWatcher == null)
175+
return;
166176

167177
if (stateChangeType == FormulaEditWatcher.StateChangeType.Move && _argumentsToolTip != null)
168178
{
@@ -305,7 +315,8 @@ public void Dispose()
305315
if (_syncContextAuto == null)
306316
return;
307317

308-
_syncContextAuto.Send(delegate
318+
// Send is not supported on _syncContextAuto
319+
_syncContextAuto.Post(delegate
309320
{
310321
if (_windowWatcher != null)
311322
{
@@ -325,6 +336,8 @@ public void Dispose()
325336
_popupListWatcher.Dispose();
326337
_popupListWatcher = null;
327338
}
339+
_syncContextAuto.Complete();
340+
_syncContextAuto = null;
328341
}, null);
329342

330343
_syncContextMain.Send(delegate
@@ -341,10 +354,6 @@ public void Dispose()
341354
}
342355
}, null);
343356

344-
_syncContextAuto.Complete();
345-
// CONSIDER: Maybe wait for the _syncContextAuto to finish...?
346-
_syncContextAuto = null;
347-
348357
_syncContextMain = null;
349358
}
350359

Source/ExcelDna.IntelliSense/Logging.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,14 @@ namespace ExcelDna.IntelliSense
5151

5252
// NOTE: To simplify configuration (so that we provide one TraceSource per referenced assembly) and still allow some grouping
5353
// we use the EventId to define a trace event classification.
54+
// These names are not surfaces to the Trace Listeners - just the IDs, so we should document them.
5455
enum IntelliSenseTraceEventId
5556
{
5657
Initialization = 1,
5758
Provider = 2,
5859
WinEvents = 3,
59-
WindowWatcher = 4
60+
WindowWatcher = 4,
61+
Display = 5
6062
}
6163

6264
// TraceLogger manages the IntelliSenseTraceSource that we use for logging.
@@ -211,5 +213,6 @@ public void Error(Exception ex, string message, params object[] args)
211213
static internal Logger Provider { get; } = new Logger(IntelliSenseTraceEventId.Provider);
212214
static internal Logger WinEvents { get; } = new Logger(IntelliSenseTraceEventId.WinEvents);
213215
static internal Logger WindowWatcher { get; } = new Logger(IntelliSenseTraceEventId.WindowWatcher);
216+
static internal Logger Display { get; } = new Logger(IntelliSenseTraceEventId.Display);
214217
}
215218
}

Source/ExcelDna.IntelliSense/SingleThreadSynchronizationContext.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,16 @@ public override void Send(SendOrPostCallback d, object state)
3737
public void RunOnCurrentThread()
3838
{
3939
foreach (var workItem in m_queue.GetConsumingEnumerable())
40-
workItem.Key(workItem.Value);
40+
{
41+
try
42+
{
43+
workItem.Key(workItem.Value);
44+
}
45+
catch (Exception ex)
46+
{
47+
Debug.Print($"### Unhandled exception on Automation thread - {ex.Message}");
48+
}
49+
}
4150

4251
Debug.Print("SingleThreadSynchronizationContext Complete!");
4352
}

Source/ExcelDna.IntelliSense/ToolTipForm.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ class ToolTipForm : Form
1414
private Label label;
1515
Win32Window _owner;
1616

17-
public ToolTipForm(IntPtr hwndParent)
17+
public ToolTipForm(IntPtr hwndOwner)
1818
{
1919
InitializeComponent();
20-
_owner = new Win32Window(hwndParent);
20+
_owner = new Win32Window(hwndOwner);
2121
// _owner = new NativeWindow();
2222
// _owner.AssignHandle(hwndParent); (...with ReleaseHandle in Dispose)
23-
Debug.Print("Created ToolTipForm with parent " + hwndParent + " Owner Handle: " + (_owner == null ? "<NULL> " : _owner.Handle.ToString()) );
23+
Debug.Print($"Created ToolTipForm with owner {hwndOwner}");
2424
}
2525

2626
public void ShowToolTip()
@@ -89,6 +89,7 @@ protected override CreateParams CreateParams
8989
{
9090
get
9191
{
92+
// NOTE: I've seen exception with invalid handle in the base.CreateParams call here...
9293
CreateParams baseParams = base.CreateParams;
9394
baseParams.ClassStyle |= CS_DROPSHADOW;
9495
baseParams.ExStyle |= ( WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW );
@@ -128,7 +129,8 @@ protected override void OnPaint(PaintEventArgs e)
128129
// TODO: Empty strings are a problem....
129130
var text = run.Text == "" ? " " : run.Text;
130131

131-
DrawString(e.Graphics, SystemBrushes.WindowText, ref layoutRect, out textSize, format, text, font);
132+
// TODO: Find the color SystemBrushes.ControlDarkDark
133+
DrawString(e.Graphics, SystemBrushes.WindowFrame, ref layoutRect, out textSize, format, text, font);
132134
totalWidth += textSize.Width;
133135
lineHeight = Math.Max(lineHeight, textSize.Height);
134136
}

0 commit comments

Comments
 (0)