Appdomain issue for IDTExtensibility2 COM addin

Add-in Express™ Support Service
That's what is more important than anything else

Appdomain issue for IDTExtensibility2 COM addin
 
Ashim Mishra


Guest


Hi Team,

We have a client who uses IDTExensibility2 com add-in for their internal purpose. They also use our excel add-in. Lately, this client is complaining that our excel add-in (created using add-in express) is hijacking the app domain of their IDTExensibility2 com add-in. To debug the issue I have created a sample IDTExensibility2 add-in and indeed this add-in is getting loaded in our app domain.

I have sent the sample add-in to you through the mail, please have a look at it and let me know if there is something we can do to stop the domain hijacking.

Thanks
Posted 24 Jun, 2019 06:50:38 Top
Ashim Mishra


Guest


Sample code:

Connect.cs

 [Serializable]
	[GuidAttribute("7EA3E06A-8832-4BF4-B5B3-3967F2D1A3DE"), 
     ProgId("SharedCOMAddinUnloadAppDomain.Connect")]
    public class Connect : /*Object*/MarshalByRefObject, Extensibility.IDTExtensibility2
	{
        [DllImport("kernel32.dll")]
        private static extern void OutputDebugString(string lpOutputString);

        public static object /*Excel.Application*/ applicationObject;
        public static object addInInstance;

        public Connect()
        { }

        public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
        {
            var connectionMode = (ext_ConnectMode)((int)connectMode);
            AppendToLogFile("Connecting...");
            AppendToLogFile("Connection Mode is " + connectMode);
            if (custom != null && custom.Length > 0)
            {
                var hostStartMode = custom.GetValue(1);
                AppendToLogFile("HostStart Mode is " + hostStartMode);
            }
            applicationObject = (Excel.Application)application;
            AppendToLogFile("Got object..");
            addInInstance = addInInst;
            AppendToLogFile("Connect...");
            AppendToLogFile("OnConnection > AppDomain = " + AppDomain.CurrentDomain.FriendlyName);
            AppendToLogFile("             > 'Connect.addInInstance'     is NULL? " + (Connect.addInInstance == null).ToString());
            AppendToLogFile("             > 'Connect.applicationObject' is NULL? " + (Connect.applicationObject == null).ToString());

            OutputDebugString("OnConnection > AppDomain = " + AppDomain.CurrentDomain.FriendlyName);
            OutputDebugString("             > 'Connect.addInInstance'     is NULL? " + (Connect.addInInstance == null).ToString());
            OutputDebugString("             > 'Connect.applicationObject' is NULL? " + (Connect.applicationObject == null).ToString());

            #region AppDomain
            /*
            _domain = AppDomain.CreateDomain("SharedCOMAddinUnloadAppDomain2", createEvidence(), createSetupInfo());
          
            Type type = typeof(Proxy);
            var value = (Proxy)_domain.CreateInstanceAndUnwrap(
                                                              type.Assembly.FullName,
                                                              type.FullName
                                                              );
            var assembly = value.GetAssembly("SharedCOMAddinUnloadAppDomain2.dll");

            // object obj = _domain.CreateInstanceFromAndUnwrap(@"C:TestSharedCOMAddinUnloadAppDomain2.dll", type.FullName);
            
            
            _domain.UnhandledException += new UnhandledExceptionEventHandler(_domain_UnhandledException);
            //}
            */
            #endregion
        }

        private void AppendToLogFile(string Message)
        {
            using (StreamWriter w = File.AppendText("c:/temp/SharedAddIn.log"))
            {
                w.WriteLine(Message);
                w.WriteLine("--------------------------------------------------------------");
            }
        }

        public void OnDisconnection(Extensibility.ext_DisconnectMode disconnectMode, ref System.Array custom)
		{}

		public void OnAddInsUpdate(ref System.Array custom)
		{}

		public void OnStartupComplete(ref System.Array custom)
		{}

		public void OnBeginShutdown(ref System.Array custom)
		{}
    }
}


CustomUDFFunction.cs


namespace IUDFFctTwoNS
{
    [ComVisible(true)]
    [Guid("68B1D454-97EF-40B8-9169-34A691323252")]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    public interface IUDFFctTwo
    {
     string IqTwo(string input); //string IQ2();
    }
    //C:WindowsSystem32mscoree.dll

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None),
     Guid("E8FD5ADF-64D5-4E00-8095-A1D952276AE8")]
    public class clsUDF : IUDFFctTwo
    {
        public AppDomain LocalAppDomain = null;
        [DllImport("kernel32.dll")]
        static extern void OutputDebugString(string lpOutputString);

        string IUDFFctTwo.IqTwo(string input)
        {
            AppendToLogFile("clsUDF::IUDFFctTwo > AppDomain = " + AppDomain.CurrentDomain.FriendlyName);
            AppendToLogFile("             > 'Connect.addInInstance'     is NULL? " + (Connect.addInInstance == null).ToString());
            AppendToLogFile("             > 'Connect.applicationObject' is NULL? " + (Connect.applicationObject == null).ToString());

            OutputDebugString("clsUDF::IUDFFctTwo > AppDomain = " + AppDomain.CurrentDomain.FriendlyName);
            OutputDebugString("                   > 'Connect.addInInstance'     is NULL? " + (Connect.addInInstance == null).ToString());
            OutputDebugString("                   > 'Connect.applicationObject' is NULL? " + (Connect.applicationObject == null).ToString());

            return DateTime.Now.Ticks.ToString();
        }

        private void AppendToLogFile(string Message)
        {
            using (StreamWriter w = File.AppendText("c:/temp/SharedAddIn.log"))
            {
                w.WriteLine(Message);
                w.WriteLine("--------------------------------------------------------------");
            }
        }
    }
}
Posted 24 Jun, 2019 06:54:40 Top
Ashim Mishra


Guest


The logs i am getting are:

clsUDF::IUDFFctTwo > AppDomain = D:\Workspace\exceladdin\AddinExpressAddin\MyAddin\bin\Debug\
--------------------------------------------------------------
> 'Connect.addInInstance' is NULL? True
--------------------------------------------------------------
> 'Connect.applicationObject' is NULL? True
--------------------------------------------------------------
Posted 24 Jun, 2019 06:57:10 Top
Ashim Mishra


Guest


Team, any update on this.
Posted 25 Jun, 2019 00:33:47 Top
Andrei Smolin


Add-in Express team


Posts: 18821
Joined: 2006-05-11
Hello Ashim,

How the add-in above gets loaded? What is "our Excel add-in" mentioned: a COM add-in, an Excel XLL add-in or an Excel Automation add-in?

At https://www.add-in-express.com/creating-addins-blog/2010/03/24/addin-xll-rtd-one-assembly/, I wrote this:

None of the above can be done for the Excel Automation add-in, which is not covered in this sample project, in particular because Add-in Express always loads it into the default AppDomain. You can try to bypass this limitation and load the UDF into the AppDomain of your COM add-in by calling a method defined in the UDF, again via ExcelApp.Evaluate. But this should be done BEFORE Excel calls some of the methods of your Excel Automation add-in. Taking into account that the order of loading Excel extensions is unknown, it may turn out to be quite a dilemma.



Andrei Smolin
Add-in Express Team Leader
Posted 25 Jun, 2019 00:43:29 Top
Ashim Mishra


Guest


Hi Andrei

Our add-in is an Excel Automation based add-in. As far as IDTExtensibility2 add-in is concerned i am trying to load it using regasm.
C:WindowsMicrosoft.NETFramework4.0.30319RegAsm.exe IUDFFctTwoDLL.dll /codebase /tlb
.

Once registered, whenever we try to invoke the UDF, we are getting the logs mentioned in my above comments. From the looks of the logs, it seems this is getting loaded in the appdomain of our automation based add-in.
Posted 25 Jun, 2019 02:38:46 Top
Andrei Smolin


Add-in Express team


Posts: 18821
Joined: 2006-05-11
Ashim Mishra writes:
i am trying to load it using regasm


This is how you register it. But how it gets loaded? Does your Automation add-in does this?

If you turn your Automation add-in off and restart Excel, will the COM add-in load?


Andrei Smolin
Add-in Express Team Leader
Posted 25 Jun, 2019 03:26:00 Top
Ashim Mishra


Guest


Yes, if i turn my Automation based add-in off, that UDF still runs. The only difference is this time it loads in DefaultAppDomain.
see the logs:

--------------------------------------------------------------
clsUDF::IUDFFctTwo > AppDomain = DefaultDomain
--------------------------------------------------------------
> 'Connect.addInInstance' is NULL? True
--------------------------------------------------------------
> 'Connect.applicationObject' is NULL? True
--------------------------------------------------------------
clsUDF::IUDFFctTwo > AppDomain = DefaultDomain
--------------------------------------------------------------
> 'Connect.addInInstance' is NULL? True
--------------------------------------------------------------
> 'Connect.applicationObject' is NULL? True
--------------------------------------------------------------
Posted 28 Jun, 2019 07:45:12 Top
Andrei Smolin


Add-in Express team


Posts: 18821
Joined: 2006-05-11
Hello Ashim,

Ashim Mishra writes:
I have sent the sample add-in to you through the mail


We don't have such an email.

Let's use this terminology.

- An Add-in Express based COM add-in may only be created/registered/loaded if your project contains an Add-in Express module descending from ADXAddinModule.
- An Add-in Express based XLL add-in may only be created/registered/loaded if your project has an Add-in Express module descending from ADXXLLModule.
- An Add-in Express based Excel Automation add-in may only be created/registered/loaded if your project has an Add-in Express module descending from ADXExcelAddinModule.
- Also, in this topic only, you use the term "an IDTExtensibility2 add-in" to identify some COM add-in that is built using Add-in Express.

My understanding is this: you have an Excel Automation add-in, not a COM add-in supporting Excel. Further on, I refer to the Excel Automation add-in as "your Excel add-in".

There are two scenarios for your Excel add-in to get loaded.
1) the worksheet [being] loaded contains a formula referring to an UDF defined in your Excel add-in. In this case, Excel loads your add-in; the assembly gets loaded to the Default AppDomain.
2) the IDTExtensibility2 add-in calls a UDF defined in your add-in e.g. by using ExcelApp.Evaluate. If this occurs *before* Excel loads your Excel add-in; the add-in's assembly gets loaded to the AppDomain of the caller add-in.

I assume you have the second scenario. I suggest that you modify the IDTExtensibility2 add-in so that it invokes your Excel add-in after a delay. Say you can use the WindowActivate event to issue an ADXAddinModule.SendMessage() call sending a message (an integer > 1024); when the OnsendMessage event receives *this* message, call ExcelApp.Evaluate.

Does the above resembles your scenario in any degree?


Andrei Smolin
Add-in Express Team Leader
Posted 28 Jun, 2019 08:50:36 Top