简体   繁体   中英

How to reference a GAC-assembly from a dynamically generated assembly?

I'm teaching myself Intermediate Language (IL) generation in C#, and I've been stuck-for-a-couple-of-hours-which-seem-like-days on how to reference System.Windows.Forms.dll (for example) from a dynamic assembly, which I'm generating using AppDomain.CurrentDomain.DefineDynamicAssembly and System.Reflection.Emit ... based on the most-excellent-example at http://olondono.blogspot.com/2008/02/creating-code-at-runtime.html

I've got a basic TransferObjectDllGenerator working, but now I'd like to reference existing libraries from (only) within the generated assembly, and can't figure out how.

This SO question lead me to the AppDomain.CurrentDomain.AssemblyResolve event. I tried implementing an event handler but it's never triggered, so I guess I've done something basically dumb, like put the event handler in completely the wrong place?

Any pointers in the right direction would be greatly appreciated.

Here's my mainline... the interesting bits are // <<-- Commented thus

using System;
using System.Reflection;
//using System.Windows.Forms; (and removed the project's Reference to System.Windows.Forms)

namespace ILGen
{
/// <summary>
/// Generates .\bin\$whatever\PersonLibrary.dll containing MSIL equivalent to:
///   namespace PersonLibrary {
///     public class Person {
///       public string FirstName { get; set; }
///       public string LastName { get; set; }
///       public Person() { }
///       public Person(string firstname, string lastname) {
///         FirstName = firstname;
///         LastName = lastname;
///       }
///     } //end-class
///   } //end-namespace
/// </summary>
public class Program
{
    public static void Main(String[] args) {
        AppDomain.CurrentDomain.AssemblyResolve += MyAssemblyResolver; // <<-- Hook the "resolve this assembly" event.
        try {
            var dll = new TransferObjectDllGenerator("PersonLibrary");
            dll.AddClass("Person", new[] {
                new Property("FirstName", typeof(string))
              , new Property("LastName", typeof(string))
              , new Property("OK", Type.GetType("System.Windows.Forms.Button")) // <<-- References System.Windows.Forms.dll; Type.GetType returns null.
            });
            Console.WriteLine("Generated: " + dll.Save());
        } finally {
            AppDomain.CurrentDomain.AssemblyResolve -= MyAssemblyResolver; // <<-- Unhook the "resolve this assembly" event.
        }
    }

    static Assembly MyAssemblyResolver(object sender, ResolveEventArgs args) // <<-- Handle the "resolve this assembly" event.
    {
        if ( args.Name.StartsWith("System.Windows.Forms.") ) // <<-- Breakpoint here, which is never reached.
            return Assembly.LoadFrom(@"C:\Windows\winsxs\msil_system.windows.forms_b77a5c561934e089_6.0.6001.22230_none_1a2132e45d2f30fc\System.Windows.Forms.dll");
        return null;
    }
} // end-class
} // end-namespace

If you need/want the TransferObjectDllGenerator code please shout... I haven't posted it because it's a bit too big (IMHO) for a forum post.

Thanks in advance for your time.

Cheers. Keith.


EDIT: To provide a working example for anyone who finds this thread in future.

There is no need to provide a custom AssemblyResolve event handler. That was a phurphy . We just need to provide the fully-qualified-name of the assembly... with one with the assembly-name, namespace, version, and GUID.

using System;
using System.Reflection;

namespace ILGen
{
public class Program
{
    public static void Main(String[] args) {
        var dll = new TransferObjectDllGenerator("PersonLibrary"); 
        // We need to provide the fully-qualified-assembly-name to 
        // make the standard assembly-resolver search the GAC. 
        // Thanks to Julien Lebosquain for pointing this out.
        Type buttonType = Type.GetType(
            "System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); 
        dll.AddClass("Person", new[] {
            new Property("FirstName", typeof(string))
          , new Property("LastName", typeof(string))
          , new Property("OK", buttonType) 
        });
        Console.WriteLine("Generated: " + dll.Save());
    }
} // end-class
} // end-namespace

Type.GetType without an assembly name specified will only search for the type name in the current assembly and in mscorlib.

Try to use the AssemblyQualifiedName of the type. Here for .NET 4:

Type.GetType("System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")

The default resolver will search in the GAC and the app domain private path so you should have nothing to do to get the behavior you desire. If you have to customize the resolution, AssemblyResolve or the new Type.GetType overload taking a resolver function in .NET 4 are the way to go. Note that resolving manually from the GAC or winsxs paths is usually a bad idea: let the framework do its resolving job.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM