Force application to run with optional DLL - c#

I have made desktop application. I have make class library and then make its DLL from University assembly. Now i want to make library DLL optional. In short i want to run the application weather or not library DLL is refereed.
Right now if i remove reference of library DLL then it gives error on library methods that they are not defined. I want this application to run with oujt giving error of library method.
I have search on google but i am unable to find out any reliable answer.

Check if assembly exists on disk, and if it's true use dynamic assembly loading:
http://msdn.microsoft.com/en-us/library/25y1ya39.aspx
Called classes/methods in your library can be replaced by stubs(new level of abstraction), in which you can check if assembly is successfully loaded, and invoke from it if yes.
Ok.. Very simple example:
"Real Assembly" code(First project, compiled as class library "RealAssembly.dll"):
namespace RealAssembly
{
using System;
public class RealClass
{
Random rand = new Random();
public int SomeProperty { get { return rand.Next(); } }
public string SomeMethod()
{
return "We used real library! Meow!";
}
}
}
"Our project" code with Fake(stub) class(Second project, compiled as Console applicaiton - "ClientApp.exe"):
using System;
using System.IO;
using System.Reflection;
namespace ClientApp
{
class FakeClass
{
public int SomeProperty { get { return 0; } }
public string SomeMethod()
{
return "Library not exists, so we used stub! :)";
}
}
class Program
{
// dynamic instance of Real or Fake class
private static dynamic RealOfFakeObject;
static void Main(string[] args)
{
TryLoadAssembly();
Console.WriteLine(RealOfFakeObject.SomeMethod());
Console.WriteLine(RealOfFakeObject.SomeProperty);
Console.WriteLine("Press any key...");
Console.ReadKey();
}
private static void TryLoadAssembly()
{
string assemblyFullName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RealAssembly.dll");
if (File.Exists(assemblyFullName))
{
var RealAssembly = Assembly.LoadFrom(assemblyFullName);
var RealClassType = RealAssembly.GetType("RealAssembly.RealClass");
RealOfFakeObject = Activator.CreateInstance(RealClassType);
}
else
{
RealOfFakeObject = new FakeClass();
}
}
}
}
This two projects are not referenced directly. "System" is the only reference used in this two projects.
So now, if compiled "RealAssembly.dll" exists in same directory we will have "We used real library! Meow!" string and random integer at console output. Otherwise if "RealAssembly.dll" not exists in same directory - "Library not exists, so we used stub! :)" and 0 will be shown.

Related

How to access running program from dynamically loaded dll

So I'm trying to access a running program from which I've injected an external DLL at runtime. I have two projects: console app and class library.
Console app:
using System;
using System.Reflection;
namespace SharpConsole
{
public class GameEvent
{
private bool _cancelled;
public bool Cancelled { get => _cancelled; }
public void SetCancelled(bool cancelled)
{
_cancelled = cancelled;
}
public override string ToString()
{
return $"Event (Cancelled={Cancelled})";
}
}
public class API
{
public static void Print(string text)
{
Console.WriteLine($"[API] {text}");
}
}
public class Program
{
public static string TestValue = "some initial test value";
static void Main(string[] args)
{
var evt = new GameEvent();
Console.WriteLine("[Console] " + evt.ToString());
Console.WriteLine("[Console] Loading dll...");
// var asm = Assembly.LoadFile(#"C:\Users\tinytengu\Desktop\SharpTest\SharpExternal\bin\Release\netcoreapp3.1\SharpExternal.dll");
var asm = Assembly.LoadFile(#"C:\Users\tinytengu\SharpExternal\SharpExternal.dll");
var classType = asm.GetType("SharpExternal.Class1");
Console.WriteLine("[Console] Invoking SharpExternal.Class1.Main method...");
var methodInfo = classType.GetMethod("Main", BindingFlags.Static | BindingFlags.Public);
methodInfo.Invoke(null, new object[] { evt });
Console.WriteLine("[Console] After changes: " + evt.ToString());
Console.WriteLine();
methodInfo = classType.GetMethod("ShowTestValue", BindingFlags.Static | BindingFlags.Public);
methodInfo.Invoke(null, null);
Console.ReadLine();
}
}
}
And a class library:
using System;
namespace SharpExternal
{
public class Class1
{
public static void Main(ref SharpConsole.GameEvent evt)
{
Console.WriteLine("[DLL] " + evt.ToString());
Console.WriteLine("[DLL] Cancelling an event");
evt.SetCancelled(true);
}
public static void ShowTestValue()
{
SharpConsole.API.Print(SharpConsole.Program.TestValue);
}
}
}
What is going on:
The console app creates GameEvent class instance
It injects the ExternalSharp.dll
ExternalSharp.Class1.Main gets called passing previously created GameEvent instance reference to it.
ExternalSharp changes GameEvent's Cancelled state and SharpConsole outputs an instance of a GameEvent that has been successfully modified from ExternalSharp
ExternalSharp's ShowTestValue gets called so it outputs a Console's TestValue field value.
The thing is, I can compile SharpExternal (which uses SharpConsole project as a dependency so it can use its classes. I can also compile SharpConsole and use its DLL file as a dependency, the result is the same) once and move its DLL file on the Desktop so it can't access any files, be recompiled and etc., i.e. in a completely empty folder. BUT I can change Console's TestValue at any moment, even from Console.ReadLine in runtime, then recompile only the Console app and the SharpExternal will output new value when the ShowTestValue method is called. I can add more static properties, methods, etc. before and after TestValue, change the file as I want, but unchanged from the first time SharpExternal.dll file which is still located on the Desktop manages to find TestValue (and other static fields, methods, ...) every time and output the correct value.
I'd like to know how this whole thing works, how ExternalSharp finds the correct TestValue address every time. I suppose it's because of the static modifier, but I haven't found any information beyond the fact that it allows type members to refer to the type itself, and not just to its instance, which I already knew.

How to call a method from an external Assembly

I have sample c# project:
namespace SampleExe
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
And I have sample c# dll:
namespace SampleDll
{
public class Program
{
public static void TestMethod(string samplestr)
{
Console.WriteLine("TestMethod Void Runned! Your string is: "+samplestr);
}
}
}
How can i call TestMethod() from compilled SampleDll.DLL (i want to load external dll)
Here's a working example of using Reflection to load a library at runtime and execute a static method. Note that it assumes quite a lot: you must know the library name, the class name, the method name, and all of its arguments ahead of time. It's often much easier to just reference a library directly.
A great way to use Reflection successfully is together with inheritance/interfaces. Library A contains the base class or interface, and Library B contains a derived class. Library A can use reflection to load Library B , then find all class types in Library B that are derived from the base class or interface (using Type.IsAssignableFrom). In this way, Library A will have strongly typed properties and methods to work with coming from the base, instead of having to know string names of classes, methods, and properties in Library B a priori.
Code for main EXE doing the reflection:
using System;
using System.IO;
using System.Linq;
using System.Reflection;
namespace SomeNamespace
{
public class Program
{
static void Main()
{
string pathToSampleDLL = "<if you know the path ahead of time, use it>";
// if SampleDLL.dll is in same directory as this EXE (a common occurrence):
string workingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
pathToSampleDLL = Path.Combine(workingDirectory, "SampleDLL.dll");
// load the DLL at runtime
Assembly sampleDLL = Assembly.LoadFrom(pathToSampleDLL);
// since you know the type name, you can use LINQ to return your type:
Type sampleType = sampleDLL.GetTypes().FirstOrDefault(t => t.Name == "Program");
// you are looking for a static method on this type, and you know its name, so use GetMethods:
MethodInfo staticMethod = sampleType.GetMethod("TestMethod", BindingFlags.Public | BindingFlags.Static);
// invoke the method. Since you know its arguments and return value ahead of time, just hard code it:
// you can use null for the object since this is a static method. It takes only one argument, a sample string
staticMethod.Invoke(null, new object[] { "sampleStr" });
}
}
}
Code for sample library (compiled to "SampleDLL.dll"):
using System;
namespace SampleDll
{
public class Program
{
public static void TestMethod(string sampleStr)
{
Console.WriteLine("TestMethod Void Runned! Your string is: " + sampleStr);
}
}
}
You have multiple options for this. You can create a dll and add the dll as a reference to the project. You can add the project as a reference also. You can create a NuGet package of dll also and use that.
Then simply call SampleDll.Program.TestMethod
To do this, you need to use reflection.
var assembly = Assembly.Load(File.ReadAllBytes("SampleDLL.dll"));
foreach(Type type in assembly.GetExportedTypes())
{
var c = Activator.CreateInstance(type);
type.InvokeMember("TestMethod", BindingFlags.InvokeMethod, null, c, new object[] { #"Hi!" });
}

Roslyn, how can I instantiate a class in a script during runtime and invoke methods of that class?

I understand how I can execute entire scripts using Roslyn in C# but what I now want to accomplish is to compile a class inside the script, instantiate it, parse it to an interface and then invoke methods that the compiled and instantiated class implements.
Does Roslyn expose such functionality? Can you someone please point me to such approach?
Thanks
I think you can do what you want for example like this:
namespace ConsoleApp2 {
class Program {
static void Main(string[] args) {
// create class and return its type from script
// reference current assembly to use interface defined below
var script = CSharpScript.Create(#"
public class Test : ConsoleApp2.IRunnable {
public void Run() {
System.Console.WriteLine(""test"");
}
}
return typeof(Test);
", ScriptOptions.Default.WithReferences(Assembly.GetExecutingAssembly()));
script.Compile();
// run and you get Type object for your fresh type
var testType = (Type) script.RunAsync().Result.ReturnValue;
// create and cast to interface
var runnable = (IRunnable)Activator.CreateInstance(testType);
// use
runnable.Run();
Console.ReadKey();
}
}
public interface IRunnable {
void Run();
}
}
Instead of returning type you created from script you can also use globals and return it that way:
namespace ConsoleApp2 {
class Program {
static void Main(string[] args) {
var script = CSharpScript.Create(#"
public class Test : ConsoleApp2.IRunnable {
public void Run() {
System.Console.WriteLine(""test"");
}
}
MyTypes.Add(typeof(Test).Name, typeof(Test));
", ScriptOptions.Default.WithReferences(Assembly.GetExecutingAssembly()), globalsType: typeof(ScriptGlobals));
script.Compile();
var globals = new ScriptGlobals();
script.RunAsync(globals).Wait();
var runnable = (IRunnable)Activator.CreateInstance(globals.MyTypes["Test"]);
runnable.Run();
Console.ReadKey();
}
}
public class ScriptGlobals {
public Dictionary<string, Type> MyTypes { get; } = new Dictionary<string, Type>();
}
public interface IRunnable {
void Run();
}
}
Edit to answer your comment.
what if I know the name and type of the class in the script? My
understanding is that script.Compile() adds the compiled assembly to
gac? Am I incorrect? If I then simply use
Activator.CreateInstance(typeofClass) would this not solve my problem
without even having to run the script
Compiled assembly is not added to gac - it is compiled and stored in memory, similar to how you can load assembly with Assembly.Load(someByteArray). Anyway, after you call Compile that assembly is loaded in current app domain so you can access your types without RunAsunc(). Problem is this assembly has cryptic name, for example: ℛ*fde34898-86d2-42e9-a786-e3c1e1befa78#1-0. To find it you can for example do this:
script.Compile();
var asmAfterCompile = AppDomain.CurrentDomain.GetAssemblies().Single(c =>
String.IsNullOrWhiteSpace(c.Location) && c.CodeBase.EndsWith("Microsoft.CodeAnalysis.Scripting.dll"));
But note this is not stable, because if you compile multiple scripts in your app domain (or even same script multiple times) - multiple such assemblies are generated, so it is hard to distinguish between them. If that is not a problem for you - you can use this way (but ensure that you properly test all this).
After you found generated assembly - problems are not over. All your script contents are compiled under wrapping class. I see its named "Submission#0" but I cannot guarantee it's always named like that. So suppose you have class Test in your script. It will be child class of that wrapper, so real type name will be "Submission#0+Test". So to get your type from generated assembly it's better to do this:
var testType = asmAfterCompile.GetTypes().Single(c => c.Name == "Test");
I consider this approach somewhat more fragile compared to previous, but if previous are not applicable for you - try this one.
Another alternative suggested in comments:
script.Compile();
var stream = new MemoryStream();
var emitResult = script.GetCompilation().Emit(stream);
if (emitResult.Success) {
var asm = Assembly.Load(stream.ToArray());
}
That way you create assembly yourself and so do not need to search it in current app domain.

Is it possible to import another C# project with a main method and set it as the entry point?

Is it possible to create a project in C# without a main method, and import another project that has one, and set the entry point to be that main method of the imported project?
The purpose of this is to provide a library complete with its main method and all startup code, requiring only a couple of "plugin" methods. This will minimize boiler-plate (in particular, start-up) code.
Abstract example:
Consider Project 1 with Program.cs:
namespace Project1 {
public class Program {
public static void Main() {
Console.WriteLine("All your Main are belong to us");
Plugin pluginClass = MagicallyGetInstanceOfPluginClassProbablyThroughInjection();
pluginClass.DoSomethingSpecificDependingOnPluginClassDefinition();
}
private Plugin MagicallyGetInstanceOfPluginClassProbablyThroughInjection(){
/*...*/
}
}
public interface Plugin {
void DoSomethingSpecificDependingOnPluginClassDefinition();
}
}
Now consider Project 2 with only class MyPlugin.cs:
namespace Project2 {
using Project1;
public class MyPlugin: Plugin {
public void DoSomethingSpecificDependingOnPluginClassDefinition() {
Console.WriteLine("I'm doing something specific!");
}
}
}
Things to point out:
Project 1 is just a library, possibly nuget'ed
It's Project 2 that imports Project 1, not the other way around
The MyPlugin.cs class above is the only class/file in the project (excluding manifests, app configs, etc)
Aim:
Project 2 should compile into an executable, running Project 1's Main function without writing any more code (no boiler-plate start-up/set-up code). There can then be Project 3, 4, 5, ... that all implement their Plugin-specific code, import Project 1 and run as independent instances.
Is this possible to do? Or do I still have to make a main method in each project that calls the imported project's start-up code? Many thanks in advance!
You could create a plugin container that scans the directory for assemblies and tries to load them. For this you would need a shared interface (interface known to your program and the plugins.
You could then add the DLLs into a defined plugin directory or you could reference the projects inside your main running project.
An example of the interface could be:
public interface IStandAlone
{
void Run();
}
And 1 or to simple implementations could be
public class Program1 : IStandAlone
{
public void Run()
{
Console.WriteLine("Program1");
}
}
public class Program2 : IStandAlone
{
public void Run()
{
Console.WriteLine("Program 2");
}
}
Then you would need to load the possible assemblies, either from the current assemblies (as is done in this example), or by scanning a directory for dlls that might have your type.
An example that scans the current assemblies for any implementations of the a definite type:
public class PluginContainer<T>
{
Type targetType = typeof(T);
public virtual IList<Type> GetMatchingTypes()
{
Assembly[] currentAssemblies = AppDomain.CurrentDomain.GetAssemblies();
IList<Type> items = new List<Type>();
if (currentAssemblies == null || currentAssemblies.Length == 0)
{
Console.WriteLine("No assemblies found!");
return items;
}
foreach (Assembly ass in currentAssemblies)
{
try
{
var types = ass.GetTypes();
foreach (var t in types)
{
if (t.IsInterface)
{
continue;
}
if (!(targetType.IsAssignableFrom(t)))
{
continue;
}
items.Add(t);
}
}
catch (ReflectionTypeLoadException rtle)
{
/* In case the loading failed, scan the types that it was able to load */
Console.WriteLine(rtle.Message);
if (rtle.Types != null)
{
foreach (var t in rtle.Types)
{
if (t.IsInterface)
{
continue;
}
if (!(targetType.IsAssignableFrom(t)))
{
continue;
}
items.Add(t);
}
}
}
catch (Exception ex)
{
/* General exception */
Console.WriteLine(ex.Message);
}
}
return items;
}
public IList<T> GetPlugins()
{
IList<Type> matchingTypes = GetMatchingTypes();
IList<T> items = new List<T>();
if (matchingTypes == null || matchingTypes.Count == 0)
{
Console.WriteLine("No matching types of {0} found", typeof(T).FullName);
return null;
}
foreach (Type type in matchingTypes)
{
try
{
T nObj = (T)Activator.CreateInstance(type);
items.Add(nObj);
}
catch (Exception ex)
{
Console.WriteLine("Error occured trying to run {0}\r\n{1}", type.FullName, ex.Message);
}
}
return items;
}
}
which can then be used inside a main method to scan for any available plugins, and to execute them:
static void Main(string[] args)
{
PluginContainer<IStandAlone> container = new PluginContainer<IStandAlone>();
var plugins = container.GetPlugins();
foreach (var plugin in plugins)
{
plugin.Run();
}
Console.ReadLine();
}
which eventually gives as an output:
Program1
Program 2
Please keep in mind, that this is a very basic example, and that a well thought out interface should be in place, that really only contains the basics, and might give some feedback to the program running the plugins (though this shouldn't be a requirement). Also offering a versions for plugins, maybe an update Url, and such things could be handy in case your plugins can be maintained or implemented by 3th party providers...
I believe the requirement for a start-up method is that it's signature needs to be public static void and it needs to have a single string[] parameter. It may also need to be named "Main", but I doubt it. If a method matches those requirements it should be available to pick as the start-up method in a project's properties.
However the start-up method is what's used to run a stand-alone executable program when it's launched. I believe what you are looking for is more of a plug-in architecture. You can create an attribute and tag your entry point method(s) with that attribute. Then, in your service, you would need to reflect over the classes in the plug-in assembly which you are loading and find methods marked with your custom attribute, and invoke the appropriate one.
Sorry if this sounds a bit vague but a "plugin architecture" is not a trivial topic.
An alternative would be to use the System.Diagnostics.Process.Start(string) method to just launch your "plug-in" as a stand-alone program.
I'm not really sure what you're asking for. Every C# project is either a .exe or a .dll. A .dll does not have a main method, but an .exe needs one. Here's the link describing what it should look like.
If you have many very similar applications then you can move all the common stuff in a .dll project and reference it in all the applications. Then you can call the methods from each .exe. You'll still have a Main() method in each .exe, but it will only contain one line which calls the common implementation.
Or you can do something like a plugin architecture, where you have one .exe and all the other applications are .dll projects, which are loaded and executed by the .exe as needed.
Six of one, half a dozen of the other, it's all the same in the end.

Getting the assembly name for a process started within an app domain

I have a service that creates an app domain and starts it:
this._appDomain = AppDomain.CreateDomain(this._appName, AppDomain.CurrentDomain.Evidence, appDomainSetup);
this._startStopControllerToRun = (IStartStop)this._appDomain.CreateInstanceFromAndUnwrap(assemblyName, this._fullyQualifiedClassName);
this._startStopControllerToRun.Start();
That has been running well for a long time now. The issue is when the controller, started within this app domain, calls a framework logging class. The logger gets the entry assembly name and records that as the source in the event log. This is how the logger gets the source (caller) name:
private static string GetSource()
{
try
{
var assembly = Assembly.GetEntryAssembly();
// GetEntryAssembly() can return null when called in the context of a unit test project.
// That can also happen when called from an app hosted in IIS, or even a windows service.
if (assembly == null)
{
// From http://stackoverflow.com/a/14165787/279516:
assembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
}
return assembly.GetName().Name;
}
catch
{
return "Unknown";
}
}
Before adding that if check, the logger would record "Unknown" for the source. After some research, I added that attempt in the if block. Now the logger records "mscorlib" as the source (entry assembly name).
This is the overview:
Host -> Controller (running within app domain)
How can I get the name of the assembly (that has the controller) running within the domain?
Note: I also tried this (below), but it gives me the name of the framework where the logging class exists (not the name of the assembly in which the controller is running within the app domain):
assembly = Assembly.GetExecutingAssembly();
This is perhaps one way to do what you want. What I'm demonstrating here is passing and receiving metadata to the created AppDomain via SetData and GetData methods so disregard how I am creating the actual remote type.
using System;
using System.Reflection;
namespace ConsoleApplication13
{
class Program
{
static void Main(string[] args)
{
AppDomain appDomain = AppDomain.CreateDomain("foo");
appDomain.SetData(FooUtility.SourceKey, FooUtility.SourceValue);
IFoo foo = (IFoo)appDomain.CreateInstanceFromAndUnwrap(Assembly.GetEntryAssembly().Location, typeof(Foo).FullName);
foo.DoSomething();
}
}
public static class FooUtility
{
public const string SourceKey = "Source";
public const string SourceValue = "Foo Host";
}
public interface IFoo
{
void DoSomething();
}
public class Foo : MarshalByRefObject, IFoo
{
public void DoSomething()
{
string source = AppDomain.CurrentDomain.GetData(FooUtility.SourceKey) as string;
if (String.IsNullOrWhiteSpace(source))
source = "some default";
Console.WriteLine(source);
}
}
}
Which outputs:
Foo Host
Press any key to continue ...
So in your case, you could pass whatever source metadata to the AppDomain:
this._appDomain = AppDomain.CreateDomain(this._appName, AppDomain.CurrentDomain.Evidence, appDomainSetup);
this._appDomain.SetData("Source", "MyController");
this._startStopControllerToRun = (IStartStop)this._appDomain.CreateInstanceFromAndUnwrap(assemblyName, this._fullyQualifiedClassName);
this._startStopControllerToRun.Start();
and in your GetSource method check for its existence.
private static string GetSource()
{
try
{
string source = AppDomain.CurrentDomain.GetData("Source") as string;
if (!String.IsNullOrWhiteSpace(source))
return source;
var assembly = Assembly.GetEntryAssembly();
// GetEntryAssembly() can return null when called in the context of a unit test project.
// That can also happen when called from an app hosted in IIS, or even a windows service.
if (assembly == null)
{
// From http://stackoverflow.com/a/14165787/279516:
assembly = new StackTrace().GetFrames().Last().GetMethod().Module.Assembly;
}
return assembly.GetName().Name;
}
catch
{
return "Unknown";
}
}
UPDATE ALTERNATIVE
You could also declare a public interface method for setting the source on a static location in the target domain as well.
using System;
using System.Reflection;
namespace ConsoleApplication13
{
class Program
{
static void Main(string[] args)
{
AppDomain appDomain = AppDomain.CreateDomain("foo");
IFoo foo = (IFoo)appDomain.CreateInstanceFromAndUnwrap(Assembly.GetEntryAssembly().Location, typeof(Foo).FullName);
foo.SetSource("Foo Host");
foo.DoSomething();
}
}
public interface IFoo
{
void DoSomething();
void SetSource(string source);
}
public class Foo : MarshalByRefObject, IFoo
{
public void DoSomething()
{
string source = Foo.Source;
if (String.IsNullOrWhiteSpace(source))
source = "some default";
Console.WriteLine(source);
}
public static string Source{get; private set;}
public void SetSource(string source)
{
Foo.Source = source;
}
}
}
I ran into a situation where somewhere buried in the .net code, it was relying on Assembly.GetEntryAssembly(). It would take the returned assembly and inspect it for an assembly level attribute. Which is going to fail if code is in an app domain.
Long story short, I had to work around this same issue. The solution is ugly, I hate that I needed to do this but it worked...
If you read the docs here - Assembly.GetEntryAssembly() Method
It contains this section:
Return Value
Type: System.Reflection.Assembly
The assembly that is the process executable in the default application domain, or the
first executable that was executed by AppDomain.ExecuteAssembly. Can
return null when called from unmanaged code.
To get around this, I added some code to my exe which makes the process exit if "/initializingappdomain" is passed as an argument.
Here is some code to do this...
// 1. Create your new app domain...
var newDomain = AppDomain.CreateDomain(...);
// 2. call domain.ExecuteAssembly, passing in this process and the "/initializingappdomain" argument which will cause the process to exit right away
newDomain.ExecuteAssembly(GetProcessName(), new[] { "/initializingappdomain" });
private static string GetProcessName()
{
return System.IO.Path.GetFileName(Process.GetCurrentProcess().MainModule.FileName.Replace(".vshost", ""));
}
// 3. Use your app domain as you see fit, Assembly.GetEntryAssembly will now return this hosting .net exe.
Again, this is far from ideal. There are better solutions if you can avoid this but if you find yourself in a situation where you don't own the code relying on Assembly.GetEntryAssembly(), this will get you by in a pinch.

Categories

Resources