locked
Error on GetTypes() of a dynamically loaded Assembly RRS feed

  • Question

  • I have an application located in C:\projects\findassembly\find.exe that will load an EXE assembly. 

    In C:\projects\findassembly\ I have
    find.exe
    t1.dll

    In C:\test\ I have
    test.exe
    t1.dll
    t2.dll
    t3.dll

    string assembly = "C:\test\test.exe";
    Assembly ab = Assembly.LoadFile( assembly );

    After loading the assembly, I look for all of the referenced assemblies which will find
    t1.dll
    t2.dll
    t3.dll


    if the assembly is not currently loaded in the AppDomain, I then try to load first from the name and then from the directory of the Initial Assembly.

    AssemblyName[] a = ab.GetReferencedAssemblies();
    for ( int i = 0; i < a.Length; i++ ) {

            Assembly b = FindCurrentDomain( n );

                  if ( b == null )
                    b = Assembly.LoadWithPartialName( n );


                  if ( b == null ) {
                    FileInfo fi = new FileInfo( ***.Location );
                    string dir = fi.DirectoryName;

                    string f = dir + "\\" + n;
                    if ( File.Exists( f + ".dll" ) )
                      f = f + ".dll";
                    else if ( File.Exists( f + ".exe" ) )
                      f = f + ".exe";

                    b = Assembly.LoadFrom( f ); //LoadFile
                  }
    }

        private Assembly FindCurrentDomain( string n ) {
          Assembly[] lass = AppDomain.CurrentDomain.GetAssemblies();
          for ( int i = 0; i < lass.Length; i++ )
            if ( lass[ i ].GetName().Name == n )
              return lass[ i ];
          return null;
        }

    After completion, I have located and Loaded the exe and all 3 dlls, 2 of which where loaded from file.



    I then will look in each loaded Assembly for a class that implements the abstract class xSetup

                  Type t = xFunction.GetType( ab, typeof( xSetup ) );
                  if ( t != null )
                    x = (xSetup)Activator.CreateInstance( t );


        public static Type GetType( Assembly a, Type ty ) {
          foreach ( Type t in a.GetTypes() )
            if ( !t.IsAbstract )
              if ( xFunction.CheckTypeFullName( t, ty ) )
                return t;
          return null;
        }
        public static bool CheckTypeFullName( Type r, Type x ) {
          if ( r == null || x == null )
            return false;
          if ( r.FullName == x.FullName )
            return true;

          return CheckTypeFullName( r.BaseType, x );
        }


    My error occurs within a.GetTypes().  Since the relative path of the loaded exe (test.exe) does not match the path for the execution (find.exe), it will not error on loading any Types that are references in t2.dll and t3.dll.

    The exact error is
    {"Could not load file or assembly 'T2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.":"T2, Version=1.0.0.0,, Culture=neutral, PublicKeyToken=null"}

    This error will repeat for every type that exists in either T2 or T3.


    I even added this line to load the found assemblys into the current AppDomain and it would still error.

             AppDomain.CurrentDomain.Load( b.GetName() );


    If I copy find.exe into C:\test and run it from there, everything works fine since the path of the exe running matches the path of the loaded exe and dlls.


    I have also tried this

              FileInfo fi = new FileInfo( assembly );
              //Create the application domain setup information.
              AppDomainSetup domaininfo = new AppDomainSetup();
              domaininfo.ApplicationBase = fi.DirectoryName;

              System.Security.Policy.Evidence adevidence = AppDomain.CurrentDomain.Evidence;

              AppDomain domain = AppDomain.CreateDomain( "Rebuild", adevidence, domaininfo );

              domain.ExecuteAssembly( assembly, domain.Evidence );
              AppDomain.Unload( domain );

    This works but not the way I want it too.. It will execute the object which will then execute the xSetup class.  I know I can can also just create an instance of the object and then execute the function but there will be alot more overhead as the same dlls get loaded into the new AppDomain so the function can be executed.


    Is there a way to force an update to the AppDomain.CurrentDomain.RelativeSearchPath value? ( I am certain the answer is no but.... )
    Is there a way to force the AppDomain.CurrentDomain to recognize the missing dll is the same as the dll loaded a few steps ago?

    Any advice??

    Wednesday, January 9, 2008 9:10 PM

Answers

  • Please check out AppDomain.AssemblyResolve Event which occurs when the resolution of an assembly fails.

    You can subscribe to that event and do a simple Assembly.LoadFrom from a location known to you or even do some fancy loading of your own based on some system you engineer.  There are also TypeResolve and ResourceResolve events in the AppDomain that let you handle those aspects of resolving the various dependencies of an AppDomain. For more details, please visit AssemblyResolve event of an AppDomain.
    Thursday, January 10, 2008 1:18 AM

  • The CurrentDomain.AssemblyResolve did exactly what I wanted it to do. Thank you.  One note about the use for anyone who reads this thread later.

    The ResolveEventArgs.Name property is the exact name you supplied to the Load function.  In my given example, some instances I load with the name T2.  Others times are done with T2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null. 

    But the real gotcha is when you are trying to dyanmically load an Assembly that is registered such as System.Web.  If you store your Assemblies in a structure and have any special loading operation when they a loaded, you good get yourself into an infinite loop trying to get the Assembly out of the data store.

    Below, I have a collection which holds all of the valid assemblys of the applicatoin  This is the Resolve function I am using. The bActive flag  prevents the looping

    private bool bActive = false;
        private Assembly CurrentDomain_AssemblyResolve( object sender, ResolveEventArgs args ) {
          if ( bActive )
            return null;

          bActive = true;
          Assembly cur = null;
          try {
            string name = args.Name;
            if ( name.IndexOf( "," ) > 0 ) {
              for ( int i = 0; i < this.Count; i++ ) {
                Assembly a = this.Index( i ).Assembly;
                if ( a != null && a.FullName == name ) {
                  cur = a;
                  break;
                }
              }
            } else {
              AssemblyType at = this.FindByName( name );
              if ( at != null )
                cur = at.Assembly;
            }

          } catch {
            cur= null;
          }
          bActive = false;
          return cur;
        }
    Thursday, January 10, 2008 3:51 PM

All replies

  • Please check out AppDomain.AssemblyResolve Event which occurs when the resolution of an assembly fails.

    You can subscribe to that event and do a simple Assembly.LoadFrom from a location known to you or even do some fancy loading of your own based on some system you engineer.  There are also TypeResolve and ResourceResolve events in the AppDomain that let you handle those aspects of resolving the various dependencies of an AppDomain. For more details, please visit AssemblyResolve event of an AppDomain.
    Thursday, January 10, 2008 1:18 AM

  • The CurrentDomain.AssemblyResolve did exactly what I wanted it to do. Thank you.  One note about the use for anyone who reads this thread later.

    The ResolveEventArgs.Name property is the exact name you supplied to the Load function.  In my given example, some instances I load with the name T2.  Others times are done with T2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null. 

    But the real gotcha is when you are trying to dyanmically load an Assembly that is registered such as System.Web.  If you store your Assemblies in a structure and have any special loading operation when they a loaded, you good get yourself into an infinite loop trying to get the Assembly out of the data store.

    Below, I have a collection which holds all of the valid assemblys of the applicatoin  This is the Resolve function I am using. The bActive flag  prevents the looping

    private bool bActive = false;
        private Assembly CurrentDomain_AssemblyResolve( object sender, ResolveEventArgs args ) {
          if ( bActive )
            return null;

          bActive = true;
          Assembly cur = null;
          try {
            string name = args.Name;
            if ( name.IndexOf( "," ) > 0 ) {
              for ( int i = 0; i < this.Count; i++ ) {
                Assembly a = this.Index( i ).Assembly;
                if ( a != null && a.FullName == name ) {
                  cur = a;
                  break;
                }
              }
            } else {
              AssemblyType at = this.FindByName( name );
              if ( at != null )
                cur = at.Assembly;
            }

          } catch {
            cur= null;
          }
          bActive = false;
          return cur;
        }
    Thursday, January 10, 2008 3:51 PM