@@ -27,101 +27,81 @@ public class ArgumentInfo
2727 // And this threading sample using tlbimp version of Windows 7 native UIA: http://code.msdn.microsoft.com/Windows-7-UI-Automation-6390614a/sourcecode?fileId=21469&pathId=715901329
2828 class IntelliSenseDisplay : MarshalByRefObject , IDisposable
2929 {
30- IntelliSenseDisplay _current ;
31- SynchronizationContext _syncContextMain ;
32- SynchronizationContext _syncContextAuto ; // On Automation thread.
33-
34- // NOTE: Add for separate UI Automation Thread
35- Thread _threadAuto ;
36-
37- readonly Dictionary < string , IntelliSenseFunctionInfo > _functionInfoMap ;
30+ SynchronizationContext _syncContextMain ; // Running on the main Excel thread (not a 'macro' context, though)
31+ SingleThreadSynchronizationContext _syncContextAuto ; // Running on the Automation thread.
32+
33+ readonly Dictionary < string , IntelliSenseFunctionInfo > _functionInfoMap =
34+ new Dictionary < string , IntelliSenseFunctionInfo > ( StringComparer . CurrentCultureIgnoreCase ) ;
3835
3936 WindowWatcher _windowWatcher ;
4037 FormulaEditWatcher _formulaEditWatcher ;
4138 PopupListWatcher _popupListWatcher ;
42- SelectDataSourceWatcher _selectDataSourceWatcher ;
39+ // SelectDataSourceWatcher _selectDataSourceWatcher;
4340
4441 // Need to make these late ...?
4542 ToolTipForm _descriptionToolTip ;
4643 ToolTipForm _argumentsToolTip ;
4744
48- private readonly List < string > _addInReferences ;
49-
5045 public IntelliSenseDisplay ( )
5146 {
52- Debug . Print ( "### Thread creating IntelliSenseDisplay: " + Thread . CurrentThread . ManagedThreadId ) ;
53-
54- _current = this ;
55- _functionInfoMap = new Dictionary < string , IntelliSenseFunctionInfo > ( StringComparer . CurrentCultureIgnoreCase ) ;
56- // TODO: Need a separate thread for UI Automation Client - event subscriptions should not be on main UI thread.
47+ // We expect this to be running in a macro context on the main Excel thread (ManagedThreadId = 1).
48+ Debug . Print ( $ "### Thread creating IntelliSenseDisplay: Managed { Thread . CurrentThread . ManagedThreadId } , Native { AppDomain . GetCurrentThreadId ( ) } ") ;
5749
5850 _syncContextMain = new WindowsFormsSynchronizationContext ( ) ;
5951
60- _addInReferences = new List < string > ( ) ;
61- }
62-
63- public void SetXllOwner ( string xllPath )
64- {
65- _threadAuto = new Thread ( ( ) => RunUIAutomation ( xllPath ) ) ;
66- _threadAuto . SetApartmentState ( ApartmentState . MTA ) ;
67- _threadAuto . Start ( ) ;
68- }
69-
70- public void RegisterFunctionInfo ( IntelliSenseFunctionInfo functionInfo )
71- {
72- // TODO : Dictionary from KeyLookup
73- _functionInfoMap . Add ( functionInfo . FunctionName , functionInfo ) ;
52+ // Make a separate thread and set to MTA, according to: https://msdn.microsoft.com/en-us/library/windows/desktop/ee671692%28v=vs.85%29.aspx
53+ var threadAuto = new Thread ( RunUIAutomation ) ;
54+ threadAuto . SetApartmentState ( ApartmentState . MTA ) ;
55+ threadAuto . Start ( ) ;
7456 }
7557
76- void RunUIAutomation ( string xllPath )
58+ // This runs on the new thread we've created to do all the Automation stuff (_threadAuto)
59+ // It returns only after when the SyncContext.Complete() has been called (from the IntelliSenseDisplay.Dispose() below)
60+ void RunUIAutomation ( )
7761 {
78- // NOTE: Add for separate UI Automation Thread
79- _syncContextAuto = new WindowsFormsSynchronizationContext ( ) ;
80- //_syncContextAuto = _syncContextMain;
62+ _syncContextAuto = new SingleThreadSynchronizationContext ( ) ;
8163
82- _windowWatcher = new WindowWatcher ( xllPath ) ;
64+ _windowWatcher = new WindowWatcher ( _syncContextAuto ) ;
8365 _formulaEditWatcher = new FormulaEditWatcher ( _windowWatcher , _syncContextAuto ) ;
8466 _popupListWatcher = new PopupListWatcher ( _windowWatcher , _syncContextAuto ) ;
85- _selectDataSourceWatcher = new SelectDataSourceWatcher ( _windowWatcher , _syncContextAuto ) ;
67+ // _selectDataSourceWatcher = new SelectDataSourceWatcher(_windowWatcher, _syncContextAuto);
8668
87- _windowWatcher . MainWindowChanged += OnMainWindowChanged ;
88- _popupListWatcher . SelectedItemChanged += OnSelectedItemChanged ;
89- _formulaEditWatcher . StateChanged += OnStateChanged ;
69+ _windowWatcher . MainWindowChanged += _windowWatcher_MainWindowChanged ;
70+ _popupListWatcher . SelectedItemChanged += _popupListWatcher_SelectedItemChanged ;
71+ _formulaEditWatcher . StateChanged += _formulaEditWatcher_StateChanged ;
9072
9173 _windowWatcher . TryInitialize ( ) ;
92- // NOTE: Add for separate UI Automation Thread
93- Application . Run ( ) ;
74+
75+ _syncContextAuto . RunOnCurrentThread ( ) ;
9476 }
9577
96- private void OnMainWindowChanged ( object sender , EventArgs args )
78+ // Runs on the auto thread
79+ void _windowWatcher_MainWindowChanged ( object sender , EventArgs args )
9780 {
98- Debug . Print ( "### Thread calling MainWindowChanged event: " + Thread . CurrentThread . ManagedThreadId ) ;
99- _syncContextMain . Post ( delegate { MainWindowChanged ( ) ; } , null ) ;
100- // MainWindowChanged();
81+ _syncContextMain . Post ( MainWindowChanged , null ) ;
10182 }
10283
103- private void OnSelectedItemChanged ( object sender , EventArgs args )
84+ // Runs on the auto thread
85+ void _popupListWatcher_SelectedItemChanged ( object sender , EventArgs args )
10486 {
105- _syncContextMain . Post ( delegate { PopupListSelectedItemChanged ( ) ; } , null ) ;
106- // PopupListSelectedItemChanged();
87+ _syncContextMain . Post ( PopupListSelectedItemChanged , null ) ;
10788 }
10889
109- private void OnStateChanged ( object sender , StateChangeEventArgs args )
90+ // Runs on the auto thread
91+ void _formulaEditWatcher_StateChanged ( object sender , FormulaEditWatcher . StateChangeEventArgs args )
11092 {
111- _syncContextMain . Post ( delegate { FormulaEditStateChanged ( args . StateChangeType ) ; } , null ) ;
112- // FormulaEditStateChanged();
93+ _syncContextMain . Post ( FormulaEditStateChanged , args . StateChangeType ) ;
11394 }
11495
115- void MainWindowChanged ( )
96+ // Runs on the main thread
97+ void MainWindowChanged ( object _unused_ )
11698 {
11799 // TODO: This is to guard against shutdown, but we should not have a race here
118100 // - shutdown should be on the main thread, as is this event handler.
119101 if ( _windowWatcher == null ) return ;
120102
121-
122103 // TODO: !!! Reset / re-parent ToolTipWindows
123- Debug . Print ( "MainWindow Change - " + _windowWatcher . MainWindow . ToString ( "X" ) ) ;
124- Debug . Print ( "### Thread calling MainWindowChanged method: " + Thread . CurrentThread . ManagedThreadId ) ;
104+ Debug . Print ( $ "IntelliSenseDisplay - MainWindowChanged - New window - { _windowWatcher . MainWindow : X} , Thread { Thread . CurrentThread . ManagedThreadId } ") ;
125105
126106 // _descriptionToolTip.SetOwner(e.Handle); // Not Parent, of course!
127107 if ( _descriptionToolTip != null )
@@ -143,8 +123,11 @@ void MainWindowChanged()
143123 // _descriptionToolTip = new ToolTipWindow("", _windowWatcher.MainWindow);
144124 }
145125
146- void PopupListSelectedItemChanged ( )
126+ // Runs on the main thread
127+ void PopupListSelectedItemChanged ( object _unused_ )
147128 {
129+ Debug . Print ( $ "IntelliSenseDisplay - PopupListSelectedItemChanged - New text - { _popupListWatcher ? . SelectedItemText } , Thread { Thread . CurrentThread . ManagedThreadId } ") ;
130+
148131 if ( _popupListWatcher == null ) return ;
149132 string functionName = _popupListWatcher . SelectedItemText ;
150133
@@ -158,8 +141,9 @@ void PopupListSelectedItemChanged()
158141 }
159142 // It's ours!
160143 _descriptionToolTip . ShowToolTip (
161- new FormattedText { GetFunctionDescription ( functionInfo ) } ,
162- ( int ) _popupListWatcher . SelectedItemBounds . Right + 25 , ( int ) _popupListWatcher . SelectedItemBounds . Top ) ;
144+ text : new FormattedText { GetFunctionDescription ( functionInfo ) } ,
145+ left : ( int ) _popupListWatcher . SelectedItemBounds . Right + 25 ,
146+ top : ( int ) _popupListWatcher . SelectedItemBounds . Top ) ;
163147 }
164148 else
165149 {
@@ -170,22 +154,24 @@ void PopupListSelectedItemChanged()
170154 }
171155 }
172156
157+ // Runs on the main thread
173158 // TODO: Need better formula parsing story here
174159 // Here are some ideas: http://fastexcel.wordpress.com/2013/10/27/parsing-functions-from-excel-formulas-using-vba-is-mid-or-a-byte-array-the-best-method/
175- void FormulaEditStateChanged ( StateChangeTypeEnum stateChangeType )
160+ void FormulaEditStateChanged ( object stateChangeTypeObj )
176161 {
162+ var stateChangeType = ( FormulaEditWatcher . StateChangeType ) stateChangeTypeObj ;
177163 // Check for watcher already disposed
178164 // CONSIDER: How to manage threading with disposal...?
179165 if ( _formulaEditWatcher == null ) return ;
180166
181- if ( stateChangeType == StateChangeTypeEnum . Move && _argumentsToolTip != null )
167+ if ( stateChangeType == FormulaEditWatcher . StateChangeType . Move && _argumentsToolTip != null )
182168 {
183169 _argumentsToolTip . MoveToolTip (
184170 ( int ) _formulaEditWatcher . EditWindowBounds . Left , ( int ) _formulaEditWatcher . EditWindowBounds . Bottom + 5 ) ;
185171 return ;
186172 }
187173
188- Debug . Print ( "^^^ FormulaEditStateChanged. CurrentPrefix: " + _formulaEditWatcher . CurrentPrefix ) ;
174+ Debug . Print ( $ "^^^ FormulaEditStateChanged. CurrentPrefix: { _formulaEditWatcher . CurrentPrefix } , Thread { Thread . CurrentThread . ManagedThreadId } " ) ;
189175 if ( _formulaEditWatcher . IsEditingFormula && _formulaEditWatcher . CurrentPrefix != null )
190176 {
191177 string prefix = _formulaEditWatcher . CurrentPrefix ;
@@ -218,6 +204,7 @@ void FormulaEditStateChanged(StateChangeTypeEnum stateChangeType)
218204 _argumentsToolTip . Hide ( ) ;
219205 }
220206
207+ // TODO: Probably not a good place for LINQ !?
221208 IEnumerable < TextLine > GetFunctionDescription ( IntelliSenseFunctionInfo functionInfo )
222209 {
223210 return
@@ -295,66 +282,46 @@ TextLine GetArgumentDescription(IntelliSenseFunctionInfo.ArgumentInfo argumentIn
295282 } ;
296283 }
297284
298- public void Shutdown ( )
299- {
300- Debug . Print ( "Shutdown!" ) ;
301- if ( _current != null )
302- {
303- try
304- {
305- _current . Dispose ( ) ;
306- }
307- catch ( Exception ex )
308- {
309- Debug . Print ( "!!! Error during Shutdown: " + ex ) ;
310- }
285+ // public void Shutdown()
286+ // {
287+ // Debug.Print("Shutdown!");
288+ // if (_current != null)
289+ // {
290+ // try
291+ // {
292+ // _current.Dispose();
293+ // }
294+ // catch (Exception ex)
295+ // {
296+ // Debug.Print("!!! Error during Shutdown: " + ex);
297+ // }
311298
312- _current = null ;
313- }
314- }
315-
316- public void AddReference ( string xllName )
317- {
318- _addInReferences . Add ( xllName ) ;
319- }
320-
321- public void RemoveReference ( string xllName )
322- {
323- List < string > functionsToRemove =
324- _functionInfoMap . Where ( p => p . Value . SourcePath == xllName ) . Select ( p => p . Key ) . ToList ( ) ;
325-
326- foreach ( string func in functionsToRemove )
327- {
328- _functionInfoMap . Remove ( func ) ;
329- }
330-
331- _addInReferences . Remove ( xllName ) ;
332- }
333-
334- public bool IsUsed ( )
335- {
336- return _addInReferences . Count > 0 ;
337- }
299+ // _current = null;
300+ // }
301+ //}
338302
339303 public void Dispose ( )
340304 {
341- _current . _syncContextAuto . Send ( delegate
305+ if ( _syncContextAuto == null )
306+ return ;
307+
308+ _syncContextAuto . Send ( delegate
342309 {
343310 if ( _windowWatcher != null )
344311 {
345- _windowWatcher . MainWindowChanged -= OnMainWindowChanged ;
312+ _windowWatcher . MainWindowChanged -= _windowWatcher_MainWindowChanged ;
346313 _windowWatcher . Dispose ( ) ;
347314 _windowWatcher = null ;
348315 }
349316 if ( _formulaEditWatcher != null )
350317 {
351- _formulaEditWatcher . StateChanged -= OnStateChanged ;
318+ _formulaEditWatcher . StateChanged -= _formulaEditWatcher_StateChanged ;
352319 _formulaEditWatcher . Dispose ( ) ;
353320 _formulaEditWatcher = null ;
354321 }
355322 if ( _popupListWatcher != null )
356323 {
357- _popupListWatcher . SelectedItemChanged -= OnSelectedItemChanged ;
324+ _popupListWatcher . SelectedItemChanged -= _popupListWatcher_SelectedItemChanged ;
358325 _popupListWatcher . Dispose ( ) ;
359326 _popupListWatcher = null ;
360327 }
@@ -374,11 +341,18 @@ public void Dispose()
374341 }
375342 } , null ) ;
376343
377- // NOTE: Add for separate UI Automation Thread
378- _threadAuto . Abort ( ) ;
379- _threadAuto = null ;
344+ _syncContextAuto . Complete ( ) ;
345+ // CONSIDER: Maybe wait for the _syncContextAuto to finish...?
380346 _syncContextAuto = null ;
347+
381348 _syncContextMain = null ;
382349 }
350+
351+ public void RegisterFunctionInfo ( IntelliSenseFunctionInfo functionInfo )
352+ {
353+ // TODO : Dictionary from KeyLookup
354+ _functionInfoMap . Add ( functionInfo . FunctionName , functionInfo ) ;
355+ }
356+ // TODO: How to UnregisterFunctionInfo ...?
383357 }
384358}
0 commit comments