Invoke Method, pass object as type - c#

I am working with the following class:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
And I have a string containing following:
public class PersonActions
{
public static void Greet(Person p)
{
string test = p.Name;
}
}
In my client application developped in WPF (.NET 4.7) I am compiling this string at runtime and invoke the Greet method like this:
//Person x = new Person();
//x.Name = "Albert";
//x.Age = 76;
var assembly = Assembly.LoadFile(pathToAsseblyContainingPersonClass);
Type t = assembly.GetType("Person");
var x = Activator.CreateInstance(t);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add(pathToAsseblyContainingPersonClass);
//code being the code from abrom above (PersonActions)
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
Assembly importassembly = results.CompiledAssembly;
Type assemblytype = importassembly.GetType("PersonActions");
ConstructorInfo constructor = assemblytype.GetConstructor(Type.EmptyTypes);
object classObject = constructor.Invoke(new object[] { });// not used for anything
MethodInfo main = assemblytype.GetMethod("Greet");
main.Invoke(classObject, new object[] { x });
Unfotunately this always crashes because somehow it cannot find the method with the same parameter type even if the types come from the same assembly.
The error thrown is a "System.IO.FileNotFoundException" although this makes not much sense. It's not a file that can't be found it's the method overload.
Somehow it is just looking for:
public static void Greet(object p)
Using just 'object' as parameter type works, but is not a possibility in my case.
Is there a way to recieve the object in the type that it is? Or maby to tell the Invocation method that the types match?
EDIT:
Guess I made both an error in my code above and my tests:
Declareing the Person as mentioned before (now commented above) works properly:
Person x = new Person();
x.Name = "Albert";
x.Age = 76;
Using Activator.Createinstance (now correct above) to create the Person x dynamically form the assebly does not work. It seems like var x = Activator.CreateInstance(t);
causes x still to be an "object" and not a "Person".
EDIT 2:
Here a minimal working example of the problem:
Having a solution containing one WPF application. MainWindow.cs containing:
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Example
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string code = #"public class PersonActions
{
public static void Greet(Person p)
{
}
}";
//Change to an absolute path if there is an exception
string pathToAsseblyContainingPersonClass = System.IO.Path.GetFullPath(#"..\..\..\Person\bin\Debug\Person.dll");
var assembly = Assembly.LoadFile(pathToAsseblyContainingPersonClass);
Type t = assembly.GetType("Person");
var x = Activator.CreateInstance(t);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.ReferencedAssemblies.Add(pathToAsseblyContainingPersonClass);
//code being the code from abrom above (PersonActions)
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
Assembly importassembly = results.CompiledAssembly;
Type assemblytype = importassembly.GetType("PersonActions");
ConstructorInfo constructor = assemblytype.GetConstructor(Type.EmptyTypes);
object classObject = constructor.Invoke(new object[] { });// not used for anything
MethodInfo main = assemblytype.GetMethod("Greet");
main.Invoke(classObject, new object[] { x });
}
}
}
And containing one class Library Project calles "Person" containing: (note that there is no namespace)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
EDIT 3: What I ended up with
Thanks to #Adam Benson I could identify the whole problem. The overall problem is, that the current appdomain does not allow to directly load load assemblies from other appdomains. Like Adam pointed out there are three solutions for that (in the linked Microsoft article). The third and definitely also the easiest solution to implement is using the AssemblyResolve event. Although this is a good solution it pains my heart and bones to let my application run into exceptions to resolve this problem.
Like Adam also pointed out is that you get another exception if you put the dll directly into the folder where the exe is located. This is only partly true since the evil twin error only appears if you compare the Person from the original Debug folder assembly and the Person loaded from the appdomain assembly (basically if you have the dll in both directories)
Loading the assembly only from the folder where there exe is located resolves both the FileNotFound and the evil twin error:
old: System.IO.Path.GetFullPath(#"..\..\..\Person\bin\Debug\Person.dll");
new:System.IO.Path.GetFullPath(#"Person.dll");
So what I ended up doing was copying the necessary assembly into the current working directory first:
File.Copy(pathToAsseblyContainingPersonClass, currentDir + #"\\Person.dll" , true);

results.CompiledAssembly throws FileNotFoundException because the assembly is not being generated due to an error occurring during the generation process. You can see the actual compilation error by checking Errors property of CompilerResults.
In this case, the error is that code provided to CompileAssemblyFromSource does not know what Person class is.
You can fix this by adding a reference to the assembly containing Person class:
parameters.ReferencedAssemblies.Add("some_dll");
Edit: I missed the comment saying that parameters contain the reference to assembly containing Person class. That probably means that there is a different error in the results.Error collection. Check it and I will update the answer (I cannot comment yet due to not having 50 rep).

This works (at least it doesn't exception):
object classObject = constructor.Invoke(new object[] { });// not used for anything
//////////////////////////////////////////
AppDomain.CurrentDomain.AssemblyResolve +=
(object sender, ResolveEventArgs resolve_args) =>
{
if (resolve_args.Name == assembly.FullName)
return assembly;
return null;
};
//////////////////////////////////////////
MethodInfo main = assemblytype.GetMethod("Greet");
Based on https://support.microsoft.com/en-gb/help/837908/how-to-load-an-assembly-at-runtime-that-is-located-in-a-folder-that-is method 3 (use the AssemblyResolve event).
I must confess to being mystified as to why it doesn't just work since you have added a ref to the assembly.
I should add that copying the extra dll that defines Person into your exe directory will not work as you then run into the "evil twin" issue where a type created in one assembly cannot be used by another instance of that assembly. (The error you get is the mind-bending "System.ArgumentException: 'Object of type 'Person' cannot be converted to type 'Person'."!!)
Edit: Just discovered that LoadFrom avoids loading the same assembly twice. See Difference between LoadFile and LoadFrom with .NET Assemblies?

Related

How to run code dynamically from text with actual context data [duplicate]

I was wondering if it is possible to save C# code fragments to a text file (or any input stream), and then execute those dynamically? Assuming what is provided to me would compile fine within any Main() block, is it possible to compile and/or execute this code? I would prefer to compile it for performance reasons.
At the very least, I could define an interface that they would be required to implement, then they would provide a code 'section' that implemented that interface.
The best solution in C#/all static .NET languages is to use the CodeDOM for such things. (As a note, its other main purpose is for dynamically constructing bits of code, or even whole classes.)
Here's a nice short example take from LukeH's blog, which uses some LINQ too just for fun.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
class Program
{
static void Main(string[] args)
{
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.GenerateExecutable = true;
CompilerResults results = csc.CompileAssemblyFromSource(parameters,
#"using System.Linq;
class Program {
public static void Main(string[] args) {
var q = from i in Enumerable.Range(1,100)
where i % 2 == 0
select i;
}
}");
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
}
}
The class of primary importance here is the CSharpCodeProvider which utilises the compiler to compile code on the fly. If you want to then run the code, you just need to use a bit of reflection to dynamically load the assembly and execute it.
Here is another example in C# that (although slightly less concise) additionally shows you precisely how to run the runtime-compiled code using the System.Reflection namespace.
You can compile a piece C# of code into memory and generate assembly bytes with Roslyn. It's already mentioned but would be worth adding some Roslyn example for this here. The following is the complete example:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
namespace RoslynCompileSample
{
class Program
{
static void Main(string[] args)
{
// define source code, then parse it (to the type used for compilation)
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(#"
using System;
namespace RoslynCompileSample
{
public class Writer
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
}");
// define other necessary objects for compilation
string assemblyName = Path.GetRandomFileName();
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location)
};
// analyse and generate IL code from syntax tree
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
// write IL code into memory
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
// handle exceptions
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
foreach (Diagnostic diagnostic in failures)
{
Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
}
else
{
// load this 'virtual' DLL so that we can use
ms.Seek(0, SeekOrigin.Begin);
Assembly assembly = Assembly.Load(ms.ToArray());
// create instance of the desired class and call the desired function
Type type = assembly.GetType("RoslynCompileSample.Writer");
object obj = Activator.CreateInstance(type);
type.InvokeMember("Write",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
obj,
new object[] { "Hello World" });
}
}
Console.ReadLine();
}
}
}
Others have already given good answers on how to generate code at runtime so I thought I would address your second paragraph. I have some experience with this and just want to share a lesson I learned from that experience.
At the very least, I could define an
interface that they would be required
to implement, then they would provide
a code 'section' that implemented that
interface.
You may have a problem if you use an interface as a base type. If you add a single new method to the interface in the future all existing client-supplied classes that implement the interface now become abstract, meaning you won't be able to compile or instantiate the client-supplied class at runtime.
I had this issue when it came time to add a new method after about 1 year of shipping the old interface and after distributing a large amount of "legacy" data that needed to be supported. I ended up making a new interface that inherited from the old one but this approach made it harder to load and instantiate the client-supplied classes because I had to check which interface was available.
One solution I thought of at the time was to instead use an actual class as a base type such as the one below. The class itself can be marked abstract but all methods should be empty virtual methods (not abstract methods). Clients can then override the methods they want and I can add new methods to the base class without invalidating existing client-supplied code.
public abstract class BaseClass
{
public virtual void Foo1() { }
public virtual bool Foo2() { return false; }
...
}
Regardless of whether this problem applies you should consider how to version the interface between your code base and the client-supplied code.
Found this useful - ensures the compiled Assembly references everything you currently have referenced, since there's a good chance you wanted the C# you're compiling to use some classes etc in the code that's emitting this:
(string code is the dynamic C# being compiled)
var refs = AppDomain.CurrentDomain.GetAssemblies();
var refFiles = refs.Where(a => !a.IsDynamic).Select(a => a.Location).ToArray();
var cSharp = (new Microsoft.CSharp.CSharpCodeProvider()).CreateCompiler();
var compileParams = new System.CodeDom.Compiler.CompilerParameters(refFiles);
compileParams.GenerateInMemory = true;
compileParams.GenerateExecutable = false;
var compilerResult = cSharp.CompileAssemblyFromSource(compileParams, code);
var asm = compilerResult.CompiledAssembly;
In my case I was emitting a class, whose name was stored in a string, className, which had a single public static method named Get(), that returned with type StoryDataIds. Here's what calling that method looks like:
var tempType = asm.GetType(className);
var ids = (StoryDataIds)tempType.GetMethod("Get").Invoke(null, null);
Warning: Compilation can be surprisingly, extremely slow. A small, relatively simple 10-line chunk of code compiles at normal priority in 2-10 seconds on our relatively fast server. You should never tie calls to CompileAssemblyFromSource() to anything with normal performance expectations, like a web request. Instead, proactively compile code you need on a low-priority thread and have a way of dealing with code that requires that code to be ready, until it's had a chance to finish compiling. For example you could use it in a batch job process.
I recently needed to spawn processes for unit testing. This post was useful as I created a simple class to do that with either code as a string or code from my project. To build this class, you'll need the ICSharpCode.Decompiler and Microsoft.CodeAnalysis NuGet packages. Here's the class:
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.TypeSystem;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
public static class CSharpRunner
{
public static object Run(string snippet, IEnumerable<Assembly> references, string typeName, string methodName, params object[] args) =>
Invoke(Compile(Parse(snippet), references), typeName, methodName, args);
public static object Run(MethodInfo methodInfo, params object[] args)
{
var refs = methodInfo.DeclaringType.Assembly.GetReferencedAssemblies().Select(n => Assembly.Load(n));
return Invoke(Compile(Decompile(methodInfo), refs), methodInfo.DeclaringType.FullName, methodInfo.Name, args);
}
private static Assembly Compile(SyntaxTree syntaxTree, IEnumerable<Assembly> references = null)
{
if (references is null) references = new[] { typeof(object).Assembly, typeof(Enumerable).Assembly };
var mrefs = references.Select(a => MetadataReference.CreateFromFile(a.Location));
var compilation = CSharpCompilation.Create(Path.GetRandomFileName(), new[] { syntaxTree }, mrefs, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
var result = compilation.Emit(ms);
if (result.Success)
{
ms.Seek(0, SeekOrigin.Begin);
return Assembly.Load(ms.ToArray());
}
else
{
throw new InvalidOperationException(string.Join("\n", result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error).Select(d => $"{d.Id}: {d.GetMessage()}")));
}
}
}
private static SyntaxTree Decompile(MethodInfo methodInfo)
{
var decompiler = new CSharpDecompiler(methodInfo.DeclaringType.Assembly.Location, new DecompilerSettings());
var typeInfo = decompiler.TypeSystem.MainModule.Compilation.FindType(methodInfo.DeclaringType).GetDefinition();
return Parse(decompiler.DecompileTypeAsString(typeInfo.FullTypeName));
}
private static object Invoke(Assembly assembly, string typeName, string methodName, object[] args)
{
var type = assembly.GetType(typeName);
var obj = Activator.CreateInstance(type);
return type.InvokeMember(methodName, BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, args);
}
private static SyntaxTree Parse(string snippet) => CSharpSyntaxTree.ParseText(snippet);
}
To use it, call the Run methods as below:
void Demo1()
{
const string code = #"
public class Runner
{
public void Run() { System.IO.File.AppendAllText(#""C:\Temp\NUnitTest.txt"", System.DateTime.Now.ToString(""o"") + ""\n""); }
}";
CSharpRunner.Run(code, null, "Runner", "Run");
}
void Demo2()
{
CSharpRunner.Run(typeof(Runner).GetMethod("Run"));
}
public class Runner
{
public void Run() { System.IO.File.AppendAllText(#"C:\Temp\NUnitTest.txt", System.DateTime.Now.ToString("o") + "\n"); }
}
To compile you could just initiate a shell call to the csc compiler. You may have a headache trying to keep your paths and switches straight but it certainly can be done.
C# Corner Shell Examples
EDIT: Or better yet, use the CodeDOM as Noldorin suggested...
using System.CodeDom.Compiler;
using System.Diagnostics;
using Microsoft.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Reflection;
namespace ASL
{
class Program
{
[Obsolete]
static void Main(string[] args)
{
string code = #"
using System;
namespace First
{
public class Program
{
public static void Main()
{
" +
"Console.WriteLine(\"Hello, world!\");"
+ #"
}
}
}";
Console.WriteLine(code);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
// Reference to System.Drawing library
parameters.ReferencedAssemblies.Add("System.Drawing.dll");
// True - memory generation, false - external file generation
parameters.GenerateInMemory = true;
// True - exe file generation, false - dll file generation
parameters.GenerateExecutable = true;
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.HasErrors)
{
StringBuilder sb = new StringBuilder();
foreach (CompilerError error in results.Errors)
{
sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
}
throw new InvalidOperationException(sb.ToString());
}
Assembly assembly = results.CompiledAssembly;
Type program = assembly.GetType("First.Program");
MethodInfo main = program.GetMethod("Main");
main.Invoke(null, null);
Console.ReadLine();
}
}
}

How do I call functions from a DLL in my resources?

So I have this DLL and this other thing which I think is refered to as a Injector / loader? It basically loads my DLL into the process of itself so it takes my DLL and loads it into the process of "Injector.exe"
In the example down below it doesnt load it from resources but instead from the desktop, the same thing applies here.
Now sicne it's being loaded it doesnt really call any functions and this is where my problem comes in.
I would like to call some the Messagebox function when the DLL is being loaded.
As far as I know and the most logical way would be to do this in the btnAssemblyLoad_Click event so when the event happens it calls the functions in the DLL.
The issue is I have no idea what this would be called, I read something about "Reflection" but im not sure this is what I would need.
How should I go on about this?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace AssemblyLoader
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnBrowse_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
if(ofd.ShowDialog() == DialogResult.OK)
{
tbAssemblyLocation.Text = ofd.FileName;
}
}
private void btnAssemblyLoad_Click(object sender, EventArgs e)
{
AssemblyName an = AssemblyName.GetAssemblyName(tbAssemblyLocation.Text);
Assembly.Load(an);
}
}
}
DLL
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MSgBOxDllCS
{
public class Funcs
{
public void CallMessageBox()
{
MessageBox.Show("Hello");
}
}
}
You would need to call instance method of the non-static class, see below.
// Step 1: load assembly to memory
var an = AssemblyName.GetAssemblyName(tbAssemblyLocation.Text);
var assembly = Assembly.Load(an);
// Step 2: get type from the assembly by name
var type = assembly.GetType("MSgBOxDllCS.Funcs");
// Step 3: get method of the type
var method = type.GetMethod("CallMessageBox");
// Step 4: create instance of the type
var funcs = Activator.CreateInstance(type);
// Step 5: invoke the instance method
method.Invoke(funcs, null);
Generally speaking what you are building is called Plug-In Framework and there is plenty of examples on internet of how it can be done, you may be able to leverage existing frameworks too depending on your requirements.
There are several ways how you can accomplish that.
Load an assembly, create an instance of a class, find the required method using Reflection and invoke it.
var assembly = Assembly.LoadFrom(DllFilePath);
var funcs = assembly.CreateInstance("MSgBOxDllCS.Funcs");
var method = funcs.GetType().GetMethod("CallMessageBox");
method.Invoke(funcs, null);
The same but use a dynamic object, gives less control over reflected members but looks better:
var assembly = Assembly.LoadFrom(DllFilePath);
dynamic funcs = assembly.CreateInstance("MSgBOxDllCS.Funcs");
funcs.CallMessageBox();
And the last but not least, is to extract an interface and put it in a separate shared DLL. This approach would help to get rid of reflection calls and by it's nature is the most reliable:
Common shared DLL:
namespace Shared
{
public interface IFuncs
{
void CallMessageBox();
}
}
DLL to be loaded:
namespace MSgBOxDllCS
{
public class Funcs : Shared.IFuncs
{
public void CallMessageBox()
{
MessageBox.Show("Hello");
}
}
}
Loader:
var assembly = Assembly.LoadFrom(DllFilePath);
var funcs = (Shared.IFuncs)assembly.CreateInstance("MSgBOxDllCS.Funcs");
funcs.CallMessageBox();

C# AppDomain can't load DLL

I'm attempting to build a hot-swapable plugin system where the user can dynamically load and unload dll's. It's necessary that the core application restart as little as possible, so I'm moving as much of the functionality to external libraries instead. From what research I've figured out, I need to create a second AppDomain and load the DLL into that, then just pass along parameters and such to that to run it. Currently, I believe I have most of the program working, but I'm encountering an error when calling the AppDomain.Unwrap() function on the object from CreateInstance. The error is as follows:
System.InvalidCastException: Unable to cast transparent proxy to type 'Program1.Loader'.
Here is the loading code:
try {
unload(dom,out loader,true);
dom=null;
AppDomainSetup dms = new AppDomainSetup();
dms.ConfigurationFile=Environment.CurrentDirectory+Path.DirectorySeparatorChar+"Program1.exe.config";
dms.ApplicationBase=Environment.CurrentDirectory+Path.DirectorySeparatorChar+"Plugins";
Evidence ev = AppDomain.CurrentDomain.Evidence;
dom=AppDomain.CreateDomain("PluginManager",ev,dms);
AssemblyName an = AssemblyName.GetAssemblyName(Environment.CurrentDirectory+"\\Plugins\\PluginManager.dll");
ObjectHandle obj = dom.CreateInstance(an.FullName,"PluginManager.PluginManager");
loader = (Loader)obj.Unwrap();
loader.LoadAssembly(#"PluginManager.dll");
if(!suppressOutput)
Console.WriteLine("Reload successful.");
}
catch(Exception e) {
unload(dom,out loader,true);
loader=null;
Console.WriteLine("PluginManager failed loading. Enter \"reload\" to try again.\n");
Console.Write(e+"\n\n");
}
This line is where the error is thrown:
loader = (Loader)obj.Unwrap();
The external DLL has almost no code in it, since this is a proof of concept. It is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PluginManager {
public class PluginManager:MarshalByRefObject {
public void run(string comm) {
Console.WriteLine(comm);
}
}
}
Edit: Here's the code for the loader class.
class Loader:MarshalByRefObject {
private Assembly _assembly;
public override object InitializeLifetimeService() {
return null;
}
public void LoadAssembly(string path) {
_assembly=Assembly.Load(AssemblyName.GetAssemblyName(path));
}
public object ExecuteStaticMethod(string typeName,string methodName,params object[] parameters) {
Type type = _assembly.GetType(typeName);
MethodInfo method = type.GetMethod(methodName,BindingFlags.Static|BindingFlags.Public);
return method.Invoke(null,parameters);
}
}
It looks like you're trying to create an object of the type PluginManager in the AppDomain and then cast its proxy to the type Loader (which is missing from your code sample).
The problem is in these lines:
ObjectHandle obj = dom.CreateInstance(an.FullName,"PluginManager.PluginManager");
loader = (Loader)obj.Unwrap();
It would work if you either create an instance of Loader instead of PluginManager - or - cast to PluginManager instead of Loader. My guess is the former:
ObjectHandle obj = dom.CreateInstance(an.FullName,"LoaderNamespace.Loader");
(Replace LoaderNamespace with the real one.)

Visual Studio unable to resolve class name unless using nested using block

I have a class that's name is the same as the namespace is is contained within:
Class File ReadModel.cs
namespace App.Core.ReadModel
{
public class ReadModel
{
}
}
Class File MyClass.cs
using App.Core.ReadModel; // this does not work
namespace Something
{
// using App.Core.ReadModel (Works if I un-comment)
public class MyClass
{
public void test()
{
var x = new ReadModel();
}
}
}
When trying to instantiate the class, even when trying to add a using directive at the top, the compiler is still unable to resolve the class. HOWEVER, if I put the using statements nested within the namespace, it works fine.
Can someone pls explain why this works? This is a new feature I've just discovered.
The error is: App.Core.ReadModel is a namespace but is used like a type
The difference between
using System ;
using Foo.Bar ;
namespace My.Widget.Tools
{
public class MySpecialTool
{
...
}
}
and
using System ;
namespace My.Widget.Tools
{
using Foo.Bar ;
public class MySpecialTool
{
...
}
}
is that in the first case, the directive using Foo.Bar ; causes the objects in the namespace Foo.Bar to be imported into the unnamed (global) namespace for the compilation unit. In the second case, the using directive imports the objects in the namespace Foo.Bar into the namespace My.Widget.Tools.
The difference has to do with search order in resolving unqualified references.
Unqualified references are resolved by first searching within the enclosing named namespace. If the reference is not resolved, then the unnamed (global) namespace for the compilation unit is searched.
Consider the case where the above namespace Foo.Bar contains a visible class that conflicts with a class contained in the System namespace.
In the first case, where the Foo.Bar namespace has been loading into the global namespace, you'll get an error regarding an ambiguous reference if you try to reference the conflicting object: it will search the enclosing namespace first, on not finding it, it will then look into the global namespace and find multiple objects and whine.
In the second case, the enclosing namespace is searched first, on finding an object of the desired name, the unqualified reference is resolved and the compiler is happy: no conflict.
Note that you can coerce the search order to the global namespace by qualifying an object reference with the global:: prefix. You can also define your own aliases with the using directive, either for a namespace:
using io = System.IO ;
or for a type:
using IntList = System.Collections.Generic.List<int> ;
the caveat with defining an alias for the namespace is that you then have to use the alias to resolve a reference. An alias defined for a type just gives you a [perhaps] shorthand way of naming the type. Probably more useful for generics than anything else. I don't see a lot of value in doing something like:
using Row = System.Data.DataRow ;
outside of writing obfuscated C#.
See also this question: Should 'using' statements be inside or outside the namespace?
ยง16 of ISO/IEC 23270:2006 (Information technology -- Programming languages -- C#) will tell you far more than you ever wanted to know about namespaces and using directives.
See also this MSDN piece on namespace aliases: http://msdn.microsoft.com/en-us/library/c3ay4x3d(v=vs.80).aspx
Edit again:
Nicholas answered your revised question very nicely. Please see his answer:
Visual Studio unable to resolve class name unless using nested using block
EDIT:
Using have to be at the top of the file. Move the using above the first namespace.
Example:
namespace App.Core.ReadModel
{
public class ReadModel
{
}
}
using App.Core.ReadModel; // cannot be placed here. Must be at top of file.
namespace App
{
public class Program
{
public static Main()
{
var obj = new ReadModel();
}
}
}
Original Answer (irrelevant to question):
Option 1: Rename Namespace
namespace App.Core.IO
{
public class ReadModel
{
}
}
Option 2: Use an Alias
using MyReadModel = App.Core.ReadModel.ReadModel;
public class Program
{
public static void Main()
{
var obj = new MyReadModel();
}
}
Option 3: Qualify Type Name
public class Program
{
public static void Main()
{
var obj = new App.Core.ReadModel.ReadModel();
}
}

Is it possible to dynamically compile and execute C# code fragments?

I was wondering if it is possible to save C# code fragments to a text file (or any input stream), and then execute those dynamically? Assuming what is provided to me would compile fine within any Main() block, is it possible to compile and/or execute this code? I would prefer to compile it for performance reasons.
At the very least, I could define an interface that they would be required to implement, then they would provide a code 'section' that implemented that interface.
The best solution in C#/all static .NET languages is to use the CodeDOM for such things. (As a note, its other main purpose is for dynamically constructing bits of code, or even whole classes.)
Here's a nice short example take from LukeH's blog, which uses some LINQ too just for fun.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
class Program
{
static void Main(string[] args)
{
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.GenerateExecutable = true;
CompilerResults results = csc.CompileAssemblyFromSource(parameters,
#"using System.Linq;
class Program {
public static void Main(string[] args) {
var q = from i in Enumerable.Range(1,100)
where i % 2 == 0
select i;
}
}");
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
}
}
The class of primary importance here is the CSharpCodeProvider which utilises the compiler to compile code on the fly. If you want to then run the code, you just need to use a bit of reflection to dynamically load the assembly and execute it.
Here is another example in C# that (although slightly less concise) additionally shows you precisely how to run the runtime-compiled code using the System.Reflection namespace.
You can compile a piece C# of code into memory and generate assembly bytes with Roslyn. It's already mentioned but would be worth adding some Roslyn example for this here. The following is the complete example:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
namespace RoslynCompileSample
{
class Program
{
static void Main(string[] args)
{
// define source code, then parse it (to the type used for compilation)
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(#"
using System;
namespace RoslynCompileSample
{
public class Writer
{
public void Write(string message)
{
Console.WriteLine(message);
}
}
}");
// define other necessary objects for compilation
string assemblyName = Path.GetRandomFileName();
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location)
};
// analyse and generate IL code from syntax tree
CSharpCompilation compilation = CSharpCompilation.Create(
assemblyName,
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
// write IL code into memory
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
// handle exceptions
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
foreach (Diagnostic diagnostic in failures)
{
Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
}
else
{
// load this 'virtual' DLL so that we can use
ms.Seek(0, SeekOrigin.Begin);
Assembly assembly = Assembly.Load(ms.ToArray());
// create instance of the desired class and call the desired function
Type type = assembly.GetType("RoslynCompileSample.Writer");
object obj = Activator.CreateInstance(type);
type.InvokeMember("Write",
BindingFlags.Default | BindingFlags.InvokeMethod,
null,
obj,
new object[] { "Hello World" });
}
}
Console.ReadLine();
}
}
}
Others have already given good answers on how to generate code at runtime so I thought I would address your second paragraph. I have some experience with this and just want to share a lesson I learned from that experience.
At the very least, I could define an
interface that they would be required
to implement, then they would provide
a code 'section' that implemented that
interface.
You may have a problem if you use an interface as a base type. If you add a single new method to the interface in the future all existing client-supplied classes that implement the interface now become abstract, meaning you won't be able to compile or instantiate the client-supplied class at runtime.
I had this issue when it came time to add a new method after about 1 year of shipping the old interface and after distributing a large amount of "legacy" data that needed to be supported. I ended up making a new interface that inherited from the old one but this approach made it harder to load and instantiate the client-supplied classes because I had to check which interface was available.
One solution I thought of at the time was to instead use an actual class as a base type such as the one below. The class itself can be marked abstract but all methods should be empty virtual methods (not abstract methods). Clients can then override the methods they want and I can add new methods to the base class without invalidating existing client-supplied code.
public abstract class BaseClass
{
public virtual void Foo1() { }
public virtual bool Foo2() { return false; }
...
}
Regardless of whether this problem applies you should consider how to version the interface between your code base and the client-supplied code.
Found this useful - ensures the compiled Assembly references everything you currently have referenced, since there's a good chance you wanted the C# you're compiling to use some classes etc in the code that's emitting this:
(string code is the dynamic C# being compiled)
var refs = AppDomain.CurrentDomain.GetAssemblies();
var refFiles = refs.Where(a => !a.IsDynamic).Select(a => a.Location).ToArray();
var cSharp = (new Microsoft.CSharp.CSharpCodeProvider()).CreateCompiler();
var compileParams = new System.CodeDom.Compiler.CompilerParameters(refFiles);
compileParams.GenerateInMemory = true;
compileParams.GenerateExecutable = false;
var compilerResult = cSharp.CompileAssemblyFromSource(compileParams, code);
var asm = compilerResult.CompiledAssembly;
In my case I was emitting a class, whose name was stored in a string, className, which had a single public static method named Get(), that returned with type StoryDataIds. Here's what calling that method looks like:
var tempType = asm.GetType(className);
var ids = (StoryDataIds)tempType.GetMethod("Get").Invoke(null, null);
Warning: Compilation can be surprisingly, extremely slow. A small, relatively simple 10-line chunk of code compiles at normal priority in 2-10 seconds on our relatively fast server. You should never tie calls to CompileAssemblyFromSource() to anything with normal performance expectations, like a web request. Instead, proactively compile code you need on a low-priority thread and have a way of dealing with code that requires that code to be ready, until it's had a chance to finish compiling. For example you could use it in a batch job process.
I recently needed to spawn processes for unit testing. This post was useful as I created a simple class to do that with either code as a string or code from my project. To build this class, you'll need the ICSharpCode.Decompiler and Microsoft.CodeAnalysis NuGet packages. Here's the class:
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.TypeSystem;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
public static class CSharpRunner
{
public static object Run(string snippet, IEnumerable<Assembly> references, string typeName, string methodName, params object[] args) =>
Invoke(Compile(Parse(snippet), references), typeName, methodName, args);
public static object Run(MethodInfo methodInfo, params object[] args)
{
var refs = methodInfo.DeclaringType.Assembly.GetReferencedAssemblies().Select(n => Assembly.Load(n));
return Invoke(Compile(Decompile(methodInfo), refs), methodInfo.DeclaringType.FullName, methodInfo.Name, args);
}
private static Assembly Compile(SyntaxTree syntaxTree, IEnumerable<Assembly> references = null)
{
if (references is null) references = new[] { typeof(object).Assembly, typeof(Enumerable).Assembly };
var mrefs = references.Select(a => MetadataReference.CreateFromFile(a.Location));
var compilation = CSharpCompilation.Create(Path.GetRandomFileName(), new[] { syntaxTree }, mrefs, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
using (var ms = new MemoryStream())
{
var result = compilation.Emit(ms);
if (result.Success)
{
ms.Seek(0, SeekOrigin.Begin);
return Assembly.Load(ms.ToArray());
}
else
{
throw new InvalidOperationException(string.Join("\n", result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error).Select(d => $"{d.Id}: {d.GetMessage()}")));
}
}
}
private static SyntaxTree Decompile(MethodInfo methodInfo)
{
var decompiler = new CSharpDecompiler(methodInfo.DeclaringType.Assembly.Location, new DecompilerSettings());
var typeInfo = decompiler.TypeSystem.MainModule.Compilation.FindType(methodInfo.DeclaringType).GetDefinition();
return Parse(decompiler.DecompileTypeAsString(typeInfo.FullTypeName));
}
private static object Invoke(Assembly assembly, string typeName, string methodName, object[] args)
{
var type = assembly.GetType(typeName);
var obj = Activator.CreateInstance(type);
return type.InvokeMember(methodName, BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, args);
}
private static SyntaxTree Parse(string snippet) => CSharpSyntaxTree.ParseText(snippet);
}
To use it, call the Run methods as below:
void Demo1()
{
const string code = #"
public class Runner
{
public void Run() { System.IO.File.AppendAllText(#""C:\Temp\NUnitTest.txt"", System.DateTime.Now.ToString(""o"") + ""\n""); }
}";
CSharpRunner.Run(code, null, "Runner", "Run");
}
void Demo2()
{
CSharpRunner.Run(typeof(Runner).GetMethod("Run"));
}
public class Runner
{
public void Run() { System.IO.File.AppendAllText(#"C:\Temp\NUnitTest.txt", System.DateTime.Now.ToString("o") + "\n"); }
}
To compile you could just initiate a shell call to the csc compiler. You may have a headache trying to keep your paths and switches straight but it certainly can be done.
C# Corner Shell Examples
EDIT: Or better yet, use the CodeDOM as Noldorin suggested...
using System.CodeDom.Compiler;
using System.Diagnostics;
using Microsoft.CSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Reflection;
namespace ASL
{
class Program
{
[Obsolete]
static void Main(string[] args)
{
string code = #"
using System;
namespace First
{
public class Program
{
public static void Main()
{
" +
"Console.WriteLine(\"Hello, world!\");"
+ #"
}
}
}";
Console.WriteLine(code);
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
// Reference to System.Drawing library
parameters.ReferencedAssemblies.Add("System.Drawing.dll");
// True - memory generation, false - external file generation
parameters.GenerateInMemory = true;
// True - exe file generation, false - dll file generation
parameters.GenerateExecutable = true;
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.HasErrors)
{
StringBuilder sb = new StringBuilder();
foreach (CompilerError error in results.Errors)
{
sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
}
throw new InvalidOperationException(sb.ToString());
}
Assembly assembly = results.CompiledAssembly;
Type program = assembly.GetType("First.Program");
MethodInfo main = program.GetMethod("Main");
main.Invoke(null, null);
Console.ReadLine();
}
}
}

Categories

Resources