Search code examples
c#wpfadd-inuielement

Unable to convert 'MS.Internal.Controls.AddInHost' back to type 'System.Windows.Controls.UserControl' or cast it to my custom interface


I have created usercontrol instance into separate appdomain and convert it to INativeHandleContract as per addin framework guideline, and I am able to pass reference of framework instance into host application domain. When I convert INativeHandleContract back to UIElement in the host application appdomain, I am unable to cast the UIElement to my custom interface type.

Can anyone please tell me whether it is possible to convert Framework element into userControl or cast it to my custom interface?

here's a part of the code:

public INativeHandleContract GetControl(string AssemblyName, string strFullNamespaceName)
    {
        var assembly = Assembly.LoadFrom(m_strAssemblyDirectory + AssemblyName);
        var type1 = typeof(IUPiAssemblyProcedure);
        var type2 = typeof(IUPiCardProcedure);
        var type3 = typeof(IUPiDatabaseFrame);
        var type4 = typeof(IUPiUserFrame);
        var t = from T in assembly.GetTypes() where type1.IsAssignableFrom(T) || type2.IsAssignableFrom(T) || type3.IsAssignableFrom(T) || type4.IsAssignableFrom(T) select T;
        if (t.Count<Type>() == 0) return null;
        var tName = t.First<Type>().FullName;
        return FrameworkElementAdapters.ViewToContractAdapter((FrameworkElement)assembly.CreateInstance(tName));
    }

public void LoadUserControl(INativeHandleContract contract)
    {
        var uControl = FrameworkElementAdapters.ContractToViewAdapter(contract);

        var t = uControl.GetType();
        t.GetMethod("methodName");
        ...
    }

My goal is to Invoke some methods defined in custom interface, implemented by one of the types in the assembly I've loaded.

Regards Tihomir Blagoev


Solution

  • I found the solution! As it's described here, "Because the host application gets an HwndHost, the host application cannot convert the object that is returned by ContractToViewAdapter to the type it is implemented as by the add-in (for example, a UserControl)."

    So if I want to reach the type instance in my sub domain, I have to do it via my proxy:

    public class Main
    {
        private AppDomain ad = null;
        private proxy remoteWorker = null;
    
        public INativeHandleContract LoadAssembly(string assemblyname, string fullInterfaceName)
        {
            if (ad != null)
            {
                AppDomain.Unload(ad);
            }
            var domSetup = new AppDomainSetup();
    
            domSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
            domSetup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;
            domSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
            domSetup.LoaderOptimization = LoaderOptimization.MultiDomainHost;
            var adevidence = AppDomain.CurrentDomain.Evidence;
    
            ad = AppDomain.CreateDomain(assemblyname, adevidence, domSetup);
    
            ad.AssemblyResolve += ad_AssemblyResolve;
            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
    
            remoteWorker = (proxy)ad.CreateInstanceAndUnwrap(typeof(proxy).Assembly.FullName, "INATO.UPINAIS.UCLibrary.proxy");
            return remoteWorker.GetControl(assemblyname, fullInterfaceName);
        }
    
        public void DoTheTrick()
        {
            var uControl = LoadAssembly("assemblyName", "interfaceName");
            panel.Content = FrameworkElementAdapters.ContractToViewAdapter(uControl);
            remoteWorker.RemoteInvoke("methodName", new object[] { });
        }
    }
    
    public class proxy : MarshalByRefObject
    {
        private object _currentInstance;
    
        public INativeHandleContract GetControl(string AssemblyName, string strFullNamespaceName)
        {
            var assembly = Assembly.LoadFrom(m_strAssemblyDirectory + AssemblyName);
            var type1 = typeof(IUPiAssemblyProcedure);
            var type2 = typeof(IUPiCardProcedure);
            var type3 = typeof(IUPiDatabaseFrame);
            var type4 = typeof(IUPiUserFrame);
            var t = from T in assembly.GetTypes() where type1.IsAssignableFrom(T) || type2.IsAssignableFrom(T) || type3.IsAssignableFrom(T) || type4.IsAssignableFrom(T) select T;
            if (t.Count<Type>() == 0) return null;
            var tName = t.First<Type>().FullName;
            _currentInstance = assembly.CreateInstance(tName);
            return FrameworkElementAdapters.ViewToContractAdapter((FrameworkElement)_currentInstance);
        }
    
        public void RemoteInvoke(string methodName, object[] parameters)
        {
            var type = _currentInstance.GetType();
            var mi = type.GetMethod(methodName);
            mi.Invoke(_currentInstance, parameters);
        }
    }