Imagine the following situation:
Assembly A is starting the program. (it has a main method)
it loads Assembly B via reflection and instantiates a class of Assembly B.
in this instance a method is called where i would like to get to the Assembly B.
i have already tried
System.Reflection.Assembly.GetCallingAssembly();
System.Reflection.Assembly.GetExecutingAssembly();
but they always give me Assembly A instead of B.
Try getting type of class contain the method you are going for and get its assembly.
string assemblyName = this.GetType().Assembly.FullName;
The Assembly.GetExecutingAssembly() is the proper method to use.
You leave preciously few breadcrumbs to diagnose the cause of having trouble with it. There are however strings attached to this. An important job performed by the jitter is to make methods disappear. This is an optimization called "inlining". In effect, the call to the method is replaced by the code of the method. It is a very important optimization, it for example makes properties as cheap as using public fields. But a consequence it that you'll get the assembly of the calling method. In your case Main(). So you'll see assembly A and not B.
This is not something you ought to tinker with, avoid having a need for this. A method shouldn't be puzzled about what assembly it lives in, you could for example use the typeof(T).Assembly property where T is the class in which the method lives.
You can disable the inlining optimization, you do so with an attribute:
using System.Runtime.CompilerServices;
...
[MethodImpl(MethodImplOptions.NoInlining)]
public void Foo() { }
try this sample ,
public static object ExecuteAssemblyMethod(string methodName,Type assemblyObj, object[] arguments)
{
object result = null;
try
{
object ibaseObject = Activator.CreateInstance(assemblyObj);
result = assemblyObj.InvokeMember(methodName, BindingFlags.Default | BindingFlags.InvokeMethod, null, ibaseObject, arguments);
}
catch (ReflectionTypeLoadException emx)
{
result = null;
return result;
}
catch (Exception ex)
{
result = null;
return result;
}
return result;
}
Usage:-
Assembly assemb = Assembly.LoadFile(#"D:\TEMP\TestClassLibrary_new.dll");
Type testType = assemb.GetType("TestClassLibrary.Class1");
object[] param = new object[] {"akshay" };
object result = LoadAssembly.ExecuteAssemblyMethod("GetName", testType, param);
Related
this code works for me but there must be a better way to accomplish the same thing. The method Search exists in multiple namespaces. the correct namespace to use depends on code that is irrelevant just like some of the other code displayed. any thoughts on rewritting this better?
example namespaces used
MTG.Sites.Test1
MTG.Sites.Test2
static public async Task<List<Card>> Search(string sNamespace)
{
List<Card> rawCards = null;
try
{
Type t = Assembly.GetExecutingAssembly().GetType($"MTG.Sites.{sNamespace}");
if (t != null)
{
dynamic classInstance = Activator.CreateInstance(t);
rawCards = await classInstance.Search(httpClient);
}
}
catch(Exception ex)
{
log.Error(ex);
}
return rawCards;
}
the code i want to improve is the use of Assembly.GetExecutingAssembly().GetType("");
the short answer is to use AutoFac. the longer answer is a complete rewrite of how the code is used. i essentially need to register each Search class that exists in each Namespace that contains one. then using the AutoFac examples (AttributeMetadataExample & AspNetCoreExample), i was able to eliminate the need for reflection to find the Search method in each Namespace (passed as a string to GetType)
I am working on a Visual Studio extension and one of it's functions creates a new app domain and load an assembly into that app domain. Then it runs some functions in the app domain. What I'd like to do, and am not sure if it's possible, is have my extension attach a debugger to the code running in the new app domain so when that code fails, I can actually see what's going on. Right now I'm flying blind and debugging the dynamical loaded assembly is a pain.
So I have a class that creates my app domain something like this:
domain = AppDomain.CreateDomain("Test_AppDomain",
AppDomain.CurrentDomain.Evidence,
AppDomain.CurrentDomain.SetupInformation);
And then creates an object like this:
myCollection = domain.CreateInstanceAndUnwrap(
typeof(MyCollection).Assembly.FullName,
typeof(MyCollection).FullName,
false,
BindingFlags.Default,
null,
new object[] { assemblyPath }, null, null);
MyCollection does something like this in it's constructor:
_assembly = Assembly.LoadFrom(assemblyPath);
So now that assembly has been loaded into Test_AppDomain since the MyCollection object was created in that domain. And it's that loaded assembly that I need to be able to attach the debugger to.
At some point myCollection creates an instance of an object and hooks up some events:
currentObject = Activator.CreateInstance(objectType) as IObjectBase;
proxy.RunRequested += (o, e) => { currentObject?.Run(); };
And basically where I have the handler for RunRequested and it runs currentObject?.Run(), I want to have a debugger attached, although it probably wouldn't be a problem (and may actually work better) if the debugger was attached earlier.
So is there a way to achieve this? Is it possible to programmatically attach a debugger when the user triggers the event that will lead to the Run function of the object created in the new AppDomain being called? How do I get the debugger attached to that (and not the extension itself)?
I tried something like this:
var processes = dte.Debugger.LocalProcesses.Cast<EnvDTE.Process>();
var currentProcess = System.Diagnostics.Process.GetCurrentProcess().Id;
var process = processes.FirstOrDefault(p => p.ProcessID == currentProcess);
process?.Attach();
But it seems the id from System.Diagnostics.Process.GetCurrentProcess().Id doesn't exist within LocalProcesses?
Even though you likely have already moved on, I found the problem very fascinating (and related to what I've been researching to blog about). So I gave it a shot as an experiment - I wasn't sure how you intended to trigger the Run() method with events (and even if it was material for your use case) so I opted for a simple method call.
Injecting Debugger.Launch()
as a PoC I ended up IL-emitting a derived class and injecting a debugger launch call before passing it onto dynamically loaded method:
public static object CreateWrapper(Type ServiceType, MethodInfo baseMethod)
{
var asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName($"newAssembly_{Guid.NewGuid()}"), AssemblyBuilderAccess.Run);
var module = asmBuilder.DefineDynamicModule($"DynamicAssembly_{Guid.NewGuid()}");
var typeBuilder = module.DefineType($"DynamicType_{Guid.NewGuid()}", TypeAttributes.Public, ServiceType);
var methodBuilder = typeBuilder.DefineMethod("Run", MethodAttributes.Public | MethodAttributes.NewSlot);
var ilGenerator = methodBuilder.GetILGenerator();
ilGenerator.EmitCall(OpCodes.Call, typeof(Debugger).GetMethod("Launch", BindingFlags.Static | BindingFlags.Public), null);
ilGenerator.Emit(OpCodes.Pop);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.EmitCall(OpCodes.Call, baseMethod, null);
ilGenerator.Emit(OpCodes.Ret);
/*
* the generated method would be roughly equivalent to:
* new void Run()
* {
* Debugger.Launch();
* base.Run();
* }
*/
var wrapperType = typeBuilder.CreateType();
return Activator.CreateInstance(wrapperType);
}
Triggering the method
Creating a wrapper for loaded method seems to be as easy as defining a dynamic type and picking a correct method from target class:
var wrappedInstance = DebuggerWrapperGenerator.CreateWrapper(ServiceType, ServiceType.GetMethod("Run"));
wrappedInstance.GetType().GetMethod("Run")?.Invoke(wrappedInstance, null);
Moving on to AppDomain
The above bits of code don't seem to care much for where the code will run, but when experimenting I discovered that I'm able to ensure the code is in correct AppDomain by either leveraging .DoCallBack() or making sure that my Launcher helper is created with .CreateInstanceAndUnwrap():
public class Program
{
const string PathToDll = #"..\..\..\ClassLibrary1\bin\Debug\ClassLibrary1.dll";
static void Main(string[] args)
{
var appDomain = AppDomain.CreateDomain("AppDomainInMain", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
appDomain.DoCallBack(() =>
{
var launcher = new Launcher(PathToDll);
launcher.Run();
});
}
}
public class Program
{
const string PathToDll = #"..\..\..\ClassLibrary1\bin\Debug\ClassLibrary1.dll";
static void Main(string[] args)
{
Launcher.RunInNewAppDomain(PathToDll);
}
}
public class Launcher : MarshalByRefObject
{
private Type ServiceType { get; }
public Launcher(string pathToDll)
{
var assembly = Assembly.LoadFrom(pathToDll);
ServiceType = assembly.GetTypes().SingleOrDefault(t => t.Name == "Class1");
}
public void Run()
{
var wrappedInstance = DebuggerWrapperGenerator.CreateWrapper(ServiceType, ServiceType.GetMethod("Run"));
wrappedInstance.GetType().GetMethod("Run")?.Invoke(wrappedInstance, null);
}
public static void RunInNewAppDomain(string pathToDll)
{
var appDomain = AppDomain.CreateDomain("AppDomainInLauncher", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation);
var launcher = appDomain.CreateInstanceAndUnwrap(typeof(Launcher).Assembly.FullName, typeof(Launcher).FullName, false, BindingFlags.Public|BindingFlags.Instance,
null, new object[] { pathToDll }, CultureInfo.CurrentCulture, null);
(launcher as Launcher)?.Run();
}
}
One way to get around this is to generate another assembly with a function that will take in a MethodInfo object and simply call System.Diagnostics.Debugger.Launch() and then the given MethodInfo, and then all you have to do is unwrap that assembly's function call it with whatever Method's Info you want to start the actual domain in, and your good it will enable the debugger and then call the method you want it to start in.
This is what im doing:
byte[] bytes = File.ReadAllBytes(#Application.StartupPath+"/UpdateMainProgaramApp.exe");
Assembly assembly = Assembly.Load(bytes);
// load the assemly
//Assembly assembly = Assembly.LoadFrom(AssemblyName);
// Walk through each type in the assembly looking for our class
MethodInfo method = assembly.EntryPoint;
if (method != null)
{
// create an istance of the Startup form Main method
object o = assembly.CreateInstance(method.Name);
// invoke the application starting point
try
{
method.Invoke(o, null);
}
catch (TargetInvocationException e)
{
Console.WriteLine(e.ToString());
}
}
The problem is that it throws that TargetInvocationException - it finds that the method is main, but it throws this exception since on this line:
object o = assembly.CreateInstance(method.Name);
o is remaining null. So I dug a bit into that stacktrace and the actual error is this:
InnerException = {"SetCompatibleTextRenderingDefault should be called before creating firstfirst IWin32Window object in the program"} (this is my translation since it gives me the stacktrace in half hebrew half english since my windows is in hebrew.)
What am i doing wrong?
The entry point method is static, so it should be invoked using a null value for the "instance" parameter. Try replacing everything after your Assembly.Load line with the following:
assembly.EntryPoint.Invoke(null, new object[0]);
If the entry point method is not public, you should use the Invoke overload that allows you to specify BindingFlags.
if you check any WinForm application Program.cs file you'll see there always these 2 lines
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
you need to call as well them in your assembly. At least this is what your exception says.
How about calling it in it's own process?
We have encountered some strange things while calling reflected generic delegates. In some cases with attatched debuger we can make impossible call, while without debugger we cannot catch any exception and application fastfails.
Here is the code:
using System;
using System.Windows.Forms;
using System.Reflection;
namespace GenericDelegate
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private delegate Class2 Delegate1();
private void button1_Click(object sender, EventArgs e)
{
MethodInfo mi = typeof (Class1<>).GetMethod("GetClass", BindingFlags.NonPublic | BindingFlags.Static);
if (mi != null)
{
Delegate1 del = (Delegate1) Delegate.CreateDelegate(typeof (Delegate1), mi);
MessageBox.Show("1");
try
{
del();
}
catch (Exception)
{
MessageBox.Show("No, I can`t catch it");
}
MessageBox.Show("2");
mi.Invoke(null, new object[] {});//It's Ok, we'll get exception here
MessageBox.Show("3");
}
}
class Class2
{
}
class Class1<T> : Class2
{
internal static Class2 GetClass()
{
Type type = typeof(T);
MessageBox.Show("Type name " + type.FullName +" Type: " + type + " Assembly " + type.Assembly);
return new Class1<T>();
}
}
}
}
There are two problems:
Behavior differs with debugger and without
You cannot catch this error without debugger by clr tricks. It's just not the clr exception. There are memory acces vialation, reading zero pointer inside of internal code.
Use case:
You develop something like plugins system for your app. You read external assembly, find suitable method in some type, and execute it. And we just forgot about that we need to check up is the type generic or not. Under VS (and .net from 2.0 to 4.0) everything works fine. Called function does not uses static context of generic type and type parameters. But without VS application fails with no sound. We even cannot identify call stack attaching debuger.
Tested with .net 4.0
The question is why VS catches but runtime do not?
Well, yes, that doesn't behave very gracefully. It is partly fixed in .NET 4.0, the CreateDelegate() method returns null. That still doesn't behave gracefully when the JIT optimizer is enabled though, it assumes that CreateDelegate cannot return null and doesn't perform a null check. You'll get a FatalExecutionEngineError instead of NRE, an exception that is no longer catchable in 4.0
I'd recommend you report the defect to connect.microsoft.com. Not so sure they'll take you seriously, I don't know what their policy is when you intentionally bypass the safeguards. It could well be that they consider the FEEE good enough. The workaround is obvious, only call methods of concrete types.
I'm not sure I follow exactly what you want to do, but if you put your code in a try/catch block there certainly is an exception thrown.
Just tested under VS 2008
VS does not catch exception at all.
Here is modified test case
static class Program
{
private delegate Foo Delegate1();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
MethodInfo mi = typeof(Bar<>).GetMethod("GetClass", BindingFlags.NonPublic | BindingFlags.Static);
if (mi != null)
{
var del = (Delegate1)Delegate.CreateDelegate(typeof(Delegate1), mi);
MessageBox.Show("1");
try
{
del();
//mi.Invoke(null, BindingFlags.NonPublic | BindingFlags.Static, null, null,CultureInfo.InvariantCulture);
}
catch (Exception)
{
MessageBox.Show("No, I can`t catch it");
}
MessageBox.Show("2");
mi.Invoke(null, new object[] { });//It's Ok, we'll get exception here
MessageBox.Show("3");
}
}
class Foo
{
}
class Bar<T> : Foo
{
internal static Foo GetClass()
{
Type type = typeof(T);
MessageBox.Show("Type name " + type.FullName + " Type: " + type + " Assembly " + type.Assembly);
return new Bar<T>();
}
}
}
but if you comment del() und uncomment mi.Invoke() you will get nice exception
true.
Late bound operations cannot be
performed on types or methods for
which ContainsGenericParameters is
true
I don't think that the code you posted actually works under the .net framework vers. 2.0 to 4.0. Static methods can be called AFAIK only on closed generic types, not on opened generic types. So you'll have to provide a type parameter. As for the debugger, try to launch your process outside Visual Studio and then attach the VS-Debugger. This time you'll get the expected exception right away.
I have an Excel add-in with a class module. I want to instantiate the class module in C# and call a method on it. How do I do that?
If you really need access to an instance of the class, you could do the following:
Generate a type library for a COM interface that you want to expose from your VBA class (e.g. IMyComInterface)
Add a reference to this type library in your VBA project
Implement the interface in your VBA class module - e.g. MyVbaClass (use the Implements keyword):
Option Explicit
Implements IMyComInterface
Private Sub IMyComInterface_SomeMethod(...)
...
End Sub
...
Reference the same type library in your C# project
Create a ComVisible C# class with a method that accepts a reference to the VBA interface instance. Something like:
public class MyVbaLoader
{
public IMyComInterface MyComInterface
{
get { return myComInterface; }
set { myComInterface = value; }
}
}
Write a "factory" method in a VBA standard module, that takes an object as a ByRef parameter. This object should assume the object passed as an argument has a property "MyComInterface" and should set this property to a new instance of the VBA class MyClass.
Public Sub MyFactoryMethod(MyVbaLoader As Object)
Dim objClass As MyVbaClass
Set objClass = New MyVbaClass
... any initialization of objClass here ...
' Pass a reference to the VBA class to the C# object MyVbaLoader
MyVbaLoader.MyComInterface = objClass
End Sub
Call the factory method from your C# code. Assuming you have opened the workbook and have a refence "workbook" in your VBA code, the code will look something like:
MyVbaLoader loader = new MyVbaLoader();
workbook.Application.Run("MyModule.MyFactoryMethod", loader, Type.Missing, ... , Type.Missing);
// we now have a reference to the VBA class module in loader.MyComInterface
// ...
As you can see, it's rather complex. Without more detail of the problem you're trying to solve it's difficult to say whether this complexity is justified, or whether there's a simpler solution.
If the above isn't clear, let me know and I'll try to clarify.
Basically you can't return a value from a VBA macro called from your C# code using Application.Run, so you have to resort to passing an object by value that has a method or property that can be called from VBA to set the instance.
VBA class modules have only two instancing modes: private, and public-not-creatable. So, you can't even instantiate them in another VB(A) project, let alone from C#.
However, there's nothing to stop you having a standard module that acts as a class factory. So, if your class module is Foo then you can have a method in a standard module called NewFoo that instantiates a new Foo for you and returns it to the caller. The Foo object would obviously have to be public-not-creatable.
[Your NewFoo method can take parameters, so you can simulate parameterized constructors, which aren't available in VBA.]
EDIT: detail on how to call VBA function (in a standard module) from C# and get the return value using Application.Run.
private static object RunMacro(Excel.Application excelApp, string macroName, object[] parameters)
{
Type applicationType = excelApp.GetType();
ArrayList arguments = new ArrayList();
arguments.Add(macroName);
if (parameters != null)
arguments.AddRange(parameters);
try
{
return applicationType.InvokeMember("Run", BindingFlags.Default | BindingFlags.InvokeMethod, null, excelApp, arguments.ToArray());
}
catch (TargetInvocationException ex)
{
COMException comException = ex.InnerException as COMException;
if (comException != null)
{
// These errors are raised by Excel if the macro does not exist
if ( (comException.ErrorCode == -2146827284)
|| (comException.ErrorCode == 1004))
throw new ApplicationException(string.Format("The macro '{0}' does not exist.", macroName), ex);
}
throw ex;
}
}
Note that you could omit all that try...catch stuff - all it's doing is handling the specific error where the macro does not exist and raising a more meaningful exception in that case.
The object returned from the function can be cast to whatever type you need. For example:
object o = RunMacro(excelApp, "MyModule.MyFunc", new object[] { "param1", 2 });
if (o is string)
{
string s = (string) o;
Console.WriteLine(s);
}
Assuming that the function actually returns an instance of your VBA-defined class object, then you can call the methods of that object in the same way, again using InvokeMember:
object o = RunMacro(excelApp, "MyModule.MyFunc", new object[] { "param1", 2 });
// assume o is an instance of your class, and that it has a method called Test that takes no arguments
o.GetType().InvokeMember("Run", BindingFlags.Default | BindingFlags.InvokeMethod, null, o, new string[] {"Test"});
If you're doing a lot of these calls, then obviously you can hide the ugly detail by creating wrapper methods to make the call for you.
Please note that I've typed most of this from memory, so I fully expect there to be syntax, logical and possibly even factual errors :-)