Pragmatically detect if if I forget to include an assembly - c#

SEE EDITS AT THE END.
I have an app that requires to user to install a 3rd party application for mine to run. I'd like to make my application provide a warning to the user if they have forgotten to install this 3rd party app instead of just quitting as a result of trying to access a missing assembly.
In C#, I'd like to check if an assembly is included.
I thought I could use:
object.ReferenceEquals(Oject, null)
But but object.ReferenceEquals does not like to take Type as an input.
I tried:
var type = (from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type2 in assembly.GetTypes()
where type2.Name == "Oject"
select type2);
if (type == null) {...}
But Visual Studio shows me that type can never be null.
So: Is there a simple way I can detect if I forgot to include an assembly before I access it (and the app just quits with no warning or message telling the user WHY it quit)?
I thought I could use
try
{
types = assembly.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
types = ex.Types.Where(p => p != null).ToArray();
}
from: Missing types using reflection on DLL but I'm doing this check from Program.cs that is a static class.
EDIT:
It turns out that I have the referenced assemblies but that these assemblies may not have the correct logic installed. So the references came along with all the binaries but since the 3rd party app was not installed, those binaries went bonkers when they could not reach their intended targets and it is those binaries that seem to be failings.
So... I still have this issue but it may explain I cannot "Catch" ??

An interesting fact about type loading in .NET. A type is loaded upon entering a method that uses it. An assembly is loaded when the first type is loaded. So, example code below (assuming "SomeType" is a type in the assembly you are looking for)
So by this logic you can have:
static void Main() {
if (CanLoad()) {
DoStuff();
} else {
Console.WriteLine("Some error message");
}
}
static void DoStuff() {
// Ok to reference SomeType here
}
private static bool CanLoad() {
try {
TryLoad();
return true;
} catch {
return false;
}
}
private static void TryLoad() {
Type t = typeof(SomeType);
}
With this code, what happens is this. Main calls CanLoad normally. CanLoad doesn't need any fancy type, so it happily starts up, and then attempts to call TryLoad. TryLoad, however, has a reference to "SomeType", so when the compiler attempts to enter that method, before executing any part of it, it attempts to load the assembly containing "SomeType". If this fails, the method throws a FileNotFoundException trying lo load it. We catch that in CanLoad and return false.
It's very important that neither "Main" nor "CanLoad" have any references to that assembly at all (even after the check), or they will crash before the check (remember, the loads are done when entering a method, not on reaching the line), but "DoStuff" can reference whatever it wants.

Related

Detecting when a DLL has raised an exception

OK, so I have the C# DLL method:
public void DeletePublisher(string strName)
{
try
{
if (_PublisherData.PublisherDictionary.ContainsKey(strName))
_PublisherData.PublisherDictionary.Remove(strName);
}
catch (Exception ex)
{
SimpleLog.Log(ex);
}
}
It works fine. If there is a exception it is detected and added to the log.
At the moment, the above is called via MFC in my C++ project using a wrapper:
bool CMSATools::DeletePublisher(CString strPublisher)
{
bool bDeleted = false;
if (m_pInterface != nullptr)
{
CComBSTR bstrPublisher = strPublisher.AllocSysString();
throw_if_fail(m_pInterface->DeletePublisher(bstrPublisher));
bDeleted = true;
}
return bDeleted;
}
They both work fine. The issue is that fact that the CPP method currently has no knowledge of the C# method having failed. Now, in this particular instance I know I could change the signature of the DLL method to return false for a exception failure occurring and examine that return value in the CPP file.
But, in other instances I am already using the return value and thus, it would seem for consistency to me, that I pass in a bool bExceptionRaised parameter instead to my methods in the DLL.
That way, I can test that value when the method seemed to complete and if it is false act accordingly.
At the moment my application doesn't realise that an exception occurred and that is confusion.
Can I assume that either of these methodologies are the simplest approach to what I am trying to detect?
Update
Based on the answer provided I have tried to follow this tutorial and I am getting confused. I have tried to follow it and I can't create a CLR DLL and build it that is a bridge to my C# DLL file.
Whilst I appreciate the answer I feel like it is breaking up everything I have worked on since the C# DLL already handles and logs it's exceptions. Whilst I would like to learn how to build this bridge for the future, I still think perhaps at the point in time just changing my signatures is sufficient. Either way, my attempt a basic build of a bridge is failing.
Use a C++/CLI wrapper for the access of the managed component.
With C++/CLI you can directly use the C# component can catch the managed exception and you can throw a native exception or even return true or false... whatever you want.
void DoManagedStuff(SOMEDATA somedata)
{
try
{
auto sm = ConvertToSomeDataToManaged(somedata);
CallManagedCode(sm);
}
catch (Exception^ e)
{
throw CMyNativeException();
}
}

Load a .NET assembly from the application's resources and run It from memory, but without terminating the main/host application

INTRODUCTION
I'm using the next C# code example shared by David Heffernan' for loading a .NET assembly from the application's resources and run It from memory:
Assembly a = Assembly.Load(bytes);
MethodInfo method = a.EntryPoint;
if (method != null)
method.Invoke(a.CreateInstance(method.Name), null);
Here I just share an adaptation in VB.NET that I am using too:
Public Shared Sub Execute(ByVal resource As Byte(), ByVal parameters As Object())
Dim ass As Assembly = Assembly.Load(resource)
Dim method As MethodInfo = ass.EntryPoint
If (method IsNot Nothing) Then
Dim instance As Object = ass.CreateInstance(method.Name)
method.Invoke(instance, parameters)
If (instance IsNot Nothing) AndAlso (instance.GetType().GetInterfaces.Contains(GetType(IDisposable))) Then
DirectCast(instance, IDisposable).Dispose()
End If
instance = Nothing
method = Nothing
ass = Nothing
Else
Throw New EntryPointNotFoundException("Entrypoint not found in the specified resource. Are you sure it is a .NET assembly?")
End If
End Sub
PROBLEM
The problem is that if the executed assembly has an application exit instruction, then it also terminates my main/host application too. For example:
ConsoleApplication1.exe compiled from this source-code:
Module Module1
Sub Main()
Environment.Exit(0)
End Sub
End Module
When I add ConsoleApplication1.exe to the application resources, and then I load it and run it with the Assembly.Load methodology, it also terminates my application because the call to Environment.Exit.
QUESTION
How can I prevent this, without modifying the source code of the executed assembly?.
Maybe I could do somehting like associate a kind of exit event handler to the executed assembly to handle/ignore it properly?. What are my options at this point?.
PS: For me no matter if the given solution is written in C# or VB.NET.
Please note two things, the first is that my intention is to resolve this issue in an automated/abstracted way, I mean the final result should just need to call the "Execute" method passing the resource and the arguments and don't worry about the rest; and secondly, I want the executed assembly to be ran synchronuslly, not async... in case of that could matter for a possible solution.
Update: My first solution doesn't work for assemblies contained in the resources of a program like OP asked; instead it loads it from the disk. The solution for loading from a byte array will follow (in progress). Note that the following points apply to both solutions:
As the Environment.Exit() method throws an exception because of lack of permissions, execution of the method will not continue after it is encountered.
You're going to need all the permissions that your Main method needs, but you can find those quickly by just typing in "Permission" in intellisense, or by checking the SecurityException's TargetSite property (which is an instance of MethodBase and will tell you which method failed).
If another method in your Main needs the UnmanagedCode permission, you're out of luck, at least using this solution.
Note that I found that the UnmanagedCode permission was the one that Environment.Exit() needs purely by trial and error.
Solution 1: When the assembly is on the disk
Okay, here's what I've found so far, bear with me. We're going to create a sandboxed AppDomain:
AppDomainSetup adSetup = new AppDomainSetup();
adSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
// This is where the main executable resides. For more info on this, see "Remarks" in
// https://msdn.microsoft.com/en-us/library/system.appdomainsetup.applicationbase(v=vs.110).aspx#Anchor_1
PermissionSet permission = new PermissionSet(PermissionState.None);
// Permissions of the AppDomain/what it can do
permission.AddPermission(new SecurityPermission(SecurityPermissionFlag.AllFlags & ~SecurityPermissionFlag.UnmanagedCode));
// All SecurityPermission flags EXCEPT UnmanagedCode, which is required by Environment.Exit()
// BUT the assembly needs SecurityPermissionFlag.Execution to be run;
// otherwise you'll get an exception.
permission.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
permission.AddPermission(new UIPermission(PermissionState.Unrestricted));
// the above two are for Console.WriteLine() to run, which is what I had in the Main method
var assembly = Assembly.LoadFile(exePath); // path to ConsoleApplication1.exe
var domain = AppDomain.CreateDomain("SomeGenericName", null, adSetup, permission, null); // sandboxed AppDomain
try
{
domain.ExecuteAssemblyByName(assembly.GetName(), new string[] { });
}
// The SecurityException is thrown by Environment.Exit() not being able to run
catch (SecurityException e) when (e.TargetSite == typeof(Environment).GetMethod("Exit"))
{
Console.WriteLine("Tried to run Exit");
}
catch (SecurityException e)
{
// Some other action in your method needs SecurityPermissionFlag.UnmanagedCode to run,
// or the PermissionSet is missing some other permission
}
catch
{
Console.WriteLine("Something else failed in ConsoleApplication1.exe's main...");
}
Solution 2: When the assembly is a byte array
Warning: cancerous solution follows.
When changing my solution to load a byte array, OP and I discovered a weird exception file not found exception: even if you pass in a byte array to Assembly.Load(), domain.ExecuteAssemblyByName() still searches the disk for the assembly, for some weird reason. Apparently we weren't the only ones with the issue: Loading Byte Array Assembly.
First, we have a Helper class:
public class Helper : MarshalByRefObject
{
public void LoadAssembly(Byte[] data)
{
var a = Assembly.Load(data);
a.EntryPoint.Invoke(null, null);
}
}
which as you can see, loads the assembly using Assembly.Load() and calls it's entry point. This is the code we'll be loading into the AppDomain:
AppDomainSetup adSetup = new AppDomainSetup();
adSetup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
// This is where the main executable resides. For more info on this, see "Remarks" in
// https://msdn.microsoft.com/en-us/library/system.appdomainsetup.applicationbase(v=vs.110).aspx#Anchor_1
PermissionSet permission = new PermissionSet(PermissionState.None);
// Permissions of the AppDomain/what it can do
permission.AddPermission(new SecurityPermission(SecurityPermissionFlag.AllFlags & ~SecurityPermissionFlag.UnmanagedCode));
// All SecurityPermission flags EXCEPT UnmanagedCode, which is required by Environment.Exit()
// BUT the assembly needs SecurityPermissionFlag.Execution to be run;
// otherwise you'll get an exception.
permission.AddPermission(new FileIOPermission(PermissionState.Unrestricted));
permission.AddPermission(new UIPermission(PermissionState.Unrestricted));
// the above two are for Console.WriteLine() to run, which is what I had in the Main method
var domain = AppDomain.CreateDomain("SomeGenericName", null, adSetup, permission, null); // sandboxed AppDomain
try
{
Helper helper = (Helper)domain.CreateInstanceAndUnwrap(typeof(Helper).Assembly.FullName, typeof(Helper).FullName);
// create an instance of Helper in the new AppDomain
helper.LoadAssembly(bytes); // bytes is the in-memory assembly
}
catch (TargetInvocationException e) when (e.InnerException.GetType() == typeof(SecurityException))
{
Console.WriteLine("some kind of permissions issue here");
}
catch (Exception e)
{
Console.WriteLine("Something else failed in ConsoleApplication1.exe's main... " + e.Message);
}
Note that in the second solution, the SecurityException becomes a TargetInvocationException with it's InnerException property being a SecurityException. Unfortunately, this means that you cannot use e.TargetSite to see which method threw the exception.
Conclusion/Things to keep in mind
This solution isn't perfect. It would be much better to somehow go through the IL of the method and artificially remove the call to Environment.Exit().
All credits go to Kirill Osenkov - MSFT
I can successfully load the assembly into another AppDomain and call its entry point.
Environment.Exit always shuts down the hosting process.
Workaround for this would be, to return an int from Main of the loaded console application. Zero for success and other numbers for errors.
Instead of this:
Module Module1
Sub Main()
// your code
Environment.Exit(0)
End Sub
End Module
write: (I hope this is valid VB.NET :-))
Module Module1
Function Main() As Integer
// your code
Return 0 // 0 == no error
End Function
End Module
Demo - C#
class Program
{
static void Main(string[] args)
{
Launcher.Start(#"C:\Users\path\to\your\console\app.exe");
}
}
public class Launcher : MarshalByRefObject
{
public static void Start(string pathToAssembly)
{
TextWriter originalConsoleOutput = Console.Out;
StringWriter writer = new StringWriter();
Console.SetOut(writer);
AppDomain appDomain = AppDomain.CreateDomain("Loading Domain");
Launcher program = (Launcher)appDomain.CreateInstanceAndUnwrap(
typeof(Launcher).Assembly.FullName,
typeof(Launcher).FullName);
program.Execute(pathToAssembly);
AppDomain.Unload(appDomain);
Console.SetOut(originalConsoleOutput);
string result = writer.ToString();
Console.WriteLine(result);
}
/// <summary>
/// This gets executed in the temporary appdomain.
/// No error handling to simplify demo.
/// </summary>
public void Execute(string pathToAssembly)
{
// load the bytes and run Main() using reflection
// working with bytes is useful if the assembly doesn't come from disk
byte[] bytes = File.ReadAllBytes(pathToAssembly); //"Program.exe"
Assembly assembly = Assembly.Load(bytes);
MethodInfo main = assembly.EntryPoint;
main.Invoke(null, new object[] { null });
}
}
Also to note:
Also, note that if you use LoadFrom you'll likely get a FileNotFound
exception because the Assembly resolver will attempt to find the
assembly you're loading in the GAC or the current application's bin
folder. Use LoadFile to load an arbitrary assembly file instead--but
note that if you do this you'll need to load any dependencies
yourself.
There is only one way to do this. You have to dynamically instrument all the code that the assembly is going to get executed. This boils down to intercepting system calls. There is no easy way to do this. Note that this does not require modifying the source code.
Why can't the .NET security system do this? While the system could have provided you with a security permission that you can use to control calls to Environment.Exit, that would not really solve the problem. The assembly could still call into unmanaged code. Other answers have pointed out that this can be done by creating an AppDomain and revoking SecurityPermissionFlag.UnmanagedCode. Indeed, this works, but you indicated in the comments that you to enable the assembly to call unmanaged code.
That's it if you want to run the code in the same process.You could also run the code in another process, but then you have to do interprocess communication.
More AppDomain code could help in finding a solution.
Code can be found at LoadUnload
The small applications included in the project LoadUnload contain AppDomain code that you maybe able to adapt in your solution.

Handle missing reference to COM interface (MATLAB) nicely

I have a VS2013 C# project which references the MATLAB COM interface. 90% of what the app does is in no way related to MATLAB and I would like to be able to compile and run in environments where MATLAB is not available. The desired behavior is:
Build, regardless of presence of the reference (throwing a warning).
If MATLAB is not available, print a message if the user tries to execute MATLAB specific functionality, but it should not be fatal.
My first instinct was to use a pre-processor directive with different build configurations, but I would like to avoid this because it gets messy when setting up continuous integration and tests etc.
So I tried some reflection:
try
{
var matlab = Activator.CreateComInstanceFrom("appname.exe", "MLApp.MLApp").Unwrap();
}
catch
{
//message
}
Which throws a MissingMethodException (i.e. it cannot find a constructor). When MATLAB is present this works fine:
var matlab = new MLApp.MLApp();
New ideas most welcome, thanks in advance!
Edit:
See below for a detailed answer, but for those looking for a quick bit of test code, here is what worked for me.
var Matlab_t = Type.GetTypeFromProgID("Matlab.Autoserver");
if (Matlab_t != null)
matlab = Activator.CreateInstance(Matlab_t);
else
MessageBox.Show("Matlab installation not found.\n");
In my COM related Unit Tests / Detectors I simply do it in multiple steps:
Find COM type
Create COM object from type
Call methods.
Release COM objects, all of them.
In your case I would suggest writing mapper class hiding all of this COM ugliness under layer of abstraction. (I just needed those helpers to test COM library I wrote, so it was opposite)
Type.GetTypeFromProgID throws no exceptions when type is not found, instead it simply returns null.
public static object GetComObject(string typeName)
{
Type ComType;
ComType = Type.GetTypeFromProgID(typeName);
if (ComType == null)
{
//COM type not found
}
// Create an instance of your COM Registered Object.
return Activator.CreateInstance(ComType);
}
When object is (or is not) created I follow up with dynamic or hand-written reflections (does same stuff as dynamic but with higher degree of control, and also works in older .NET versions).
public static object CallFunction(string method, string typeName, params object[] args)
{
Type ComType;
object ComObject;
ComType = Type.GetTypeFromProgID(typeName);
if (ComType == null)
{
//COM type not found
}
// Create an instance of your COM Registered Object.
ComObject = Activator.CreateInstance(ComType);
// Call the Method and cast return to whatever it should be.
return ComType.InvokeMember(method, BindingFlags.InvokeMethod, null, ComObject, args);
}
After work is finished you should release them, all of them. Including objects returned by (created by) methods in root COM object. (Releasing parent, but not its children causes, aside of memleaks, Unit Test engine to freeze, nasty)
public static void DisposeCOM(dynamic obj)
{
if (obj != null)
{
Marshal.ReleaseComObject(obj);
}
}

AssemblyResolve Does not fire

I have an asp.net application. I want to load some assemblies dynamically.
this is my code on application start
protected void Application_Start(Object sender, EventArgs e)
{
LoadPrivateAssemblies();
}
private static void LoadPrivateAssemblies()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;
Assembly.Load("MyDLL");
}
static Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
{
//loads and returns assembly successfully.
}
this code works fine except when a nested c# code calls a class from my dynamic dll Inside an asp.net page (not code-behind)
sample :
<%if(MyDLL.TestObject.Value){%>white some ting<%}%>
what should I do now ?
I think If I know When a new AppDomain is created, it may solve my problem.
I really think you are barking up the wrong tree here.
Assembly.Load(byte[]) does "persist" the assembly in the app domain - otherwise, what else would it be doing?
To illustrate the fact it does, try this:
Create a solution with one console application, and one class library, named OtherAssembly.
In the class library, OtherAssembly, add a single class:
namespace OtherAssembly
{
public class Class1
{
public string HelloWorld()
{
return "Hello World";
}
}
}
In the console application, use this as your program:
public class Program
{
static void Main(string[] args)
{
try
{
using (var fs = new FileStream("OtherAssembly.dll", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var buffer = new byte[fs.Length];
// load my assembly into a byte array from disk
fs.Read(buffer, 0, (int) fs.Length);
// load the assembly in the byte array into the current app domain
AppDomain.CurrentDomain.Load(buffer);
}
// get my type from the other assembly that we just loaded
var class1 = Type.GetType("OtherAssembly.Class1, OtherAssembly");
// create an instance of the type
var class1Instance = class1.GetConstructor(Type.EmptyTypes).Invoke(null);
// find and invoke the HelloWorld method.
var hellowWorldMethod = class1.GetMethod("HelloWorld");
Console.WriteLine(hellowWorldMethod.Invoke(class1Instance, null));
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
finally
{
Console.ReadLine();
}
}
}
Don't reference OtherAssembly from your main program assembly, instead, compile the solution and manually drop OtherAssembly.dll in the main program's bin folder.
Run the main program. It outputs "Hello World", which it can only have done if the assembly was loaded and retained in memory. You'll note I've been very careful not to give Visual Studio or C# any hint to load this OtherAssembly. No reference was added, the type is not explicitly referenced in C#.
You need to look again at your problem.
[EDIT: in response to you commenting on the fact this is not an ASP.NET application]
OK - I've moved my main program to an ASP.NET web page and tried accessing the assembly both from code behind and the markup - and it works in both situations. I'm sure you are missing something here - it just doesn't make sense that the behaviour of this method - whose job it is to load an assembly into the current app domain - is different in an ASP.NET scenario.
It seems to me there are at least two things to investigate:
If it really is a new app domain being created on each call, it would only be because of an unhandled exception in your application. The quickest way to diagnose this is to use SysInternals Process Explorer. Fire it up - add .Net -> Total AppDomains to the list of columns and watch the app domain count for your process (IIS worker process or web dev) as you run your app. If this increases on each call, you've got unhandled exceptions tearing down one AppDomain and forcing another to be created.
Are you sure this assembly is really being loaded? When you pass the byte array to Assembly.Load, try also writing it out to disk using FileStream.Write. Open the file in Reflector or some similar tool. Is this the assembly you expected it to be. Is it really being loaded at all, or is it an empty/corrupt array?
I'm not trying to be argumentative, but this really feels like you are looking in the wrong place for the cause of the problem.
I think If I know When a new AppDomain is created, it may solve my problem
You should use AssemblyLoad event. AssemblyResolved occurs when the resolution of assembly is fails
I found that the problem is because Assembly.Load(bytes); does not persist the assembly in appdomain. does any body know how to persist loaded assembly using Assembly.Load(bytes); in appdomain ?
finally I decided to switch to LoadFile method instead of load.
EDIT
Finally I switched from Assemly.Load(byte[]) to Assembly.LoadFile(string).
But it did not correct the problem itself. I have added <%#Assembly name="MyDLL"%> To All of my ASPX files that has markup C# code emebeded.
And This Solved my Problem.
Thanks to your answers. I have voted up answers that helped me, but I can not accept your solutions because no one is not complete enough.

Is there any way to ensure a function is compiled JIT?

I have a function that I want to be sure is compiled JIT (i.e. right before it's called). Are there attributes or assembly settings that will ensure this? If not, then how can I guarantee that a function is compiled JIT?
Thanks!
EDIT:
I want to do this to prevent my application from crashing due to a missing referenced assembly. If my function is compiled JIT, I can wrap the function call that references the missing assembly in a try...catch block and gracefully handle the situation.
It is my understanding that there could be times when whole classes (or even the whole application) could be Jitted - this would cause an error I couldn't possibly catch.
If I read this correctly, you are worried about errors occurring when a class/method is first compiled. This requires awareness of the boundaries. It is obtainable with an extra layer.
If something is 'wrong with SuspectType (ie a required assembly not loading), the try/catch in the following is of no use because the Jitting of Scenario1() itself will fail.
void Scenario1()
{
try
{
var x = new SuspectType();
...
}
catch (..) { ... }
}
It could be rewritten as
void Scenario1a()
{
try
{
Scenario1b();
}
catch (..) { ... }
}
void Scenario1b()
{
var x = new SuspectType();
...
}
But, per Jon Skeet's comment, I'm not sure if this holds for the CFx.
I may be answering the wrong question, but it looks like you mainly want to be able to intercept assembly load failures (whole classes being JITted defeat the try/catch guards around the calls but that's a side effect of using explicit guards around method calls).
If you want to catch assembly resolution woes, instead of specifying a try/catch around every possible call, you could just listen to the global AssemblyResolve event and respond to the assembly loading failures (we're talking about .Net assemblies here, native dll's load failures would have to be tracked with a different mechanism).
static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += OnResolveFailure;
//...
}
static Assembly OnResolveFailure(object sender, ResolveEventArgs args)
{
//Do something here...
}
The downside of this is you can't do much, except looking for the assembly somewhere else (or logging the error). Specific and graceful logic when a resolution fails is not provided with this way of catching load failures.
Is this something that your app needs to be able to resolve on it's own, or are you debugging some kind of assembly load problem right now?
If the latter, have a look at the fusion log. This is log that is generated by the subsystem that probes for and loads assemblies at run time.
Here's an article: http://www.hanselman.com/blog/BackToBasicsUsingFusionLogViewerToDebugObscureLoaderErrors.aspx

Categories

Resources