66using System . Text . RegularExpressions ;
77using Microsoft . Win32 ;
88using ExcelDna . Integration ;
9+ using System . Threading ;
910
1011namespace ExcelDna . IntelliSense
1112{
1213 // This class implements the registration and activation of the calling add-in as an IntelliSense Server.
1314 //
1415 // Among different add-ins that are loaded into an Excel process, at most one IntelliSenseServer can be Active.
15- // This should always be the IntelliSenseServer with the greatest version number among those registered.
16+ // This should always be an IntelliSenseServer with the greatest version number among those registered.
17+ //
1618 // At the moment the bookkeeping for registration and activation in the process is done with environment variables.
1719 // (An attractive alternative is the hidden Excel name space: http://www.cpearson.com/excel/hidden.htm )
18- // This prevents cross-AppDomain calls, which are problematic because assemblies are then loaded into multiple AppDomains, and
19- // since the mechanism is intended to cater for different assembly versions, this would be a problem. Also, we don't control
20- // the CLR hosting configuration, so can't always set the MultiDomain flag on setup. COM mechanisms could work, but are complicated.
20+ // This allows us not to make cross-AppDomain calls, which are problematic because assemblies are then loaded into multiple AppDomains, and
21+ // since the mechanism is intended to cater for different assembly versions, this would be a problem. Also, as we don't control
22+ // the CLR hosting configuration, we can't always set the MultiDomain flag on setup. COM mechanisms could work, but are complicated.
2123 // Another approach would be to use a hidden Excel function that the Active server provides, and have all server register with the active server.
2224 // When a new server should become active, it then tells the active server, and somehow gets all the other registrations...
2325
@@ -28,7 +30,7 @@ namespace ExcelDna.IntelliSense
2830 // REMEMBER: COM events are not necessarily safe macro contexts.
2931 public static class IntelliSenseServer
3032 {
31- const string ServerVersion = "1.0.9 " ; // TODO: Define and manage this somewhere else
33+ const string ServerVersion = "1.0.10 " ; // TODO: Define and manage this somewhere else
3234
3335 // NOTE: Do not change these constants in custom versions.
3436 // They are part of the co-operative safety mechanism allowing different add-ins providing IntelliSense to work together safely.
@@ -55,12 +57,19 @@ public static class IntelliSenseServer
5557 static bool _isActive = false ;
5658 static IntelliSenseHelper _helper = null ;
5759
58- // Called directly from AutoOpen.
60+ // The name change from Register to Install is just to force attention on this message for anyone upgrading
61+ // - omitting the Uninstall call causes an Excel crash when the add-in is unloaded for any reason
62+ [ Obsolete ( "IntelliSenseServer now requires matching calls to Install (inside AutoOpen) and Uninstall (inside AutoClose)" , true ) ]
5963 public static void Register ( )
64+ {
65+ }
66+
67+ // Called directly from AutoOpen()
68+ public static void Install ( )
6069 {
6170 TraceLogger . Initialize ( ) ;
6271
63- Logger . Initialization . Info ( $ "IntelliSenseServer.Register Begin: Version { ServerVersion } in { AppDomain . CurrentDomain . FriendlyName } ") ;
72+ Logger . Initialization . Info ( $ "IntelliSenseServer.Install Begin: Version { ServerVersion } in { AppDomain . CurrentDomain . FriendlyName } ") ;
6473 if ( IsDisabled ( ) )
6574 return ;
6675
@@ -93,7 +102,30 @@ public static void Register()
93102
94103 AppDomain . CurrentDomain . DomainUnload += CurrentDomain_DomainUnload ;
95104 AppDomain . CurrentDomain . ProcessExit += CurrentDomain_ProcessExit ;
96- Logger . Initialization . Info ( "IntelliSenseServer.Register End" ) ;
105+ Logger . Initialization . Info ( "IntelliSenseServer.Install End" ) ;
106+ }
107+
108+ // Should be called in a macro context, e.g. from AutoClose()
109+ // Was added again in 1.0.10 to clean up WinEvents
110+ // Maybe we won't need it if we can move to our own thread handling WinEvents
111+ public static void Uninstall ( )
112+ {
113+ Logger . Initialization . Info ( $ "IntelliSenseServer.Uninstall Begin: Version { ServerVersion } in { AppDomain . CurrentDomain . FriendlyName } ") ;
114+
115+ UnpublishRegistration ( ) ;
116+ if ( _isActive )
117+ {
118+ Deactivate ( ) ;
119+
120+ // See if there is another server to active
121+ var highestRegistration = GetHighestPublishedRegistration ( ) ;
122+ if ( highestRegistration != null )
123+ {
124+ ActivateServer ( highestRegistration ) ;
125+ }
126+ }
127+
128+ Logger . Initialization . Info ( $ "IntelliSenseServer.Uninstall End") ;
97129 }
98130
99131 // Invokes a Refresh on the Active server (if there is on)
@@ -115,9 +147,8 @@ public static void Refresh()
115147 Logger . Initialization . Info ( $ "IntelliSenseServer.Refresh End") ;
116148 }
117149
118- private static void CurrentDomain_ProcessExit ( object sender , EventArgs e )
150+ static void CurrentDomain_ProcessExit ( object sender , EventArgs e )
119151 {
120-
121152 // CONSIDER: We get this quite late in the shutdown
122153 // We should try to find a way to identify Excel shutdown a lot earlier
123154 Logger . Initialization . Verbose ( "IntelliSenseServer ProcessExit Begin" ) ;
@@ -129,10 +160,8 @@ private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
129160 // Don't try to clean up clean up on process exit - all our resources are in-process anyway
130161 // Leads to an error with the main thread SynchronizationContext, which might be shut down already.
131162 // In particular we don't have to call UnhookWinEvent: "If the client's thread ends, the system automatically calls this function."
132- // Deactivate();
133163 }
134164 Logger . Initialization . Verbose ( "IntelliSenseServer ProcessExit End" ) ;
135-
136165 }
137166
138167 // DomainUnload runs when AutoClose() would run on the add-in.
@@ -142,20 +171,12 @@ private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
142171 static void CurrentDomain_DomainUnload ( object sender , EventArgs e )
143172 {
144173 Logger . Initialization . Verbose ( "IntelliSenseServer DomainUnload Begin" ) ;
145- //// Early shutdown notification
146- //XlCall.ShutdownStarted();
147174
148- UnpublishRegistration ( ) ;
149175 if ( _isActive )
150176 {
151- Deactivate ( ) ;
152-
153- var highestRegistration = GetHighestPublishedRegistration ( ) ;
154- if ( highestRegistration != null )
155- {
156- ActivateServer ( highestRegistration ) ;
157- }
177+ Logger . Initialization . Error ( "IntelliSenseServer DomainUnload while active - Add and IntelliSenseServer.Unregister() call in AutoClose() !" ) ;
158178 }
179+
159180 Logger . Initialization . Verbose ( "IntelliSenseServer DomainUnload End" ) ;
160181 }
161182
@@ -167,7 +188,6 @@ internal static bool Activate()
167188 SetActiveRegistrationInfo ( ) ;
168189 _isActive = true ;
169190
170- // Now initialize (TODO: perhaps lazily...?)
171191 _helper = new IntelliSenseHelper ( ) ;
172192 // TODO: Perhaps also register macro to trigger updates
173193 return true ;
@@ -179,9 +199,15 @@ internal static bool Activate()
179199 }
180200 }
181201
182- // Called internally from the AppDomain_DomainUnload or AppDomain_ProcessExit event handler, and via the control function from another server when that server figures out that it must become the active server.
202+ // Should be called only on the Excel main thread, either from Unregister (AutoClose)
203+ // or via the control function from another server when that server figures out that it must become the active server.
183204 internal static bool Deactivate ( )
184205 {
206+ if ( Thread . CurrentThread . ManagedThreadId != 1 )
207+ {
208+ // At least try to log
209+ Logger . Initialization . Error ( $ "IntelliSenseServer.Deactivate not called on the main Excel thread!") ;
210+ }
185211 try
186212 {
187213 if ( _helper != null )
@@ -193,7 +219,6 @@ internal static bool Deactivate()
193219 }
194220 catch ( Exception ex )
195221 {
196- // TODO: Log
197222 Logger . Initialization . Error ( $ "IntelliSenseServer.Deactivate error: { ex } ") ;
198223 return false ;
199224 }
@@ -509,7 +534,6 @@ static object IntelliSenseServerControl(object control)
509534 }
510535 return false ;
511536 }
512-
513537 #endregion
514538 }
515539}
0 commit comments