I am developing a library in C# that generates runtime types using System.Reflection.Emit.TypeBuilder class and i want to generate the following class hierarchy:
[XmlInclude(typeof(Derived))]
public class Base
{
}
public class Derived : Base
{
}
I use the TypeBuilder class in the following way:
class Program
{
public static void Main(string[] args)
{
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"), AssemblyBuilderAccess.Run);
var moduleBuilder = assembly.DefineDynamicModule("Test");
var baseTypeBuilder = moduleBuilder.DefineType("Base", TypeAttributes.Public, typeof(Object));
var derivedTypeBuilder = moduleBuilder.DefineType("Derived", TypeAttributes.Public);
derivedTypeBuilder.SetParent(baseTypeBuilder);
baseTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(XmlIncludeAttribute).GetConstructor(new[] { typeof(Type) }), new[] { derivedTypeBuilder }));
var baseType = baseTypeBuilder.CreateType();
var derivedType = derivedTypeBuilder.CreateType();
var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
}
}
The call:
var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
I receive the following error:
Could not load file or assembly 'Test, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
Any ideas are well-appreciated: how can i apply a custom attribute on a TypeBuilder for base class that refers to a TypeBuilder for a derived class?
P.S: I'm using Visual Studio 2017 (v15.7.5) and a C# Class Library (.NET Framework project template) NOT .NET Core or .NET Standard
Cannot provide much reason but a solution that will work without additional load/unload or whatever from and to disk:
Adding a separate field containing the actual Assembly, one can just subscribe to AppDomain.CurrentDomain.AssemblyResolve and check if the dynamic assembly was requested.
If it was, you just need to return your field and it works perfectly fine.
Example:
class Program
{
static Assembly ass;
public static void Main(string[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Test"), AssemblyBuilderAccess.Run);
var moduleBuilder = assembly.DefineDynamicModule("Test");
var baseTypeBuilder = moduleBuilder.DefineType("Base", TypeAttributes.Public, typeof(Object));
var derivedTypeBuilder = moduleBuilder.DefineType("Derived", TypeAttributes.Public);
derivedTypeBuilder.SetParent(baseTypeBuilder);
baseTypeBuilder.SetCustomAttribute(new CustomAttributeBuilder(typeof(XmlIncludeAttribute).GetConstructor(new[] { typeof(Type) }), new[] { derivedTypeBuilder }));
var baseType = baseTypeBuilder.CreateType();
var derivedType = derivedTypeBuilder.CreateType();
ass = baseType.Assembly;
var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
Console.WriteLine(attribute.Type.FullName);
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return ass;
}
}
I've reproduced your exception. It's looks like the .NET Framework want to read a module from disk.
So simplest workaround would be to save your assembly to the disk:
var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
new AssemblyName("Test"),
AssemblyBuilderAccess.RunAndSave); // allow run & save
var moduleBuilder = assembly.DefineDynamicModule("Test",
"Test.dll"); // specify a file name where module will be stored
...
var baseType = baseTypeBuilder.CreateType();
var derivedType = derivedTypeBuilder.CreateType();
assembly.Save("Test.dll");
Now I was able to get attribute without exception:
var attribute = baseType.GetCustomAttribute<XmlIncludeAttribute>();
Well, I can tell you why the fix #X39 posted works. Stepping through the forest of framework code (perhaps fallbacks for different cultures) for the baseType.GetCustomAttribute(); call, when finally reaching appdomain.cs I see:
At this point, _AssemblyResolve is null (which is the private backing field for AppDomain.CurrentDomain.AssemblyResolve) and after stepping through to return null;, the code crashes with the error you posted.
More info: https://learn.microsoft.com/en-us/dotnet/api/system.appdomain.assemblyresolve?view=netframework-4.7.2
#X39 feel free to merge my answer and let me know so that I can remove mine, I did not want to edit your answer directly.
Related
I'm developing some .Net application and I need to inject in any assembly new method with my own code. I'm using Mono.Cecil to get body of assembly and I found some samples, but they're old enough. Unfortunately, there's no info in migraton section on github wiki.
So, I have this code:
using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace CustomFieldsInjection
{
public partial class Injector
{
public static void MethodInjection(string assemblyFilename, string typeName, string methodName)
{
AssemblyDefinition assembly = AssemblyFactory.GetAssembly(assemblyFilename);
TypeReference returnTypeReference = assembly.MainModule.Import(typeof(void));
MethodDefinition methodDefinition = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Static, returnTypeReference);
Instruction instruction1 = methodDefinition.Body.CilWorker.Create(OpCodes.Nop);
Instruction instruction2 = methodDefinition.Body.CilWorker.Create(OpCodes.Ldstr, methodName);
MethodReference writeline = assembly.MainModule.Import(typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }));
methodDefinition.Body.CilWorker.Append(instruction1);
methodDefinition.Body.CilWorker.Append(instruction2);
methodDefinition.Body.CilWorker.InsertAfter(instruction2, methodDefinition.Body.CilWorker.Create (OpCodes.Call, writeline));
methodDefinition.Body.CilWorker.Append (methodDefinition.Body.CilWorker.Create(OpCodes.Ret))
assembly.MainModule.Inject(methodDefinition, assembly.MainModule.Types[typeName]);
MethodReference methodReference = null;
foreach (MethodDefinition method in assembly.MainModule.Types[typeName].Methods)
{
if (method.Name == methodName)
{
methodReference = assembly.MainModule.Import(method);
break;
}
}
Instruction callTest = methodDefinition.Body.CilWorker.Create(OpCodes.Call, methodReference);
if (assembly.EntryPoint != null)
{
assembly.EntryPoint.Body.CilWorker.InsertBefore(assembly.EntryPoint.Body.Instructions[0], callTest);
}
AssemblyFactory.SaveAssembly(assembly, assemblyFilename);
}
}
}
It's old sample. Most features are up to date. I'm interesting in this construction:
assembly.MainModule.Inject(methodDefinition, assembly.MainModule.Types[typeName]);
I could not find a new analogues of this design. Someone can tell me what it can be replaced?
I'm not familiar with the construct you are referring to, but adding a MethodDefinition to an existing type is quite easy
using (var assemblyDefinition = AssemblyDefinition.ReadAssembly("assemblyPath")) {
var module = AssemblyDefinition.MainModule;
//Select the type you need to open for addition
var typeDef = module.Types.First(td => td.Name == "footer");
//Add your MethodDefinition
typeDef.Methods.Add(your_method_definition);
//Write the assembly back
assemblyDefinition.Write();
}
NOTE: If you don't use yet cecil 0.10.0.0 you'll use slightly different ReadAssembly() and Write() variants (without the using, and passing the assemblyPath to Write, mainly...)
I am trying to source a PropertyGrid with a dynamically generated object.
For combo selections on this property grid, I have built a TypeConverter (where T is an enum, defining the list of options):
public class TypedConverter<T> : StringConverter where T : struct, IConvertible
{
...
public override System.ComponentModel.TypeConverter.StandardValuesCollection
GetStandardValues(ITypeDescriptorContext context)
{
if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
string[] values = Enum.GetValues(typeof(T)).OfType<object>().Select(o => o.ToString()).ToArray();
return new StandardValuesCollection(values);
}
}
I can then add a custom attribute to the property, referencing this TypeConverter, as below (typedConverterGenericType is the the type of TypedConverter with an enum generic argument)
CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(typeof(TypeConverterAttribute).GetConstructor(new Type[] { typeof(Type) }), new Type[] { typedConverterGenericType });
propertyBuilder.SetCustomAttribute(attributeBuilder);
This works great, as long as the Enum in question is hardcoded: AddTypeConverterAttribute(propertyBuilder, typeof(TypedConverter<Fred>));. In the debugger, the attribute on the property gives me {[System.ComponentModel.TypeConverterAttribute( ....
However, when I use a dynamically built enum (that I have determined is generated properly in reflection) does not work:
Type enumType = enumBuilder.CreateType();//This generates a proper enum, as I have determined in reflection
Type converterType = typeof(TypedConverter<>);
Type typedConverterType = converterType.MakeGenericType(enumType);
AddTypeConverterAttribute(propertyBuilder, typedConverterType);
In the debugger, the attribute on the property now gives me {System.Reflection.CustomAttributeData}, and drilling into this, I have an error on the ConstructorArguments ... Mscorlib_CollectionDebugView<System.Reflection.CustomAttributeData>(type.GetProperties()[1].CustomAttributes).Items[4].ConstructorArguments' threw an exception of type 'System.IO.FileNotFoundException'
What am I doing wrong? How can I get the TypeConverter attribute set properly?
EDIT: In case someone wants to see how I add the attribute
private void AddTypeConverterAttribute(PropertyBuilder propertyBuilder, Type typedConverterGenericType)
{
CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(typeof(TypeConverterAttribute).GetConstructor(new Type[] { typeof(Type) }), new Type[] { typedConverterGenericType });
propertyBuilder.SetCustomAttribute(attributeBuilder);
}
EDIT2
Testing confirms it is an issue with the dynamically built enum - if I create the generic type with Type typedConverterType = converterType.MakeGenericType(typeof(Fred)); it works fine.
EDIT 3
My test project is available here. It is reading some JSON from Resouces, and trying to generate a class whose type is described by that JSON.
I am creating an instance of that class (Activator.CreateInstance) that will source a PropertyGrid. To get combo selection on that PropertyGrid, I am creating a Type, with a property attributed with TypedConverter, where T is an enum that describes the values in the combo selection.
This works great for hardcoded enums, but not for programatically generated ones
I just figured out that there is a simple solution to this problem. You need to set the AssemblyResolve event of your current domain and return the the requested assembly in the event handler:
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return AppDomain
.CurrentDomain
.GetAssemblies()
.FirstOrDefault(assembly => assembly.FullName == args.Name);
}
This will make your dynamically generated Enums work
I believe I was able to get this working by using different dynamic assemblies. Let me know if this works for you:
AppDomain currentDomain = AppDomain.CurrentDomain;
AssemblyName enumAssembly = new AssemblyName("enumAssembly");
AssemblyBuilder ab = currentDomain.DefineDynamicAssembly(
enumAssembly, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = ab.DefineDynamicModule(enumAssembly.Name,
enumAssembly.Name + ".dll");
// Define a public enumeration with the name "Foo" and an
// underlying type of Integer.
EnumBuilder eb = mb.DefineEnum("Foo", TypeAttributes.Public, typeof(int));
eb.DefineLiteral("Bar", 0);
eb.DefineLiteral("Baz", 1);
Type final_foo = eb.CreateType();
ab.Save(enumAssembly.Name + ".dll");
var converterType = typeof(TypedConverter<>);
AssemblyName dynamicAsm = new AssemblyName();
dynamicAsm.Name = "DynamicAsm";
// To generate a persistable assembly, specify AssemblyBuilderAccess.RunAndSave.
AssemblyBuilder myAsmBuilder = currentDomain.DefineDynamicAssembly(dynamicAsm,
AssemblyBuilderAccess.RunAndSave);
// Generate a persistable single-module assembly.
ModuleBuilder myModBuilder =
myAsmBuilder.DefineDynamicModule(dynamicAsm.Name, dynamicAsm.Name + ".dll");
TypeBuilder myTypeBuilder = myModBuilder.DefineType("CustomerData",
TypeAttributes.Public);
PropertyBuilder custNamePropBldr = myTypeBuilder.DefineProperty("elevation",
PropertyAttributes.HasDefault,
final_foo,
null);
var typedConverterType = converterType.MakeGenericType(final_foo);
CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(
typeof(TypeConverterAttribute).GetConstructor(
new Type[] { typeof(Type) }),
new Type[] { typedConverterType }
);
custNamePropBldr.SetCustomAttribute(attributeBuilder);
I found the following code at Dr Dobbs (slightly rewritten):
namespace TestEXEApp
{
public class Program
{
static void Main(string[] args)
{
AssemblyName an = new AssemblyName();
an.Name = "TestEXEApp";
AppDomain ad = AppDomain.CurrentDomain;
AssemblyBuilder ab = ad.DefineDynamicAssembly(an,
AssemblyBuilderAccess.Save);
ModuleBuilder mb = ab.DefineDynamicModule(an.Name, "Hello.exe");
TypeBuilder tb = mb.DefineType("TestEXEApp.Program",
TypeAttributes.Public | TypeAttributes.Class);
MethodBuilder fb = tb.DefineMethod("Main",
MethodAttributes.Public |
MethodAttributes.Static,
typeof(int), new Type[] { typeof(string[]) });
ILGenerator ilg = fb.GetILGenerator();
ilg.Emit(OpCodes.Ldstr, "Hello, World!");
ilg.Emit(OpCodes.Call, typeof(TestClasses.Class1).GetMethod("Test",
new Type[] { typeof(string) }));
ilg.Emit(OpCodes.Ldc_I4_0);
ilg.Emit(OpCodes.Ret);
// Seal the lid on this type
Type t = tb.CreateType();
// Set the entrypoint (thereby declaring it an EXE)
ab.SetEntryPoint(fb, PEFileKinds.ConsoleApplication);
// Save it
ab.Save("Hello.exe");
Console.WriteLine("Press enter...");
Console.ReadLine();
}
}
}
Also, I have the following class in a separate project called TestClasses:
namespace TestClasses
{
public class Class1
{
public static void Test(String t)
{
Console.WriteLine("Test: " + t);
}
}
}
Now, when I compile this, I get (obviously) the two files "TestEXEApp.exe" and "TestClasses.dll". If I run it, I get a new file, "Hello.exe", and if I run this new file, it prints "Test: Hello, World!". So far, so good. Now, the problem is, this new file obviously depends on TestClasses.dll. So if I emit it into another directory, or move it for some reason, it can no longer execute.
Is there some way for Reflection.Emit to in some way include the TestClasses.Class1 code into the Hello.exe executable, so that it becomes 100% self-contained (apart from the dependence on the .NET framework, obviously)? Or, if that can't be done, how can I get the program to output a copy of TestClasses.dll, so that I always have the dll-file written in the same directory as Hello.exe?
How do I get a list of references in the parent assembly in C#. I'm thinking of a DLL that is loaded into another program, and the driver needs to use some of the parent assembly references in reflection and serialization. So far, I haven't tried anything as I'm not sure where to start.
It's pretty classic reflection issue, when you need to load an assembly and the assembly contains references, which are not referenced to the calling assembly.
Basically, you should load the assembly inside separate application domain.
e.g., you have a project ProxyDomain with a class ProxyType:
public class ProxyType : MarshalByRefObject
{
public void DoSomeStuff(string assemblyPath)
{
var someStuffAssembly = Assembly.LoadFrom(assemblyPath);
//Do whatever you need with the loaded assembly, e.g.:
var someStuffType = assembly.GetExportedTypes()
.First(t => t.Name == "SomeStuffType");
var someStuffObject = Activator.CreateInstance(someStuffType);
someStuffType.GetMethod("SomeStuffMethod").Invoke(someStuffObject, null);
}
}
And in your calling project, which contains a reference to ProxyDomain, you need to load the assembly, execute DoSomeStuff and unload the assembly resources:
public class SomeStuffCaller
{
public void CallDoSomeStuff(string assemblyPath)
{
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
//Path to the directory, containing the assembly
setup.ApplicationBase = "...";
//List of directories where your private references are located
setup.PrivateBinPath = "...";
setup.ShadowCopyFiles = "true";
var reflectionDomain = AppDomain.CreateDomain("ProxyDomain", null, setup);
//You should specify the ProxyDomain assembly full name
//You can also utilize CreateInstanceFromAndUnwrap here:
var proxyType = (ProxyType)reflectionDomain.CreateInstanceAndUnwrap(
"ProxyDomain",
"ProxyType");
proxyType.DoSomeStuff(assemblyPath);
AppDomain.Unload(reflectionDomain);
}
}
I have a assembly. In this assembly I have a class and interface. I need to load this assembly at runtime and want to create an object of the class and also want to use the interface.
Assembly MyDALL = Assembly.Load("DALL"); // DALL is name of my dll
Type MyLoadClass = MyDALL.GetType("DALL.LoadClass"); // LoadClass is my class
object obj = Activator.CreateInstance(MyLoadClass);
This is my code. How could it be improved?
If your assembly is in GAC or bin use the assembly name at the end of type name instead of Assembly.Load().
object obj = Activator.CreateInstance(Type.GetType("DALL.LoadClass, DALL", true));
You should Use Dynamic Method with for Improving. its faster than reflection..
Here is a sample code for creating Object using Dynamic Method..
public class ObjectCreateMethod
{
delegate object MethodInvoker();
MethodInvoker methodHandler = null;
public ObjectCreateMethod(Type type)
{
CreateMethod(type.GetConstructor(Type.EmptyTypes));
}
public ObjectCreateMethod(ConstructorInfo target)
{
CreateMethod(target);
}
void CreateMethod(ConstructorInfo target)
{
DynamicMethod dynamic = new DynamicMethod(string.Empty,
typeof(object),
new Type[0],
target.DeclaringType);
ILGenerator il = dynamic.GetILGenerator();
il.DeclareLocal(target.DeclaringType);
il.Emit(OpCodes.Newobj, target);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Ret);
methodHandler = (MethodInvoker)dynamic.CreateDelegate(typeof(MethodInvoker));
}
public object CreateInstance()
{
return methodHandler();
}
}
//Use Above class for Object Creation.
ObjectCreateMethod inv = new ObjectCreateMethod(type); //Specify Type
Object obj= inv.CreateInstance();
This method takes only 1/10th time needed by Activator.
Check out http://www.ozcandegirmenci.com/post/2008/02/Create-object-instances-Faster-than-Reflection.aspx
Check out http://www.youtube.com/watch?v=x-KK7bmo1AM
To modify his code to load multiple assemblies use
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
string assemblyName = args.Name.Split(',').First();
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("YourNamespace." + assemblyName + ".dll"))
{
byte[] assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
In your main method put
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Be sure to add your assemblies to your project and change the build action property to "Embedded Resource".