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.
Related
I have these lines of code in my .NET 4.7.2 server application:
object saveObject = proxyDef.GetEntityAsNativeObject(entity, DynamicProxyAssembly); // this works
((AxdSalesOrder)saveObject).SalesTable[0].TableDlvAddr = null; // this throws error
I can execute the null-set (2nd line above) in the VS2019 Watch Window and it works perfectly and achieves the desired effect. But when I allow it to run in normal execution (whether in debug mode or not), I get an unhandled exception on that 2nd line:
Unable to cast object of type 'AxdSalesOrder' to type
'Elogix.MSAx.Core.Ax2012ElogixServices.AxdSalesOrder'
There is dynamic stuff going in in relation to that type:
public override object GetEntityAsNativeObject(MSAxEntity entity, Assembly dynamicProxyAssembly) {
var salesOrderObject = Activator.CreateInstance(dynamicProxyAssembly.GetType("AxdSalesOrder"));
var salesOrderTable = DynamicEntityUtil.CreateObjectFromDynamicEntity(entity, dynamicProxyAssembly, "AxdEntity_SalesTable");
Array tableLines =
Array.CreateInstance(
salesOrderObject.GetType().GetProperty("SalesTable").PropertyType.GetElementType(), 1);
tableLines.SetValue(salesOrderTable, 0);
salesOrderObject.SetPropertyValue("SalesTable", tableLines);
return salesOrderObject;
}
public static object CreateObjectFromDynamicEntity(DynamicEntity entity, Assembly dynamicProxyAssembly, string objectTypeName) {
return CreateObjectFromDynamicEntity(entity, dynamicProxyAssembly.GetType(objectTypeName));
}
public static object CreateObjectFromDynamicEntity(DynamicEntity entity, Type type) {
if (type == null) {
throw new ArgumentException("Cannot create object from dynamic entity because \"Type\" is null.");
}
if (type.IsArray) {
return CreateArrayFromDynamicEntity(entity, type);
}
return CreateClassFromDynamicEntity(entity, type);
}
private static object CreateClassFromDynamicEntity(DynamicEntity entity, Type type) {
var nativeObject = Activator.CreateInstance(type);
// this will recursively convert the dynamic values to the native type values on the object.
updateValuesFromDynamicValues(entity);
var modifiedProperties = from property in entity.Properties
//where property.State != DynamicPropertyState.Unchanged
select property;
foreach(var property in modifiedProperties) {
Type valueUnderlyingType = Nullable.GetUnderlyingType(property.Type);
if (valueUnderlyingType != null && valueUnderlyingType.IsEnum) {
PropertyInfo info = nativeObject.GetType().GetProperty(property.Name);
Type targetUnderlyingType = Nullable.GetUnderlyingType(info.PropertyType);
if (property.Value == null) {
info.SetValue(nativeObject, null, null);
} else {
object correctedValue = property.Value.CorrectedEnumValue(targetUnderlyingType);
info.SetValue(nativeObject, correctedValue, null);
}
} else if (property.Type.IsEnum) {
if (property.Value == null) {
continue;
}
object correctedValue = property.Value.CorrectedEnumValue(property.Type);
nativeObject.SetPropertyValue(property.Name, correctedValue);
} else {
try {
nativeObject.SetPropertyValue(property.Name, property.Value);
} catch (Exception ex) {
Log.Write(ex.Message);
}
}
}
return nativeObject;
}
Here is how it looks in the VS2019 Watch Window:
Did this in Immediate Window:
var t = saveObject.GetType();
t.FullName
"AxdSalesOrder"
As you can see, the type's FullName is not very full, not qualified by anything, due to the dynamic nature of the type.
I can try it this way:
(saveObject as AxdSalesOrder).SalesTable[0].TableDlvAddr = null;
Again, that works in Watch, but throws this exception when run in normal execution:
Object reference not set to an instance of an object.
Clearly, VS/Watch knows the type, which is why it can execute without errors inside Watch. But the .net runtime apparently doesn't know the type at run time. How can I get this object cast to work in normal code execution like it does when run in Watch?
The answer here is to take advantage of dynamic typing since the static type is not available here:
dynamic saveObject = proxyDef.GetEntityAsNativeObject(entity, DynamicProxyAssembly);
saveObject.SalesTable[0].TableDlvAddr = null;
Another possible approach would be to use reflection, however since the expression applied to the object involves two properties and an index lookup (.SalesTable[0].TableDlvAddr) this would look much less readable.
The GetEntityAsNativeObject could also benefit from this style, you could consider rewriting it to using dynamic binding rather than reflection.
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).
The returned type of namespaceObjType is a System.__ComObject and it's impossible to call InvokeMember("OpenSharedItem", ...) on it.
How do you need to call this method with the late binding technique?
The only difference I see is that the returned object type of the Session property is only an interface instead of a real COM Class.
Code example:
object outlookApp = System.Runtime.InteropServices.Marshal.GetActiveObject("Outlook.Application");
Type outlookAppType = outlookApp.GetType();
object templateObj = null;
System.IO.File.Copy(templateName, temporaryFileName, true);
object namespaceObj = outlookAppType.InvokeMember("Session", System.Reflection.BindingFlags.GetProperty, null, outlookApp, new object[0]);
if(namespaceObj != null)
{
Type namespaceObjType = namespaceObj.GetType();
// Exception on the next line of code
templateObj = namespaceObjType.InvokeMember("OpenSharedItem", System.Reflection.BindingFlags.InvokeMethod, null, outlookApp, new object[] { temporaryFileName });
}
Exception after executing is: Unknown name. (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))
You need to pass a namesapce object instead of the outlookApp:
namespaceObjType.InvokeMember("OpenSharedObject", System.Reflection.BindingFlags.InvokeMethod, null, namespaceObj, new object[] { temporaryFileName });
Anyway, the Namespace class doesn't provide the OpenSharedObject moded.
Are you interested in the OpenSharedItem method?
I am trying load a function in a dll. The dll is loaded but just at the place of invoking the function, I am getting an exception
Ambiguous match found
Here is the code snippet.
Assembly dll = Assembly.LoadFrom(DLLPATH);
if (dll != null)
{
Type Tp = dll.GetType("ABCD.FooClass");
if (Tp != null)
{
Object obj = Activator.CreateInstance(Tp);
if (obj != null)
{
List = (List<String>)obj.GetType().GetMethod("Foo").Invoke(obj, null);
}
else
{
Console.WriteLine("obj is null");
}
}
Console.WriteLine("Type is null");
}
else
Console.WriteLine("Dll is not loaded");
Console.ReadKey();
The method which I am calling (i.e Foo), does not accept any parameters and it is an overloaded method. Is that the place where I am going wrong or is it some other place?
Is there another way to invoke such methods which does not accept any parameters? I tried the solution posted here but it is not working.
If there is an overload and you want to invoke the method with no parameters this is the correct solution:
MethodInfo mi = obj.GetType().GetMethod("Foo", new Type[] { });
The method Type.GetMethod(string methodName) throws the exception you mentioned if there is more than one method with the specified name ( see this MSDN topic ). As Foo is an overload as you say I suspect that there are multiple Foo methods in the same DLL. If you have for example the methods :
IList<string> Foo()
IList<string> Foo(object someParameter)
The method GetMethod(string methodName) can not determine which one you want to have. In this case you should use the method GetMethods and determine the correct method on your own.
Thanks guys for your help.!!
As I told you, the method (i.e FOO) which I was calling, is overloaded. I did not used GetMethod() properly I suppose. Now, I found a solution using GetMethods() function.
I changed my code in following way and it worked.!!
Assembly dll = Assembly.LoadFrom(DLLPATH);
if (dll != null)
{
Type Tp = dll.GetType("ABCD.FooClass");
if (Tp != null)
{
Object obj = Activator.CreateInstance(Tp);
if (obj != null)
{
MethodInfo[] AllMethods = obj.GetType().GetMethods();
MethodInfo Found = AllMethods.FirstOrDefault(mi => mi.Name == "Foo" && mi.GetParameters().Count() == 0);
if (Found != null)
List = (List<String>)Found.Invoke(obj, null);
}
else
Console.WriteLine("obj is null");
}
else
Console.WriteLine("Type is null");
}
else
Console.WriteLine("Dll is not loaded");
Thanks.
My "Ambiguous match found" was I had a textbox in the ASCX(frontend) named Bio, a data element named Bio in a listview <%# DataBinder.Eval(Container.DataItem, "Bio")%> and I named a string variable Bio in .CS.
No build errors or "redlines" but generated an error at runtime. I renamed the variables differently and the error went away. The prgrammer who wrote the code didn't follow naming conventions such as BioTxt. This would of eliminated the error.
I have a WCF service that accepts an object as a parameter that has a URI and Method Name.
What I am trying to do is have a method that will look # the URI, if it contains the words "localhost" it will use reflection and call a method, of the name that is passed in as a parameter, within the the same class, return a value and continue on.
public class Test
{
public GetStatResponse GetStat(GetStatRequest request)
{
GetStatResponse returnValue = new GetStatResponse();
if(Helpers.Contains(request.ServiceURI,"localhost", StringComparison.OrdinalIgnoreCase))
{
MethodInfo mi = this.GetType().GetMethod(request.ServiceMethod /*, BindingFlags.Public | BindingFlags.IgnoreCase*/);
returnValue = (GetStatResponse)mi.Invoke(this,null);
}
The above is the code segment pertaining to this question. I pull the MethodInfo no problem but I am running into issues on the mi.Invoke. The exception that I receive is "Exception has been thrown by the target of an invocation." With an Inner Exception "Object reference not set to an instance of an object". I have tried changing the code to (GetStatResponse)mi.Invoke(new Test(), null), with no luck. Test being the class.
I'm open to other suggestions as to how to resolve this, I just thought reflection might be the easiest.
The Method that I am calling with my testing is defined as
public GetStatResponse TestMethod()
{
GetStatResponse returnValue = new GetStatResponse();
Stat stat = new Stat();
Stat.Label = "This is my label";
Stat.ToolTip = "This is my tooltip";
Stat.Value = "this is my value";
returnValue.Stat = stat;
return returnValue;
}
Because you are not specifying BindingFlags in your GetMethod() call, you are only going to be returned methods matching the name containing request.ServiceMethod that are PUBLIC.
Check whether the method you are trying to invoke is public, otherwise MethodInfo will return null.
If it is not public, either make the method public or include the BindingFlags.NonPublic flag.
Also, you should always make sure that mi != null before calling mi.Invoke
Before calling the method you might want to make sure that the MethodInfo you are pulling through reflection is not null:
MethodInfo mi = this.GetType().GetMethod(
request.ServiceMethod,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase
);
// Make sure that the method exists before trying to call it
if (mi != null)
{
returnValue = (GetStatResponse)mi.Invoke(this, null);
}
After your update it seems that the exception is thrown inside the method you are calling:
GetStatResponse returnValue = new GetStatResponse();
// Don't forget to initialize returnValue.Stat before using it:
returnValue.Stat = new WhateverTheTypeIs();
returnValue.Stat.Label = "This is my label";