Skip to content

Commit 4388c7a

Browse files
committed
Fix up providers
1 parent 9fd24bb commit 4388c7a

File tree

3 files changed

+138
-57
lines changed

3 files changed

+138
-57
lines changed

Source/ExcelDna.IntelliSense/ExcelDna.IntelliSense.csproj

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,21 @@
4040
<HintPath>..\packages\UIAComWrapper.1.1.0.14\lib\net40\Interop.UIAutomationClient.dll</HintPath>
4141
<EmbedInteropTypes>False</EmbedInteropTypes>
4242
</Reference>
43+
<Reference Include="Microsoft.Office.Interop.Excel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL">
44+
<EmbedInteropTypes>True</EmbedInteropTypes>
45+
<HintPath>..\packages\ExcelDna.Interop.14.0.1\lib\Microsoft.Office.Interop.Excel.dll</HintPath>
46+
<Private>True</Private>
47+
</Reference>
48+
<Reference Include="Microsoft.Vbe.Interop, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL">
49+
<EmbedInteropTypes>True</EmbedInteropTypes>
50+
<HintPath>..\packages\ExcelDna.Interop.14.0.1\lib\Microsoft.Vbe.Interop.dll</HintPath>
51+
<Private>True</Private>
52+
</Reference>
53+
<Reference Include="Office, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c, processorArchitecture=MSIL">
54+
<EmbedInteropTypes>True</EmbedInteropTypes>
55+
<HintPath>..\packages\ExcelDna.Interop.14.0.1\lib\Office.dll</HintPath>
56+
<Private>True</Private>
57+
</Reference>
4358
<Reference Include="System" />
4459
<Reference Include="System.Core" />
4560
<Reference Include="System.Drawing" />

Source/ExcelDna.IntelliSense/IntelliSenseProvider.cs

Lines changed: 122 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using System.Threading;
77
using ExcelDna.Integration;
8+
using Microsoft.Office.Interop.Excel;
89

910
namespace ExcelDna.IntelliSense
1011
{
@@ -33,14 +34,15 @@ namespace ExcelDna.IntelliSense
3334
// TODO: Consider interaction with Application.MacroOptions. (or not?)
3435

3536
// TODO: We might relax the threading rules, to say that Refresh runs on the same thread as Invalidate
36-
// TODO: We might get rid of Refresh (since that runs in the Invalidate context)
37+
// TODO: We might get rid of Refresh (since that runs in the Invalidate context)
38+
// TODO: The two providers have been refactored to work very similarly - maybe be can extract out a base class...
3739
interface IIntelliSenseProvider : IDisposable
3840
{
3941
void Initialize(); // Executed in a macro context, on the main Excel thread
4042
void Refresh(); // Executed in a macro context, on the main Excel thread
4143
event EventHandler Invalidate; // Must be raised in a macro context, on the main thread
4244

43-
IEnumerable<IntelliSenseFunctionInfo> GetFunctionInfos(); // Called from a worker thread - no Excel or COM access (probably an MTA thread involved in the UI Automation)
45+
IList<IntelliSenseFunctionInfo> GetFunctionInfos(); // Called from a worker thread - no Excel or COM access (probably an MTA thread involved in the UI Automation)
4446
}
4547

4648
// Provides IntelliSense info for all Excel-DNA based .xll add-ins, using the built-in RegistrationInfo helper function.
@@ -137,53 +139,14 @@ public IEnumerable<IntelliSenseFunctionInfo> GetFunctionInfos()
137139
public event EventHandler Invalidate;
138140

139141
public ExcelDnaIntelliSenseProvider()
140-
{
141-
var threadRefresh = new Thread(RunRefreshWatch);
142-
threadRefresh.Start();
143-
}
144-
145-
// DANGER: Still subject to LoaderLock problem...
146-
void loaderNotification_LoadNotification(object sender, LoaderNotification.NotificationEventArgs e)
147-
{
148-
Debug.Print($"@>@>@>@> LoadNotification: {e.Reason} - {e.FullDllName}");
149-
if (e.FullDllName.EndsWith(".xll", StringComparison.OrdinalIgnoreCase))
150-
_syncContextExcel.Post(ProcessLoadNotification, e);
151-
}
152-
153-
// Runs on the main thread, in a macro context
154-
void ProcessLoadNotification(object state)
155-
{
156-
Debug.Assert(Thread.CurrentThread.ManagedThreadId == 1);
157-
// we might want to introduce a delay here, so that the .xll can complete loading...
158-
var notification = (LoaderNotification.NotificationEventArgs)state;;
159-
var xllPath = notification.FullDllName;
160-
lock (_xllRegistrationInfos)
161-
{
162-
XllRegistrationInfo regInfo;
163-
if (!_xllRegistrationInfos.TryGetValue(xllPath, out regInfo))
164-
{
165-
if (notification.Reason == LoaderNotification.Reason.Loaded)
166-
{
167-
regInfo = new XllRegistrationInfo(xllPath);
168-
_xllRegistrationInfos[xllPath] = regInfo;
169-
regInfo.Refresh();
170-
OnInvalidate();
171-
}
172-
}
173-
else if (notification.Reason == LoaderNotification.Reason.Unloaded)
174-
{
175-
_xllRegistrationInfos.Remove(xllPath);
176-
}
177-
}
178-
}
179-
180-
void RunRefreshWatch()
181142
{
182143
_loaderNotification = new LoaderNotification();
183144
_loaderNotification.LoadNotification += loaderNotification_LoadNotification;
184145
_syncContextExcel = new ExcelSynchronizationContext();
185146
}
186147

148+
#region IIntelliSenseProvider implementation
149+
187150
// Must be called on the main Excel thread
188151
public void Initialize()
189152
{
@@ -193,10 +156,9 @@ public void Initialize()
193156
{
194157
foreach (var xllPath in GetLoadedXllPaths())
195158
{
196-
XllRegistrationInfo regInfo;
197-
if (!_xllRegistrationInfos.TryGetValue(xllPath, out regInfo))
159+
if (!_xllRegistrationInfos.ContainsKey(xllPath))
198160
{
199-
regInfo = new XllRegistrationInfo(xllPath);
161+
XllRegistrationInfo regInfo = new XllRegistrationInfo(xllPath);
200162
_xllRegistrationInfos[xllPath] = regInfo;
201163
regInfo.Refresh();
202164
}
@@ -219,14 +181,53 @@ public void Refresh()
219181
}
220182

221183
// May be called from any thread
222-
public IEnumerable<IntelliSenseFunctionInfo> GetFunctionInfos()
184+
public IList<IntelliSenseFunctionInfo> GetFunctionInfos()
223185
{
224186
lock (_xllRegistrationInfos)
225187
{
226188
return _xllRegistrationInfos.Values.SelectMany(ri => ri.GetFunctionInfos()).ToList();
227189
}
228190
}
229191

192+
#endregion
193+
194+
// DANGER: Still subject to LoaderLock problem...
195+
// TODO: Consider Load/Unload done when AddIns is enumerated...
196+
void loaderNotification_LoadNotification(object sender, LoaderNotification.NotificationEventArgs e)
197+
{
198+
Debug.Print($"@>@>@>@> LoadNotification: {e.Reason} - {e.FullDllName}");
199+
if (e.FullDllName.EndsWith(".xll", StringComparison.OrdinalIgnoreCase))
200+
_syncContextExcel.Post(ProcessLoadNotification, e);
201+
}
202+
203+
// Runs on the main thread, in a macro context
204+
void ProcessLoadNotification(object state)
205+
{
206+
Debug.Assert(Thread.CurrentThread.ManagedThreadId == 1);
207+
// we might want to introduce a delay here, so that the .xll can complete loading...
208+
var notification = (LoaderNotification.NotificationEventArgs)state;;
209+
var xllPath = notification.FullDllName;
210+
lock (_xllRegistrationInfos)
211+
{
212+
XllRegistrationInfo regInfo;
213+
if (!_xllRegistrationInfos.TryGetValue(xllPath, out regInfo))
214+
{
215+
if (notification.Reason == LoaderNotification.Reason.Loaded)
216+
{
217+
regInfo = new XllRegistrationInfo(xllPath);
218+
_xllRegistrationInfos[xllPath] = regInfo;
219+
//regInfo.Refresh(); // Rather not.... so that we don't even try during the AddIns enumeration... OnInvalidate will lead to Refresh()
220+
OnInvalidate();
221+
}
222+
}
223+
else if (notification.Reason == LoaderNotification.Reason.Unloaded)
224+
{
225+
_xllRegistrationInfos.Remove(xllPath);
226+
// OnInvalidate();
227+
}
228+
}
229+
}
230+
230231
// Called in macro context
231232
// Might be implemented by COM AddIns check, or checking loaded Modules with Win32
232233
// Application.AddIns2 also lists add-ins interactively loaded (Excel 2010+) and has IsOpen property.
@@ -278,7 +279,7 @@ public WorkbookRegistrationInfo(string name)
278279
{
279280
_name = name;
280281
}
281-
282+
282283
// Called in a macro context
283284
public void Refresh()
284285
{
@@ -349,38 +350,97 @@ public IEnumerable<IntelliSenseFunctionInfo> GetFunctionInfos()
349350
Dictionary<string, WorkbookRegistrationInfo> _workbookRegistrationInfos = new Dictionary<string, WorkbookRegistrationInfo>();
350351

351352
public event EventHandler Invalidate;
352-
353+
354+
#region IIntelliSenseProvider implementation
355+
356+
public WorkbookIntelliSenseProvider()
357+
{
358+
}
359+
353360
public void Initialize()
354361
{
355362
Logger.Provider.Info("WorkbookIntelliSenseProvider.Initialize");
356363

364+
// The events are just to keep track of the set of open workbooks,
365+
var xlApp = (Application)ExcelDnaUtil.Application;
366+
xlApp.WorkbookOpen += Excel_WorkbookOpen;
367+
xlApp.WorkbookBeforeClose += Excel_WorkbookBeforeClose;
368+
//xlApp.WorkbookAddinInstall += Excel_WorkbookAddinInstall;
369+
//xlApp.WorkbookAddinUninstall += Excel_WorkbookAddinUninstall;
370+
357371
//var app = ExcelDnaUtil.Application;
358372
// app.WorkbookLoaded...
359-
foreach (var name in GetLoadedWorkbookNames())
373+
lock (_workbookRegistrationInfos)
360374
{
361-
WorkbookRegistrationInfo regInfo;
362-
if (!_workbookRegistrationInfos.TryGetValue(name, out regInfo))
375+
foreach (var name in GetLoadedWorkbookNames())
363376
{
364-
regInfo = new WorkbookRegistrationInfo(name);
365-
_workbookRegistrationInfos[name] = regInfo;
377+
if (!_workbookRegistrationInfos.ContainsKey(name))
378+
{
379+
WorkbookRegistrationInfo regInfo = new WorkbookRegistrationInfo(name);
380+
_workbookRegistrationInfos[name] = regInfo;
381+
regInfo.Refresh();
382+
}
366383
}
367-
regInfo.Refresh();
368384
}
369385
}
370386

387+
// Runs on the main thread
371388
public void Refresh()
372389
{
373390
Logger.Provider.Info("WorkbookIntelliSenseProvider.Refresh");
391+
lock (_workbookRegistrationInfos)
392+
{
393+
foreach (var regInfo in _workbookRegistrationInfos.Values)
394+
{
395+
regInfo.Refresh();
396+
}
397+
}
374398
}
375399

376-
public IEnumerable<IntelliSenseFunctionInfo> GetFunctionInfos()
400+
// May be called from any thread
401+
public IList<IntelliSenseFunctionInfo> GetFunctionInfos()
377402
{
378-
return _workbookRegistrationInfos.Values.SelectMany(ri => ri.GetFunctionInfos());
403+
lock (_workbookRegistrationInfos)
404+
{
405+
return _workbookRegistrationInfos.Values.SelectMany(ri => ri.GetFunctionInfos()).ToList();
406+
}
379407
}
380408

409+
#endregion
410+
411+
void Excel_WorkbookOpen(Workbook Wb)
412+
{
413+
var regInfo = new WorkbookRegistrationInfo(Wb.Name);
414+
lock (_workbookRegistrationInfos)
415+
{
416+
_workbookRegistrationInfos[Wb.Name] = regInfo;
417+
OnInvalidate();
418+
}
419+
}
420+
421+
void Excel_WorkbookBeforeClose(Workbook Wb, ref bool Cancel)
422+
{
423+
// Do we have to worry about renaming / Save As?
424+
lock (_workbookRegistrationInfos)
425+
{
426+
_workbookRegistrationInfos.Remove(Wb.Name);
427+
}
428+
}
429+
430+
//private void Excel_WorkbookAddinInstall(Workbook Wb)
431+
//{
432+
// throw new NotImplementedException();
433+
//}
434+
435+
//private void Excel_WorkbookAddinUninstall(Workbook Wb)
436+
//{
437+
// throw new NotImplementedException();
438+
//}
439+
381440
// Called in macro context
382441
// Might be implemented by tracking Application events
383442
// Remember this changes when a workbook is saved, and can refer to the wrong workbook as they are closed / opened
443+
// CONSIDER: Check AddIns2 ?
384444
IEnumerable<string> GetLoadedWorkbookNames()
385445
{
386446
// TODO: Implement properly...
@@ -391,6 +451,11 @@ IEnumerable<string> GetLoadedWorkbookNames()
391451
}
392452
}
393453

454+
void OnInvalidate()
455+
{
456+
Invalidate?.Invoke(this, EventArgs.Empty);
457+
}
458+
394459
public void Dispose()
395460
{
396461
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
33
<package id="ExcelDna.Integration" version="0.33.9" targetFramework="net40" />
4+
<package id="ExcelDna.Interop" version="14.0.1" targetFramework="net40" />
45
<package id="Excel-DNA.Lib" version="0.33.9" targetFramework="net40" />
56
<package id="UIAComWrapper" version="1.1.0.14" targetFramework="net40" />
67
</packages>

0 commit comments

Comments
 (0)