99
1010namespace ExcelDna . IntelliSense
1111{
12- // This class implements the registration and activation of this add-in as an IntelliSense Server.
12+ // This class implements the registration and activation of the calling add-in as an IntelliSense Server.
1313 //
1414 // Among different add-ins that are loaded into an Excel process, at most one IntelliSenseServer can be Active.
1515 // This should always be the IntelliSenseServer with the greatest version number among those registered.
@@ -25,29 +25,10 @@ namespace ExcelDna.IntelliSense
2525 // This is against a unique GUID-based name for every registered server, so that the Activate call can be made on an inactive server.
2626 // (To be called in a macro context only, e.g. from AutoOpen.)
2727
28- // The Active Server also registers a macro with Excel under a well-know name (not the server-specific GUID),
29- // which passes through to a specific provider.
30- // This is so that an add-in can tell (some provider in) the active server to update
31- // E.g. Application.Run("ExcelDna.IntelliSense.Refresh", ' NOT WITH: "VBA") , ActiveWorkbook.Name)
32- // Application.Run("ExcelDna.IntelliSense.Refresh", ' NOT WITH: "XLL") , ExcelDnaUtil.XllPath)
33- // Application.Run("ExcelDna.IntelliSense.Refresh", ' NOT WITH: "XML") , @"C:\Temp\MyInfo.xml")
34- // XlCall.Excel(XlCall.xlcRun, "ExcelDna.IntelliSense.Refresh")
35- // NB: This can't be the only way a provider knows what to load, because we don't want to do a hand-over
36- // when a new Server becomes Active (there might not have been a server loaded at the start).
37- // So we want the provider to always be able to scan to get the info.
38- // The Refresh call just allows a re-scan in a macro context (and of course would fail if no Server is Active)
39-
40- // Now:
41- // When a Server becomes active it lets all the providers scan.
42- // When a new add-in is then loaded, it calls the ExcelDna.IntelliSense.Refresh macro to force a re-scan.
43- // When an add-in dynamically registers some more UDFs, it can call Update to force a rescan.
44- // So there is no way to say "listen to this .xml file".
45- // So the add-in should provide a discoverable way that a provider can call it to say "what files must I listen to?".
46-
4728 // REMEMBER: COM events are not necessarily safe macro contexts.
4829 public static class IntelliSenseServer
4930 {
50- const string ServerVersion = "0.0.10 " ; // TODO: Define and manage this somewhere else
31+ const string ServerVersion = "0.0.11 " ; // TODO: Define and manage this somewhere else
5132
5233 // NOTE: Do not change these constants in custom versions.
5334 // They are part of the co-operative safety mechanism allowing different add-ins providing IntelliSense to work together safely.
@@ -82,7 +63,7 @@ public static void Register()
8263 if ( IsDisabled ( ) )
8364 return ;
8465
85- RegisterControlFunction ( ) ;
66+ RegisterControlMacro ( ) ;
8667 PublishRegistration ( ) ;
8768
8869 bool shouldActivate = false ;
@@ -102,11 +83,11 @@ public static void Register()
10283 Logger . Initialization . Info ( $ "IntelliSenseServer not being activated now. Active Version: { activeInfo . Version } ") ;
10384 }
10485 // Else we're not activating - there is an active server and it is the same version or newer
105- // TODO: Tell it to load our UDFs somehow - maybe call a hidden macro?
10686
107- if ( shouldActivate )
87+ if ( shouldActivate &&
88+ ( activeInfo == null || DeactivateServer ( activeInfo ) ) )
10889 {
109- var activated = Activate ( ) ;
90+ Activate ( ) ;
11091 }
11192
11293 AppDomain . CurrentDomain . DomainUnload += CurrentDomain_DomainUnload ;
@@ -219,12 +200,12 @@ static bool ActivateServer(RegistrationInfo registrationInfo)
219200 // Suppress errors if things go wrong, including unexpected return types.
220201 try
221202 {
222- var result = ExcelDna . Integration . XlCall . Excel ( ExcelDna . Integration . XlCall . xlfCall , registrationInfo . GetControlMacroName ( ) , ControlMessageDeactivate ) ;
203+ var result = ExcelDna . Integration . XlCall . Excel ( ExcelDna . Integration . XlCall . xlUDF , registrationInfo . GetControlMacroName ( ) , ControlMessageDeactivate ) ;
223204 return ( bool ) result ;
224205 }
225- catch ( Exception /*ex*/ )
206+ catch ( Exception ex )
226207 {
227- // TODO: Log
208+ Logger . Initialization . Error ( ex , $ "IntelliSenseServer { registrationInfo . ToRegistrationString ( ) } could not be activated." ) ;
228209 return false ;
229210 }
230211 }
@@ -236,12 +217,17 @@ static bool DeactivateServer(RegistrationInfo registrationInfo)
236217 // Suppress errors if things go wrong, including unexpected return types.
237218 try
238219 {
239- var result = ExcelDna . Integration . XlCall . Excel ( ExcelDna . Integration . XlCall . xlfCall , registrationInfo . GetControlMacroName ( ) , ControlMessageDeactivate ) ;
220+ var result = ExcelDna . Integration . XlCall . Excel ( ExcelDna . Integration . XlCall . xlUDF , registrationInfo . GetControlMacroName ( ) , ControlMessageDeactivate ) ;
221+ if ( result is ExcelError )
222+ {
223+ Logger . Initialization . Error ( $ "IntelliSenseServer { registrationInfo . ToRegistrationString ( ) } could not be deactivated.") ;
224+ return false ;
225+ }
240226 return ( bool ) result ;
241227 }
242- catch ( Exception /*ex*/ )
228+ catch ( Exception ex )
243229 {
244- // TODO: Log
230+ Logger . Initialization . Error ( ex , $ "IntelliSenseServer Deactivate call for { registrationInfo . ToRegistrationString ( ) } failed." ) ;
245231 return false ;
246232 }
247233 }
@@ -285,9 +271,14 @@ public int CompareTo(RegistrationInfo other)
285271 return CompareVersions ( Version , other . Version ) ;
286272 }
287273
274+ public static string GetControlMacroName ( Guid serverId )
275+ {
276+ return "IntelliSenseServerControl_" + serverId . ToString ( "N" ) ;
277+ }
278+
288279 public string GetControlMacroName ( )
289280 {
290- return "IntelliSenseControl_" + ServerId . ToString ( "N" ) ;
281+ return GetControlMacroName ( ServerId ) ;
291282 }
292283
293284 // 1.2.0 is equal to 1.2
@@ -389,13 +380,16 @@ static int[] ParseVersion(string versionString)
389380 return version ;
390381 }
391382
392- // Version patterns are ","-joined lists of (dotted integer strings, with a possible trailing .* wildcard).
383+ // Version patterns are either a single * (universal match) or a ","-joined lists of (dotted integer strings, with a possible trailing .* wildcard).
393384 // e.g. 1.2.*, which would be matched with regex 1\.2(\.\d+)*
394385 static bool IsVersionMatch ( string version , string versionPattern )
395386 {
396387 if ( string . IsNullOrEmpty ( versionPattern ) )
397388 return false ;
398389
390+ if ( versionPattern == "*" )
391+ return true ; // Universal pattern - matches all versions
392+
399393 var regexParts = new List < string > ( ) ;
400394 var parts = versionPattern . Split ( ',' ) ;
401395 foreach ( var part in parts )
@@ -427,12 +421,12 @@ static RegistrationInfo GetHighestPublishedRegistration()
427421
428422 #region IntelliSense control function registered with Excel
429423
430- static void RegisterControlFunction ( )
424+ static void RegisterControlMacro ( )
431425 {
432426 var method = typeof ( IntelliSenseServer ) . GetMethod ( nameof ( IntelliSenseServerControl ) , BindingFlags . Static | BindingFlags . Public ) ;
433- var name = "IntelliSenseServerControl_" + _serverId . ToString ( "N" ) ;
427+ var name = RegistrationInfo . GetControlMacroName ( _serverId ) ;
434428 ExcelIntegration . RegisterMethods ( new List < MethodInfo > { method } ,
435- new List < object > { new ExcelCommandAttribute { Name = name } } ,
429+ new List < object > { new ExcelCommandAttribute { Name = name } } , // Macros in .xlls are always hidden
436430 new List < List < object > > { new List < object > { null } } ) ;
437431 // No Unregistration - that will happen automatically (and is only needed) when we are unloaded.
438432 }
0 commit comments