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;
/// Last开发者_运维技巧Name = 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.
精彩评论