Search code examples
c#.netreflection.net-assemblyappdomain

Dynamically Loaded Assembly - Settings & Communication


Ok so... I have a WPF application (let's call it Launcher.exe) which loads and executes another WPF application (let's call it Loaded.exe) dynamically using something like this:

Byte[] assemblyData;

using (BinaryReader reader = new BinaryReader(new FileStream(filePath, FileMode.Open)))
    assemblyData = reader.ReadBytes(Convert.ToInt32(fs.Length));

Assembly assembly = Assembly.Load(assemblyData);
MethodInfo method = assembly.EntryPoint;

if (method != null)
{
    Object instance = assembly.CreateInstance(method.Name);
    method.Invoke(o, null);
}

Now... the problem is that Launched.exe has its own settings in the file Loaded.exe.config, and it also uses them in bindings. For example:

Topmost="{Binding Mode=TwoWay, Path=Topmost, Source={x:Static p:Settings.Default}}"

First question is, how can I make my dynamically loaded assembly properly load/bind/update and, more generally, use its own settings? I don't think it can handle this automagically...

Second question is: can Loaded.exe communicate with Launcher.exe? Let's say Loaded.exe needs some data that only Launcher.exe can retrieve... how can it ask for it? I think I need something like a proxy between the two assemblies, but I can't even figure out how to start coding this...


Solution

  • I supose you'll need to load a separate assembly with it's own .config file, no? One way i do that is to load the assembly in a new AppDomain. You'll could deploy that assembly in a separate folder with all his needed references.

    First setup the AppDomain, here you have a method:

    AppDomain getAppDomainForAssembly(string assemblypath, string appdomainname) 
        {
            //this._assembly_file = AssemblyFile;
    
            string _assembly_file_name = System.IO.Path.GetFileName(assemblypath);
            string _rootpath = System.IO.Path.GetDirectoryName(assemblypath);
    
            //this._assembly_class_name = AssemblyClassNameToInstance;
            AppDomainSetup _app_domain_info = new AppDomainSetup();
            _app_domain_info.ApplicationBase = _rootpath;
            _app_domain_info.PrivateBinPath = _rootpath;
            _app_domain_info.PrivateBinPathProbe = _rootpath;
            _app_domain_info.ConfigurationFile = _rootpath + @"\app.config"; //Here put the path to the correct .assembly .config file
            AppDomain _app_domain = AppDomain.CreateDomain(
                appdomainname, null, _app_domain_info);
    
            return _app_domain;
        }
    

    Then get an instance of the object that executes the method on the assembly:

    protected System.Reflection.Assembly _asm_resolve(string assemblyFile)
        {
            return System.Reflection.Assembly.LoadFrom(assemblyFile);
        }
    
    object getInstanceFromAppDomain(ref AppDomain appDomain, 
      string assemblyPath, string className = null) 
        {
            if (string.IsNullOrEmpty(className))
            {
                System.Reflection.Assembly assembly = _asm_resolve(assemblyPath);
                System.Reflection.MethodInfo method = assembly.EntryPoint;
    
                return appDomain.CreateInstanceFromAndUnwrap(assemblyPath, method.Name);
            }
            else 
            {
    
                return appDomain.CreateInstanceFromAndUnwrap(assemblyPath, className);
    
            }
        }
    

    Even if we know the Object Type, we could create a method with generic type:

    T getInstanceFromAppDomain<T>(ref AppDomain appDomain, 
     string assemblyPath, string className = null) 
        {
            if (string.IsNullOrEmpty(className))
            {
                System.Reflection.Assembly assembly = _asm_resolve(assemblyPath);
                System.Reflection.MethodInfo method = assembly.EntryPoint;
    
                return (T)appDomain.CreateInstanceFromAndUnwrap(assemblyPath, method.Name);
            }
            else 
            {
    
                return (T)appDomain.CreateInstanceFromAndUnwrap(assemblyPath, className);
    
            }
        }
    

    And then, invoke the method on the created instance, wich is executing in the new appDomain:

    void executeMethod(Type objecttype, string methodname, ref object instancedObject, object[] methodparams) 
        {
            objecttype.InvokeMember(
                methodname, System.Reflection.BindingFlags.InvokeMethod, null, instancedObject, methodparams);
        }
    

    You could use like this:

    AppDomain newappdomain = getAppDomainForAssembly(filePath, "Loaded.exe.domain");
            object loadedexe_object = getInstanceFromAppDomain(ref newappdomain,
                filePath);
    
            //If you know the method name to call...
            executeMethod(loadedexe_object.GetType(), "methodname", ref loadedexe_object, null);
    
            //or get entry point...
            executeMethod(loadedexe_object.GetType(),
                _asm_resolve(filePath).EntryPoint.Name, ref loadedexe_object, null);
    

    For the second question, you could use NamedPipes, Remoting, WCF... You need to implement interprocess communication on the same machine. Take a look at MSDN documentaion, code sample covering this scenario with WCF http://msdn.microsoft.com/en-us/library/system.servicemodel.netnamedpipebinding.aspx

    See this sample on CodeProject, using Remoting Inter-process communication via Remoting