.NET Fetch all exception in 3rd party app - c#

We are integrating with a 3rd party application by supplying a file they can import. The file contains many properties(+100) , not all of them are mandatory, we only need a few. However, the application keeps crashing(gently, with an alert due to big try catch) with 'object not set to a reference ...' without a stacktrace. So at some place the 3rd party app is not verifying some optional parameters on nulls causing the crash. Searching what property is searching for a needle in a haystack.
Is it possible somehow to monitor all exceptions of an application we don't have the source of even if they are caught? This so we can get the stacktrace and check with Ilspy what property is causing the problem.
The 3rd party app is from a relatively big company. We cannot just communicate with their developers.

You could try:
Assembly otherAssembly = typeof(/* a class of the other assembly */).Assembly;
AppDomain.CurrentDomain.FirstChanceException += (sender, fceea) =>
{
AppDomain domain = (AppDomain)sender;
var method = fceea.Exception.TargetSite;
var declaringType = method.DeclaringType;
var assembly = declaringType.Assembly;
if (assembly == otherAssembly)
{
// Log the stacktrace of the Exception, or whatever
// you want
}
};
This will let you see all the Exceptions (even those catched). You have to put this code where the program starts (or even in other places it is ok, but try to not execute it multiple times, because the event is AppDomain-wide)
Note that considering how the stack trace inside an exception is handled, perhaps it's better to:
if (assembly == otherAssembly)
{
// Log the stacktrace st of the Exception, or whatever
// you want
string st = new StackTrace(1, true).ToString();
}
so that you can see the full stack trace.
Now, as I've suggested you, you could write a small Console app/Winforms app, Add Reference to the other exe (yes, you can add as a reference another .exe if that is written in .NET :-) ), and in your Main do something like:
var otherAssembly = typeof(/* Some type from the other assembly */).Assembly;
// We look all the classes in an assembly for a static Main() that has
// the "right" signature
var main = (from x in otherAssembly.GetTypes()
from y in x.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)
where y.Name == "Main" && (y.ReturnType == typeof(int) || y.ReturnType == typeof(void)) && y.GetGenericArguments().Length == 0
let parameters = y.GetParameters()
where parameters.Length == 0 || (parameters.Length == 1 && parameters[0].ParameterType == typeof(string[]))
select y).Single();
if (main.GetParameters().Length == 0)
{
// static Main()
main.Invoke(null, null);
}
else
{
// static Main(string[] args)
// Note that new string[0] is the string[] args!
// You can pass *your* string[] args :-)
// Or build one however you want
main.Invoke(null, new object[] { new string[0] });
}
to invoke the other Main(). Clearly before doing this, you have to setup the FirstChanceException handler

Related

Does the inbuilt Ninject assembly loaders have error handling

I've written a plugin manager so
public class WeinCadPluginManager : NinjectModule, IEnableSerilog
{
public override void Load()
{
var codeBaseUrl = new Uri(Assembly.GetExecutingAssembly().CodeBase);
var codeBasePath = Uri.UnescapeDataString(codeBaseUrl.AbsolutePath);
var dirPath = Path.GetDirectoryName(codeBasePath);
Debug.Assert(dirPath != null, "dirPath != null");
var path = dirPath;
var types = Directory
.GetFiles(path, "*.dll")
.Select (Assembly.LoadFile)
.SelectMany (assembly =>
{
try
{
return assembly.GetExportedTypes();
}
catch (Exception e)
{
this.Serilog()
.Error
(e, "Failed to load assembly {assembly}", assembly);
return new Type[]
{
};
}
})
.Where(type=>typeof(IWeinCadPlugin).IsAssignableFrom(type))
.ToList();
foreach (var assembly in types)
{
Kernel.Bind<IWeinCadPlugin>().To(assembly).InSingletonScope();
}
}
}
Now this pretty much duplicates
Kernel.Load("*.dll")
except if there are any errors exporting the types from an assembly then Kernel.Load crashes with no error handling possible. I wouldn't want the failure to load a single plugin assembly to crash my app. Is my solution the only viable way or does Ninject have some error handling available?
I think a try { } catch { } around each Kernel.Load("specificplugin.dll") should suffice.
You would still have to find all the assemblies yourself, but would have to write less code.
var codeBaseUrl = new Uri(Assembly.GetExecutingAssembly().CodeBase);
var codeBasePath = Uri.UnescapeDataString(codeBaseUrl.AbsolutePath);
var dirPath = Path.GetDirectoryName(codeBasePath);
var dllPaths = Directory.GetFiles(dirpath, "*.dll");
foreach(string dllPath in dllPaths)
{
try
{
kernel.Load(dllPath);
}
catch (Exception e)
{
this.Serilog()
.Error(e, "Failed to load assembly {assembly}", assembly);
}
}
Drawback: ninject must be adding the binding to the kernel either on .Bind() or on .To(..), because all the rest of the fluent syntax methods are optional. So if there would be an exception in the .When(), .InScope(),.. any other optional method, you would be left with a non-complete binding and thus, most likely, a faulty software.
(However i suspect that most errors will not materialize when creating the binding, but rather when activating the binding. And you are not protected against that.)
As far as i know there is no way to remove bindings from ninject once you've added them. Except for the .Rebind() - but that is always replacing a binding with a different one.
So no, i don't think there's something like "rollback on exception".
So we must be looking for an alternative solution. There is one: child kernels. https://github.com/ninject/ninject.extensions.childkernel
Load each plugin into it's own child kernel. in case the fooplugin.dll load fails, dispose the child kernel. In case it works.. well you're good to go! :)
This also has the advantage that plugin's can't influence one another. Imagine two plugins would do IBindingRoot.Bind<string>().ToConstant("some constant") ;-)
(please note that i haven't tested whether disposing the child kernel before the parent kernel works "as expected", so you should test it out first. Simple enough, right?)

How to figure out dynamically all methods with custom attribute

I have a simple challenge. I dynamically need to figure out all methods with a specific attribute in C#. I'm going to load the assemblies dynamically from another application and need to find out the exact methods. The assemblies look like the followings:
Base.dll:
Class Base
{
[testmethod]
public void method1()
...
}
Derived.dll:
Class Derived:Base
{
[testmethod]
public void method2()
}
Now in 3rd application I dynamically like to load the above mentioned dlls and find out testmethods.
If I load Base.dll, I need to get testmethod1. If I load Drived.dll, should I get testmethod1 and testmethod2.
I found some code online which helped me to load dlls dynamically:
List<Assembly> a = new List<Assembly>();
string bin = #"Bin-Folder";
DirectoryInfo oDirectoryInfo = new DirectoryInfo(bin);
//Check the directory exists
if (oDirectoryInfo.Exists)
{
//Foreach Assembly with dll as the extension
foreach (FileInfo oFileInfo in oDirectoryInfo.GetFiles("*.dll", SearchOption.AllDirectories))
{
Assembly tempAssembly = null;
//Before loading the assembly, check all current loaded assemblies in case talready loaded
//has already been loaded as a reference to another assembly
//Loading the assembly twice can cause major issues
foreach (Assembly loadedAssembly in AppDomain.CurrentDomain.GetAssemblies())
{
//Check the assembly is not dynamically generated as we are not interested in these
if (loadedAssembly.ManifestModule.GetType().Namespace != "System.Reflection.Emit")
{
//Get the loaded assembly filename
string sLoadedFilename =
loadedAssembly.CodeBase.Substring(loadedAssembly.CodeBase.LastIndexOf('/') + 1);
//If the filenames match, set the assembly to the one that is already loaded
if (sLoadedFilename.ToUpper() == oFileInfo.Name.ToUpper())
{
tempAssembly = loadedAssembly;
break;
}
}
}
//If the assembly is not aleady loaded, load it manually
if (tempAssembly == null)
{
tempAssembly = Assembly.LoadFrom(oFileInfo.FullName);
}
a.Add(tempAssembly);
}
The above code is working fine and I can load the DLLs. However when I use the following code to find out the right method, it doesn't return any desired results. I'm wondering which part is not correct. The following code lists about 145 methods but non of them is one which I'm looking for.
public static List<string> GetTests(Type testClass)
{
MethodInfo[] methodInfos = testClass.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance);
Array.Sort(methodInfos,
delegate(MethodInfo methodInfo1, MethodInfo methodInfo2)
{ return methodInfo1.Name.CompareTo(methodInfo2.Name); });
foreach (MethodInfo mi in methodInfos)
{
foreach (var item in mi.GetCustomAttributes(false))
{
if
(item.ToString().CompareTo("Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute") == 0)
result.Add(mi.Name);
}
}
return result;
}
Can any one help me with this issue?
I'm not sure why but I tried to instantiate objects from above mentioned classes (Base and Derived) and the above mentioned code returns the right results. However as mentioned above if I don't have an object from base and derived classes and try to figure out the methods based on the types, it doesn't return the desired results.
Thx
The simplest approach is to use MethodInfo.IsDefined - quite possibly with LINQ as well:
var testMethods = from assembly in assemblies
from type in assembly.GetTypes()
from method in type.GetMethods()
where method.IsDefined(typeof(TestMethodAttribute))
select method;
foreach (var method in testMethods)
{
Console.WriteLine(method);
}
(I'd do all the sorting with LINQ as well. Obviously you can tune the GetMethods call etc to only return instance methods, for example.)
It's not entirely clear to me why your current approach doesn't work or why it does work when you've created instances - but without a short but complete example demonstrating the problem, it would be hard to diagnose it further. I'd definitely start with the code above :)

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;
}

How to compile C# DLL on the fly, Load, and Use

A) compiling C# EXE's and DLL's on the fly are relatively easy.
B) Executing an EXE means that a new application is run. Loading a DLL means that methods and functions can be used in cases that may be shared between applications or projects.
Now, the quickest and easiest way to compile your EXE (or with mild modifications, DLL) can be found from the MSDN or for your convenience:
private bool CompileCSharpCode(string script)
{
lvErrors.Items.Clear();
try
{
CSharpCodeProvider provider = new CSharpCodeProvider();
// Build the parameters for source compilation.
CompilerParameters cp = new CompilerParameters
{
GenerateInMemory = false,
GenerateExecutable = false, // True = EXE, False = DLL
IncludeDebugInformation = true,
OutputAssembly = "eventHandler.dll", // Compilation name
};
// Add in our included libs.
cp.ReferencedAssemblies.Add("System.dll");
cp.ReferencedAssemblies.Add("System.Windows.Forms.dll");
cp.ReferencedAssemblies.Add("Microsoft.VisualBasic.dll");
// Invoke compilation. This works from a string, but you can also load from a file using FromFile()
CompilerResults cr = provider.CompileAssemblyFromSource(cp, script);
if (cr.Errors.Count > 0)
{
// Display compilation errors.
foreach (CompilerError ce in cr.Errors)
{
//I have a listview to display errors.
lvErrors.Items.Add(ce.ToString());
}
return false;
}
else
{
lvErrors.Items.Add("Compiled Successfully.");
}
provider.Dispose();
}
catch (Exception e)
{
// never really reached, but better safe than sorry?
lvErrors.Items.Add("SEVERE! "+e.Message + e.StackTrace.ToString());
return false;
}
return true;
}
Now that you can compile on the fly, there are a few variances between how to load the DLL. Typically speaking, you would add it as a reference in Visual Studios to be compiled into the project. This is rather easy and you have probably done it many times over, but we want to use it in our current project, and we can't very well require the user to recompile the entire project every time they want to test out their new DLL. Therefor, I will simply discuss how one loads a library 'on the fly'. Another term here would by "programmatically". To do this, after a successful compile, we load up an Assembly as follows:
Assembly assembly = Assembly.LoadFrom("yourfilenamehere.dll");
If you have an AppDomain, you can try this:
Assembly assembly = domain.Load(AssemblyName.GetAssemblyName("yourfilenamehere.dll"));
Now that the lib is "referenced", we can open it up and use it. There are 2 ways to do this. One requires you to know if the method has parameters, another will check for you. I'll do the later, you can check the MSDN for the other.
// replace with your namespace.class
Type type = assembly.GetType("company.project");
if (type != null)
{
// replace with your function's name
MethodInfo method = type.GetMethod("method");
if (method != null)
{
object result = null;
ParameterInfo[] parameters = method.GetParameters();
object classInstance = Activator.CreateInstance(type, null);
if (parameters.Length == 0) // takes no parameters
{
// method A:
result = method.Invoke(classInstance, null);
// method B:
//result = type.InvokeMember("method", BindingFlags.InvokeMethod, null, classInstance, null);
}
else // takes 1+ parameters
{
object[] parametersArray = new object[] { }; // add parameters here
// method A:
result = method.Invoke(classInstance, parametersArray);
// method B:
//result = type.InvokeMember("method", BindingFlags.InvokeMethod, null, classInstance, parametersArray);
}
}
}
PROBLEM:
First compile works fine. First execution works fine. However, the recompile attempt will error, saying that your *.PDP (debugger database) is in use. I've heard some hints about marshaling, and AppDomains, but I haven't quite cleared up the problem. Recompile will only fail after the DLL has been loaded.
Current attempt at Marshaling && AppDomain:
class ProxyDomain : MarshalByRefObject
{
private object _instance;
public object Instance
{
get { return _instance; }
}
private AppDomain _domain;
public AppDomain Domain
{
get
{
return _domain;
}
}
public void CreateDomain(string friendlyName, System.Security.Policy.Evidence securityinfo)
{
_domain = AppDomain.CreateDomain(friendlyName, securityinfo);
}
public void UnloadDomain()
{
try
{
AppDomain.Unload(_domain);
}
catch (ArgumentNullException dne)
{
// ignore null exceptions
return;
}
}
private Assembly _assembly;
public Assembly Assembly
{
get
{
return _assembly;
}
}
private byte[] loadFile(string filename)
{
FileStream fs = new FileStream(filename, FileMode.Open);
byte[] buffer = new byte[(int)fs.Length];
fs.Read(buffer, 0, buffer.Length);
fs.Close();
return buffer;
}
public void LoadAssembly(string path, string typeName)
{
try
{
if (_domain == null)
throw new ArgumentNullException("_domain does not exist.");
byte[] Assembly_data = loadFile(path);
byte[] Symbol_data = loadFile(path.Replace(".dll", ".pdb"));
_assembly = _domain.Load(Assembly_data, Symbol_data);
//_assembly = _domain.Load(AssemblyName.GetAssemblyName(path));
_type = _assembly.GetType(typeName);
}
catch (Exception ex)
{
throw new InvalidOperationException(ex.ToString());
}
}
private Type _type;
public Type Type
{
get
{
return _type;
}
}
public void CreateInstanceAndUnwrap(string typeName)
{
_instance = _domain.CreateInstanceAndUnwrap(_assembly.FullName, typeName);
}
}
Errors on _instance = _domain.CreateInstanceAndUnwrap(_assembly.FullName, typeName); saying that my Assembly isn't serializable. Tried adding [Serializable] tag to my class with no luck. Still researching fixes.
Seems things can get a bit confusing when you can't see how they're being used, so here's making it easy?
private void pictureBox1_Click(object sender, EventArgs e)
{
pd.UnloadDomain();
if (CompileCSharpCode(header + tScript.Text + footer))
{
try
{
pd.CreateDomain("DLLDomain", null);
pd.LoadAssembly("eventHandler.dll", "Events.eventHandler");
pd.CreateInstanceAndUnwrap("Events.eventHandler"); // Assembly not Serializable error!
/*if (pd.type != null)
{
MethodInfo onConnect = pd.type.GetMethod("onConnect");
if (onConnect != null)
{
object result = null;
ParameterInfo[] parameters = onConnect.GetParameters();
object classInstance = Activator.CreateInstance(pd.type, null);
if (parameters.Length == 0)
{
result = pd.type.InvokeMember("onConnect", BindingFlags.InvokeMethod, null, classInstance, null);
//result = onConnect.Invoke(classInstance, null);
}
else
{
object[] parametersArray = new object[] { };
//result = onConnect.Invoke(classInstance, parametersArray);
//result = type.InvokeMember("onConnect", BindingFlags.InvokeMethod, null, classInstance, parametersArray);
}
}
}*/
//assembly = Assembly.LoadFrom(null);
}
catch (Exception er)
{
MessageBox.Show("There was an error executing the script.\n>" + er.Message + "\n - " + er.StackTrace.ToString());
}
finally
{
}
}
}
Once you have loaded a DLL into (the default appdomain of) a running process, the file on disk cannot be overwritten until the process is terminated. DLLs cannot be unloaded in managed code like they can be in unmanaged code.
You need to create a new appdomain in your host process and load the newly created DLL assembly into that appdomain. When you are ready to compile a new version of the DLL, you can dispose of the appdomain. This will unload the DLL from memory and release the lock on the DLL file, so that you can compile a new DLL to that same file. You can then construct a new appdomain to load the new DLL into.
The main hazard of using appdomains is that all calls across the appdomain boundary must be marshalled, much like an IPC or network RPC. Try to keep the interface of the objects you need to call across the appdomain boundary to a minimum.
You can also compile the assembly to memory, receiving a byte array or stream as the output, then load that assembly into the separate appdomain. This avoids creating debris on disk that will need to be deleted eventually.
Do not use compile to memory as a workaround for the file lock issue. The core issue is that assemblies cannot be removed from memory when they are loaded into the default appdomain of the process. You MUST create a new appdomain and load the DLL into that appdomain if you want to unload that assembly from memory later in the lifetime of the process.
Here's a rough outline of how to construct an object in the context of another appdomain:
var appdomain = AppDomain.CreateDomain("scratch");
byte[] assemblyBytes = // bytes of the compiled assembly
var assembly = appdomain.Load(assemblyBytes);
object obj = appdomain.CreateInstanceAndUnwrap(assembly.FullName, "mynamespace.myclass");
After this sequence, obj will contain a reference to a proxy that links to the actual object instance inside the appdomain. You can invoke methods on obj using reflection or typecast obj to a common interface type and call methods directly. Be prepared to make adjustments to support RPC marshalling of the method call parameters. (see .NET remoting)
When working with multiple appdomains, you have to be careful how you access types and assemblies because a lot of .NET functions default to operating in the current appdomain of the caller, which is usually not what you want when you have multiple appdomains. compilerResult.CompiledAssembly, for example, internally performs a Load of the generated assembly in the caller's appdomain. What you want is to load the assembly into your other appdomain. You have to do that explicitly.
Update:
In your recently added code snippet showing how you load your appdomain, this line is your problem:
_assembly = Assembly.LoadFrom(path);
That loads the DLL into the current appdomain (the caller's appdomain), not into the target appdomain (referenced by _domain in your example). You need to do use _domain.Load() to load the assembly into that appdomain.
if you have no need to debug, or don't mind to debug the "dynamic" code, with some missing information.
you can generate the code in memory.. this will allow you to compile the code several times.. but will not generate a .pdb
cp.GenerateInMemory = true;
in alternative if you have no need to able to locate the assembly on disk you can ask the compiler to dump all the code in the temp directory and generate a temp name for the dll (wich will always be unique)
cp.TempFiles = new TempFileCollection(Path.GetTempPath(), false);
//cp.OutputAssembly = "eventHandler.dll";
in both this cases to access the dll and it's types you can get it from the compiler results
Assembly assembly = cr.CompiledAssembly;
not explicitly loading is necessary
but if non of this situations apply and you must and a physical .dll with a .pdp in a known folder.. the only advice i can give you it to put a version number on the dll..
and in case you don't have a simple way to control the amount of times the dll was compiled you can always resort to a timestamp..
cp.OutputAssembly = "eventHandler"+DateTime.Now.ToString("yyyyMMddHHmmssfff")+".dll";
of course you must realize that every time you compile a new .dll will be loaded into memory and wont be unloaded unless you use separate app domains.. but that goes out of scope for this question..

C# Getting Parent Assembly Name of Calling Assembly

I've got a C# unit test application that I'm working on. There are three assemblies involved - the assembly of the C# app itself, a second assembly that the app uses, and a third assembly that's used by the second one.
So the calls go like this:
First Assembly ------> Second Assembly---------> Third Assembly.
What I need to do in the third assembly is get the name of the Fist Assembly that called the second assembly.
Assembly.GetExecutingAssembly().ManifestModule.Name
Assembly.GetCallingAssembly().ManifestModule.Name
returns the name of the Second assembly.
and
Assembly.GetEntryAssembly().ManifestModule.Name
return NULL
Does anybody know if there is a way to get to the assembly name of the First Assembly?
As per the other users demand here I put the code. This is not 100% code but follow of code like this.
namespace FirstAssembly{
public static xcass A
{
public static Stream OpenResource(string name)
{
return Reader.OpenResource(Assembly.GetCallingAssembly(), ".Resources." + name);
}
}
}
using FirstAssembly;
namespace SecondAssembly{
public static class B
{
public static Stream FileNameFromType(string Name)
{
return = A.OpenResource(string name);
}
}
}
and Test project method
using SecondAssembly;
namespace ThirdAssembly{
public class TestC
{
[TestMethod()]
public void StremSizTest()
{
// ARRANGE
var Stream = B.FileNameFromType("ValidMetaData.xml");
// ASSERT
Assert.IsNotNull(Stream , "The Stream object should not be null.");
}
}
}
I guess you should be able to do it like this:
using System.Diagnostics;
using System.Linq;
...
StackFrame[] frames = new StackTrace().GetFrames();
string initialAssembly = (from f in frames
select f.GetMethod().ReflectedType.AssemblyQualifiedName
).Distinct().Last();
This will get you the Assembly which contains the first method which was started first started in the current thread. So if you're not in the main thread this can be different from the EntryAssembly, if I understand your situation correctly this should be the Assembly your looking for.
You can also get the actual Assembly instead of the name like this:
Assembly initialAssembly = (from f in frames
select f.GetMethod().ReflectedType.Assembly
).Distinct().Last();
Edit - as of Sep. 23rd, 2015
Please, notice that
GetMethod().ReflectedType
can be null, so retrieving its AssemblyQualifiedName could throw an exception.
For example, that's interesting if one wants to check a vanilla c.tor dedicated only to an ORM (like linq2db, etc...) POCO class.
This will return the the initial Assembly that references your currentAssembly.
var currentAssembly = Assembly.GetExecutingAssembly();
var callerAssemblies = new StackTrace().GetFrames()
.Select(x => x.GetMethod().ReflectedType.Assembly).Distinct()
.Where(x => x.GetReferencedAssemblies().Any(y => y.FullName == currentAssembly.FullName));
var initialAssembly = callerAssemblies.Last();
It worked for me using this:
System.Reflection.Assembly.GetEntryAssembly().GetName()
Assembly.GetEntryAssembly() is null if you run tests from nunit-console too.
If you just want the name of the executing app then use:
System.Diagnostics.Process.GetCurrentProcess().ProcessName
or
Environment.GetCommandLineArgs()[0];
For nunit-console you would get "nunit-console" and "C:\Program Files\NUnit 2.5.10\bin\net-2.0\nunit-console.exe" respectively.
Try:
Assembly.GetEntryAssembly().ManifestModule.Name
This should be the assembly that was actually executed to start your process.
Not completely sure what you're looking for, especially as when running in the context of a unit test you'll wind up with:
mscorlib.dll
Microsoft.VisualStudio.TestPlatform.Extensions.VSTestIntegration.dll
(or something similar depending on your test runner) in the set of assemblies that lead to any method being called.
The below code prints the names of each of the assemblies involved in the call.
var trace = new StackTrace();
var assemblies = new List<Assembly>();
var frames = trace.GetFrames();
if(frames == null)
{
throw new Exception("Couldn't get the stack trace");
}
foreach(var frame in frames)
{
var method = frame.GetMethod();
var declaringType = method.DeclaringType;
if(declaringType == null)
{
continue;
}
var assembly = declaringType.Assembly;
var lastAssembly = assemblies.LastOrDefault();
if(assembly != lastAssembly)
{
assemblies.Add(assembly);
}
}
foreach(var assembly in assemblies)
{
Debug.WriteLine(assembly.ManifestModule.Name);
}
If you know the number of frame in the stack, you can use the StackFrame object and skip the number of previous frame.
// You skip 2 frames
System.Diagnostics.StackFrame stack = new System.Diagnostics.StackFrame(2, false);
string assemblyName = stack.GetMethod().DeclaringType.AssemblyQualifiedName;
But, if you want the first call, you need to get all frames and take the first. (see AVee solution)
How about Assembly.GetEntryAssembly()? It returns the main executable of the process.
Process.GetCurrentProcess().MainModule.ModuleName should also return about the same as the ManifestModule name ("yourapp.exe").
This works for getting the original assembly when using two assemblies in an NUnit test, without returning a NULL. Hope this helps.
var currentAssembly = Assembly.GetExecutingAssembly();
var callerAssemblies = new StackTrace().GetFrames()
.Select(x => x.GetMethod().ReflectedType.Assembly).Distinct()
.Where(x => x.GetReferencedAssemblies().Any(y => y.FullName == currentAssembly.FullName));
var initialAssembly = callerAssemblies.Last();

Categories