0

I need to create a new AppDomain for my solution so that I can invoke the method from it's own AppDomain. All of the dependent .dll files for my solution have been embedded into my.exe using Fody/Costura. I have created a Loader : MarshalByRefObject class to invoke my method but when I execute the loader at runtime I get an exception of:

Could not load file or assembly 'my.Assembly, Version=19.1.7242.23931, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

The code is looking for the .dll files in my AppDomain.CurrentDomain.BaseDirectory to execute my method via the loader but they are not there but are embedded in the executable. Is there a way for my new AppDomain to know about the assemblies that have been embedded into my executable without having to load them from the BaseDirectory?

I have looked for ways to load the other assemblies into my new AppDomain but haven't found a way to do it. I also suspect there might be Fody/Costura API to do it but haven't found anything that worked for this scenario.

        internal class Loader : MarshalByRefObject
        {
            object CallInternal(string dll, string typename, string method, object[] parameters)
            {

                Assembly a = Assembly.LoadFile(dll);
                Type t = a.GetType(typename);
                MethodInfo m = t.GetMethod(method);
                Console.WriteLine("Invoking " + m.Name);
                return m.Invoke(null, parameters);
            }

            public static object Call(string dll, string typename, string method, params object[] parameters)
            {
                object result = null;
                try
                {
                    AppDomain newAppDomain = AppDomain.CreateDomain("myNewDomain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
                    Loader ld = (Loader)newAppDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(Loader).FullName);  //DLL file not found exception thrown from here
                    result = (Loader)ld.CallInternal(dll, typename, method, parameters);
                    AppDomain.Unload(newAppDomain);
                }
                catch (Exception ee)
                {
                    Console.WriteLine("Exception Message [" + ee.Message + "]");
                    PDFUtil.Report.ErrSilent(ee);
                }
                return result;
            }
        }

I am looking to have my Loader class be instantiated without a need to load the .dlls from disk during runtime. Is this possible with Fody/Costura embedded .dlls?

jknight001
  • 21
  • 3
  • Does the dll you are loading use other dlls? Are those dlls in Base Directory? A Net executable looks for other dll in the same folder as the executable (not base folder). To make sure you are loading the latest version of you executables always add other dll by use menu : Add Existing Item and then browse to where executable is compiled. Then use Solution Explorer open the Reference folder and right click on the existing item and check box to copy to executable folder. – jdweng Oct 30 '19 at 22:20
  • Yes, the class that contains the method I am trying to invoke requires 3 .dll files. These 3 assemblies have been loaded into the current AppDomain but are not available in my new AppDomain. So when I use CreateInstanceAndUnwrap() with my new AppDomain it throws the exception of FileNotFound. I guess since it didn't find the assembly in my new AppDomain, it looks for it in the base folder. CreateInstanceAndUnwrap() . – jknight001 Oct 31 '19 at 16:55
  • If I put those .dlls into the base folder, the code works because it is looking for the .dlls there but it defeats the purpose of embedded .dlls because I will deliver a larger executable that includes these .dlls (in the current AppDomain) but will also have to install the three .dlls separately into the base directory so that the .exe can find them at runtime. I would rather find a way to add the assemblies to my new AppDomain from the original AppDomain at runtime without having to load them from the disk so I can deliver just one .exe file. Is there a way to do that with Fody/Costura?. – jknight001 Oct 31 '19 at 16:56
  • Not sure. Do you want to download every time you start the application? What you are asking seems to be a conflict "add the assemblies to my new AppDomain from the original AppDomain at runtime" and "I can deliver just one .exe file". – jdweng Oct 31 '19 at 17:08
  • With in the .exe there is the current AppDomain. I created a second AppDomain in the same .exe. Because it is a new AppDomain, it does not have any of the assemblies in it that the original AppDomains have. In order to use Reflection to Invoke my method, I need some of the dependent assemblies in the new AppDomain. Trying to figure out how to load them without having to load the .dll from the hard drive. All of this is happening in the same.exe. – jknight001 Oct 31 '19 at 19:21

1 Answers1

2

I added my .dlls to my Visual Studio Project and set Build-Action to be Embedded Resource

I am running this bit of code to get the assembly into my new AppDomain:

string resName = null;
foreach (string resourceName in Assembly.GetExecutingAssembly().GetManifestResourceNames())
{
   Console.WriteLine("Assembly.GetExecutingAssembly().GetManifestResourceNames() [" + resourceName + "] ");
   if (resourceName.Contains("my.Assembly"))
   {
      resName = resourceName;
      Console.WriteLine("CurrentDomain Assembly my.Assembly [" +  "] Resource name [" + resName + "]");
    }

}
Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream(resName);
if (s != null)
{
    byte[] data = new BinaryReader(s).ReadBytes((int)s.Length);
    Assembly a = Assembly.Load(data);
    newAppDomain.Load(data);

     foreach (Assembly cdAssem in newAppDomain.GetAssemblies())
     {
        Console.WriteLine("newAppDomain Assembly 2 [" + cdAssem.GetName().FullName + "]");
      }
}

I can see that the assembly is part of my new AppDomain:

newAppDomain Assembly 2 [mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]

newAppDomain Assembly 2 my.Assembly Version=19.1.7243.19014, Culture=neutral, PublicKeyToken=null]

But using CreateInstanceAndUnwrap() still fails saying it can't find my.Assembly.dll. I am wondering if the only way one can load an assembly into the AppDomain is by loading it from the Base Directory?

Community
  • 1
  • 1
jknight001
  • 21
  • 3
  • When adding another project to current project there are to ways of doing it. 1) Copy all the executable from 2nd project or reference the path of the 2nd project in the first project. Often you use relative paths like ../../secondProj/bin/debug/abc.exe. – jdweng Nov 01 '19 at 07:21
  • Can you please explain your answer like where did you put this code . Is it in `static void main()` . Did you add dlls under `Resources` in VS. Basically , I need to know how do I utilize your answer with the code that you posted in the question. I have the same issue where I used Costura to build exe , but when I run it , gives me error `Cannot resolve dependency to assembly 'Ranor exWrapper, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it has not been preloaded.` I have looked around but couldnt find any solution that pertains to Fody/Costura . Appreciate any help. – user1207289 Feb 07 '20 at 04:41