Can't get MethodInfo instance in reflection-only context - c#

The following code shows method's name correctly:
static void Main(string[] args)
{
try
{
Assembly assembly = Assembly.Load("ClassLibrary1");
foreach (var referencedAssembly in assembly.GetReferencedAssemblies())
{
Assembly.Load(referencedAssembly.Name);
}
Type bl1Type = assembly.GetType("ClassLibrary1.Bl1");
var types = new[] {typeof(MyEnum) };
var method = bl1Type.GetMethod("Method1", BindingFlags.Instance | BindingFlags.Public, Type.DefaultBinder, types, null);
Console.WriteLine(method == null ? "Method was null" : $"Found {method.Name}");
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
But if I try to resolve Method1 in a reflection-only context to improve performance and change the call to Assembly.Load("ClassLibrary1"); with Assembly.ReflectionOnlyLoad("ClassLibrary1"); then method is always null and don't get resolved. Any ideas about how to resolve a method in a reflection-only context?

If you try the following you will see that your method is found in the list
var methods = bl1Type.GetMethods(BindingFlags.Instance | BindingFlags.Public);
Also,if you search for the method that accepts the string parameter it will also work
var types = new[] {typeof(string)};
I think it's confused by the MyEnum.
If you load the MyEnum from the second dll it will work
Assembly assembly2 = Assembly.ReflectionOnlyLoad("ClassLibrary2");
Type bl2Type = assembly2.GetType("ClassLibrary2.MyEnum");
var types = new[] { bl2Type };
The full code
using System;
using System.Linq;
using System.Reflection;
namespace ReflectionTest
{
class Program
{
static void Main(string[] args)
{
Assembly assembly = Assembly.ReflectionOnlyLoad("ClassLibrary1");
Type bl1Type = assembly.GetType("ClassLibrary1.Bl1");
var types = new[] { Assembly.ReflectionOnlyLoad(assembly.GetReferencedAssemblies().Single(a => a.Name == "ClassLibrary2").Name).GetType("ClassLibrary2.MyEnum") };
//var types = new[] {typeof(MyEnum)}; //doesn't work
var method = bl1Type.GetMethod("Method1", BindingFlags.Instance | BindingFlags.Public, Type.DefaultBinder, types, null);
Console.WriteLine(method == null ? "Method was null" : $"Found {method.Name}");
Console.ReadLine();
}
}
}
So,your initial code doesn't work because it tries to find a method that uses a different "MyEnum" (and of course it doesn't exist)

ReflectionOnly context is separate loader context from the regular loader context. It loads own copy of the assemblies and types, the system types from mscorlib are shared with the regular loader context though.
Also, there is typically no performance advantage in loading assemblies into ReflectionOnly context. ReflectionOnly context uses the exact same loading mechanism as regular assemblies, except that attempt to execute any code from them is blocked and a few sanity checks are suppressed (https://blogs.msdn.microsoft.com/junfeng/2004/08/24/reflection-only-assembly-loading/ describes the details).

Related

When implementing an interface that has a method with 'in' parameter by TypeBuilder.CreateType, TypeLoadException is thrown

Before beginning, this is my first question on SO. So there might be faults or lack of information about the problem. Please let me know if there's something that I need to correct. Thanks.
Using TypeBuilder, I'm building a class that implements an interface that contains a method. After implementing that method with ILGenerator, then I call TypeBuilder.CreateType() and everything goes well in the normal case.
But if the method contains any parameter with the in modifier, also known as readonly reference for value types, TypeBuilder.CreateType() throws TypeLoadException("Method 'SomeMethod' ... does not have an implementation.").
Unlike the usual case of TypeLoadException that implemented method with the same signature as the one declared in the interface(s) doesn't exist, this problem is raised only when the method contains in parameter(s) even signatures are the same. When I remove or change the in modifier to ref or out, TypeBuilder.CreateType() successfully recognizes the generated method as an implementation of one declared in the interface, and the type is built normally.
Here's a fully compilable example:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
namespace EmitMethodWithInParamTest
{
public struct StructParam
{
public String Data;
}
public interface ISomeInterface
{
Int32 SomeMethod(in StructParam param);
}
static class EmitExtension
{
public static void ReplicateCustomAttributes(this ParameterBuilder paramBuilder, ParameterInfo paramInfo)
{
foreach (var attrData in paramInfo.GetCustomAttributesData())
{
var ctorArgs = attrData.ConstructorArguments.Select(arg => arg.Value).ToArray();
// Handling variable arguments
var ctorParamInfos = attrData.Constructor.GetParameters();
if (ctorParamInfos.Length > 0 &&
ctorParamInfos.Last().IsDefined(typeof(ParamArrayAttribute)) &&
ctorArgs.Last() is IReadOnlyCollection<CustomAttributeTypedArgument> variableArgs)
{
ctorArgs[ctorArgs.Length - 1] = variableArgs.Select(arg => arg.Value).ToArray();
}
var namedPropArgs = attrData.NamedArguments.Where(arg => !arg.IsField);
var namedPropInfos = namedPropArgs.Select(arg => (PropertyInfo)arg.MemberInfo).ToArray();
var namedPropValues = namedPropArgs.Select(arg => arg.TypedValue.Value).ToArray();
var namedFieldArgs = attrData.NamedArguments.Where(arg => arg.IsField);
var namedFieldInfos = namedFieldArgs.Select(arg => (FieldInfo)arg.MemberInfo).ToArray();
var namedFieldValues = namedFieldArgs.Select(arg => arg.TypedValue.Value).ToArray();
var attrBuilder = new CustomAttributeBuilder(attrData.Constructor,
ctorArgs, namedPropInfos, namedPropValues, namedFieldInfos, namedFieldValues);
paramBuilder.SetCustomAttribute(attrBuilder);
}
}
}
class Program
{
static Program()
{
Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-us");
}
static void Main(String[] args)
{
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("DynamicAssembly"), AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
var typeBuilder = moduleBuilder.DefineType("SomeClass",
TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoLayout,
null /*base class*/,
new[] { typeof(ISomeInterface) });
var methodInfoToImpl = typeof(ISomeInterface).GetMethod(nameof(ISomeInterface.SomeMethod));
var paramInfos = methodInfoToImpl.GetParameters();
var methodBuilder = typeBuilder.DefineMethod(methodInfoToImpl.Name,
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final,
CallingConventions.HasThis,
methodInfoToImpl.ReturnType,
paramInfos.Select(pi => pi.ParameterType).ToArray());
foreach (var paramInfo in paramInfos)
{
// paramInfo.Position is zero-based but DefineParameter requires 1-based index.
var paramBuilder = methodBuilder.DefineParameter(paramInfo.Position + 1, paramInfo.Attributes, paramInfo.Name);
if (paramInfo.Attributes.HasFlag(ParameterAttributes.HasDefault))
{
paramBuilder.SetConstant(paramInfo.DefaultValue);
}
paramBuilder.ReplicateCustomAttributes(paramInfo);
}
// Dummy implementation for example. Always throws NotImplementedException.
var ilGen = methodBuilder.GetILGenerator();
ilGen.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(Type.EmptyTypes));
ilGen.Emit(OpCodes.Throw);
var builtType = typeBuilder.CreateType(); // <- TypeLoadException("Method 'SomeMethod' in type 'SomeClass' from assembly 'DynamicAssembly, ...' does not have an implementation.") is thrown.
var generatedObj = (ISomeInterface)Activator.CreateInstance(builtType);
var someParam = new StructParam() { Data = "SomeData" };
var result = generatedObj.SomeMethod(in someParam); // <- NotImplementedException expected by dummy implementation if executed.
Console.WriteLine($"Result: {result}");
}
}
}
This code is also uploaded to Pastebin.
While digging down this problem, I found that the in parameter has two custom attributes, InteropServices.InAttribute and CompilerServices.IsReadOnlyAttribute. But when I generate a method without implementing the interface (this succeeds normally because no signature matching required), in parameter of generated method has only one custom attribute, InAttribute. So I replicated all custom attributes of parameters from the interface, but still TypeLoadException is being raised.
I've tested this on .NET Framework 4.6.1 and .NET Core 2.2 with C# 7.2 and 7.3. And all environments gave me the same exception. I'm using Visual Studio 2017 on Windows.
Is there anything that I have missed or are there any workarounds?
Thank you for any help in advance.
After writing the question above, I've been investigated built binary of sample code in IL and source code of CoreCLR for a few days, and now I found the problem and solution.
In short, required and optional custom modifiers of return type and each parameter type take a part of method signature like each types do, and it had to be replicated manually. I thought that it will be done by passing ParameterAttributes.In to MethodBuilder.DefineParameter and replicating the custom attribute InAttribute, but it was wrong.
And, among in, ref and out modifiers, only in emits a required custom modifier to specified parameter. In contrast, ref and out are represented only with their type itself. This is the reason why only in didn't work as expected.
To replicate custom modifiers, call to TypeBuilder.DefineMethod need be modified like this:
var methodBuilder = typeBuilder.DefineMethod(methodInfoToImpl.Name,
MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final,
CallingConventions.HasThis,
methodInfoToImpl.ReturnType,
methodInfoToImpl.ReturnParameter.GetRequiredCustomModifiers(), // *
methodInfoToImpl.ReturnParameter.GetOptionalCustomModifiers(), // *
paramInfos.Select(pi => pi.ParameterType).ToArray(),
paramInfos.Select(pi => pi.GetRequiredCustomModifiers()).ToArray(), // *
paramInfos.Select(pi => pi.GetOptionalCustomModifiers()).ToArray() // *
);
Marked lines with // * are newly added to replicate custom modifiers of return/parameter types.
Or, we can do this by calling MethodBuilder.SetSignature method after calling DefineMethod without any type and custom modifiers arguments. If we decided to call SetSignature separately, we need to call it before any DefineParameter, SetCustomAttribute, Equals(Object), SetImplementationFlags, getter of property Signature and many other methods that call the internal method MethodBuilder.GetMethodSignature() that cache bytes representing method signature.
Thank you for reading and giving me advice. :)

Convert Reflection.Emit to Roslyn

I need to convert an existing code that uses Reflection.Emit to Roslyn.
The code I have currently is basically this:
var assemblyName = new AssemblyName("AssemblyName");
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save);
var builder = assemblyBuilder.DefineDynamicModule("test", "test.dll");
var type = builder.DefineType("Entry", TypeAttributes.Public, typeof(object), null);
var method = type.DefineMethod("###Start_v1.4.3.0", MethodAttributes.Public | MethodAttributes.HideBySig);
method.SetReturnType(typeof(void));
var generator = method.GetILGenerator();
generator.Emit(OpCodes.Nop);
generator.Emit(OpCodes.Ret);
type.CreateType();
assemblyBuilder.Save(#"test.dll");
As you can see, there is a class named Entry with a method called ###Start_v1.4.3.0.
We're using this for more than 7 years now but evereytime we need to change anything, it's a pain because we need to use those Emits and it's not trivial.
It would be great if we could just have Roslyn to compile the code:
public class Entry
{
public void ###Start_v1.4.3.0()
{
}
}
But it doesn't work due to the method name being invalid.
The compiled dll is used by a third party component and it looks for this class and method name to execute. We tried to reach the developers to have a new version but no luck.
I think Roslyn won't compile this at all, but I believe there might be a way to rename the method name later from let's say just Start() to ###Start_v1.4.3.0()... I just don't know how to do this.
Any help will be very welcome.
If the only problem is the illegal method name, you can easily resolve that issue.
Compile the dll with a legal name, and then you have several ways to change the method name.
With mono.cecil its pretty simple.
public void ChangeMethodName()
{
//Before changing the method name
var assem = Assembly.LoadFile(#"C:\temp\ClassLibrary1.dll");
Console.WriteLine(
assem.GetType("ClassLibrary1.Class1").
GetMethod("Start", BindingFlags.Static | BindingFlags.Public).
Invoke(null, null));
// Change the name
var module = ModuleDefinition.ReadModule(#"C:\temp\ClassLibrary1.dll");
TypeDefinition myType =
module.Types.First(type => type.Name == "Class1");
var method = myType.Methods.First(m => m.Name == "Start");
method.Name = "###Start_v1.4.3.0";
module.Write(#"C:\temp\ClassLibrary1_new.dll");
//After changing the method name
assem = Assembly.LoadFile(#"C:\temp\ClassLibrary1_new.dll");
Console.WriteLine(
assem.GetType("ClassLibrary1.Class1").
GetMethod("###Start_v1.4.3.0",
BindingFlags.Static|BindingFlags.Public).
Invoke(null, null));
}
public class Class1
{
public static string Start()
{
return $"my name is {MethodBase.GetCurrentMethod().Name}";
}
}

C# add event handler to class that has been loaded from an assembly at run time

As part of evaluating a 3rd party DLL called "GemBox.document", i want to be able to run this assembly during run time. However in order to get it to run in trial mode, i need to use this:
ComponentInfo.FreeLimitReached +=
(sender, e) => e.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial;
This is the standard way if you directly reference the DLL in the application. However, i want to be able to do this by calling the DLL at runtime. What is the correct syntax for this?
Edit: ComponentInfo is a public static class of GemBox.Document
For future reference, here is how we can load GemBox.Document assembly at run-time and set it in a Trial mode through reflection:
using System;
using System.Reflection;
class Program
{
// Load GemBox.Document assembly.
static Assembly gemboxAssembly = Assembly.LoadFrom(#"C:\GemBox.Document.dll");
// Create method for handling FreeLimitReached event.
static void HandleFreeLimit(object sender, EventArgs e)
{
// Call: e.FreeLimitReachedAction = FreeLimitReachedAction.ContinueAsTrial
dynamic freeLimitArgs = e;
freeLimitArgs.FreeLimitReachedAction =
(dynamic)Enum.Parse(
gemboxAssembly.GetType("GemBox.Document.FreeLimitReachedAction"),
"ContinueAsTrial");
}
static void Main(string[] args)
{
// Call: ComponentInfo.SetLicense("FREE-LIMITED-KEY")
Type componentInfo = gemboxAssembly.GetType("GemBox.Document.ComponentInfo");
componentInfo.GetMethod("SetLicense", BindingFlags.Public | BindingFlags.Static)
.Invoke(null, new object[] {"FREE-LIMITED-KEY"});
// Get HandleFreeLimit as MethodInfo.
MethodInfo handleFreeLimitMethod =
typeof(Program).GetMethod("HandleFreeLimit",
BindingFlags.NonPublic | BindingFlags.Static);
// Call: ComponentInfo.FreeLimitReached += HandleFreeLimit
EventInfo freeLimitReached = componentInfo.GetEvent("FreeLimitReached");
freeLimitReached.AddEventHandler(null,
Delegate.CreateDelegate(freeLimitReached.EventHandlerType,
handleFreeLimitMethod));
// Call: DocumentModel document = DocumentModel.Load(#"C:\Sample.docx")
Type documentModel = gemboxAssembly.GetType("GemBox.Document.DocumentModel");
dynamic document = documentModel.GetMethod("Load", new Type[]{ typeof(string)})
.Invoke(null, new object[] { #"C:\Sample.docx" });
// TODO: Use "document" object as needed ...
document.Save(#"C:\Sample.pdf");
}
}

How can I simulate a ReflectionTypeLoadException?

I have a user in production who is getting a ReflectionTypeLoadException while trying get the assembly types from an Outlook interop assembly. I need to code the application to better debug the problem but I cannot reproduce his issue so I have no way of testing the code to make sure it is giving me what I need.
I found this post: Error message 'Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.' which has sample code but I would like to debug through it to make sure it works for me and to see how it works.
Here is my code that throws. I have the actual assembly loaded. It is when I enumerate the types contained within that I get the exception:
Type t = assembly.GetTypes().Where(x => x.IsClass && x.Name.Equals("ApplicationClass")).FirstOrDefault();
Can anyone provide a sample or some insight into how I may simulate this problem so I can validate the code that I need to write?
Thanks.
I came across the same issue today. Here's what I came up with, based on one of the cases where I've hit this in the past:
var assemblyName = new AssemblyName("TestAssembly");
var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var module = assemblyBuilder.DefineDynamicModule("MainModule");
var typeBuilder = module.DefineType(name: "TestType", attr: TypeAttributes.Public | TypeAttributes.Class);
// uncommenting this stops the error. Basically, GetTypes() seems to fail if the ModuleBuilder
// has an "unfinished" type
//typeBuilder.CreateType();
module.Assembly.GetTypes();
It seems all public members of System.Reflection.Assembly are virtual, so something like this may suit your purposes:
public class DodgyAssembly : Assembly
{
public override Type[] GetTypes()
{
throw new ReflectionTypeLoadException(new [] { typeof(Foo) }, new [] { new Exception() });
}
}
var assembly = new DodgyAssembly();
At the point where you create an instance of the outlook class, do you have a try block around it to get a stacktrace? also, does the application run as a client application? if so, the user may not have outlook installed?
You should be able to just throw a ReflectionTypeLoadException and use that to test. Obviously you should probably throw a more realistic example for your particular case, but this will simulate that exception type.
try
{
var test = 10;
throw new ReflectionTypeLoadException(new Type[] { test.GetType() }, new Exception[] { new FileNotFoundException() });
}
catch (ReflectionTypeLoadException ex)
{
var whatIsTheException = ex;
}

Beginner's reflection issues

I'm trying to learn reflection in C# and need some help with my code. I've had trouble finding good code examples/guides, so I apologize if my code is poorly done.
Essentially I'm just trying to check out a given assembly dll for a particular method name (path and method name have been redacted).
The problem occurs on the line object lateBoundObj = asm.CreateInstance(typeName); and it reads An object reference is required for the non-static field, method, or property...
I understand this has to do with static vs non-static and creating a new Assembly or something along those lines, but need some help understanding the issue and how to fix it.
Thank you!
public const string assemblyPath = #"<my file path>";
Assembly asm;
static void Main(string[] args)
{
//asm = new Assembly();
Console.Read();
MethodInfo mi;
object result = null;
object[] arguments = new object[] { "ABC123" };
try
{
Assembly assemblyInstance = Assembly.LoadFrom(assemblyPath);
Type[] types = assemblyInstance.GetTypes();
foreach (Type t in types)
{
mi = t.GetMethod("<my method name>");
if (mi != null)
{
string typeName = t.FullName;
object lateBoundObj = asm.CreateInstance(typeName);
result = t.InvokeMember("GetWeb", BindingFlags.Public | BindingFlags.InvokeMethod | BindingFlags.Instance, null, lateBoundObj, arguments);
break;
}
}
//set return for find method
}
catch (Exception ex) { }
}
The problem is that you're never assigning a value to asm, so it's got the default value of null. Perhaps you meant to use assemblyInstance instead?
In fact, I wouldn't use Assembly.CreateInstance or Type.FullName at all there - I'd use:
object lateBoundObj = Activator.CreateInstance(t);
Also note, you should always avoid code like this:
catch (Exception ex) { }
Always at least log the exception. Ideally, don't catch an exception you can't really "handle" at all.
asm variable is never assigned. You should call CreateInstance on assemblyInstance instead.

Categories