C# Compile at Runtime Visual Studio unusual behavior - c#

Helo,
i have a strange behavior in Visual Studio.
My code is a compiling at runtime, in VS it
workts fine but if i start the programm from
release-folder the compiled dll seems to be blocked,
what is not that unusal but why does VS dont say that?
Can some one tell me also how to unload a compiled dll
in runtime?
VS:
Release-Folder:
My Test Code here:
static void Main(string[] args)
{
var property = "Value";
var key = "MyKey";
var binding = string.Format("{0}.{1}", key, property);
var dllName = string.Format("Condition_{0}_{1}.dll", key, property);
var input = "65535";
var condition = "Convert.ToInt64(input) > 0xFFFF ? 0 : 501";
for (var i = 0; i < 5; i++)
{
try
{
var conditionCompiled = BuildCondition(key, property, condition);
Type moduleType = null;
if (conditionCompiled.Errors.HasErrors)
{
if (conditionCompiled.Errors[0].ErrorNumber != "CS0042")
throw new ConditionCompileException(conditionCompiled.Errors[0].ErrorText);
var assemblyPath = Path.Combine(Directory.GetCurrentDirectory(), dllName);
var assembly = Assembly.LoadFile(assemblyPath);
moduleType = assembly.GetType("DynaCore.ConditionRunner");
}
else
{
var module = conditionCompiled.CompiledAssembly.GetModules()[0];
moduleType = module.GetType("DynaCore.ConditionRunner");
}
var method = moduleType.GetMethod("RunCondition");
var value = method.Invoke(null, new object[] { input });
Console.WriteLine("DynaCore.ConditionRunner.RunCondition({0}) => {1}", input, value);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
if (e.InnerException != null)
Console.WriteLine(e.InnerException.Message);
}
}
Console.ReadLine();
}
private static CompilerResults BuildCondition(string name, string property, string condition)
{
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, string.Format("Condition_{0}_{1}.dll", name, property), true);
parameters.GenerateExecutable = false;
var results = csc.CompileAssemblyFromSource(parameters, string.Format(
#" using System.Linq;
using System;
using DESFireSDK.Dependency;
namespace DynaCore
{{
class ConditionRunner
{{
public static object RunCondition(string input)
{{
return {0};
}}
}}
}}"
, condition));
return results;
}

Related

Create Class Dynamically During Runtime

Hi im trying to create a class dending on data gathered from a user input. Once its chosen id like to the create the field names and the data types based on that and fill that class with data from document effectively creating a list of that Class.
Eg I create a class called Class1 and give it 3 Properties : ID , Name , Weight and define there types as int , string , int
Then I want to fill it with data Eg : (Example in json to show structure)
ID:{
1,
2,
3
},
Name:{
A,
B,
c
},
Weight:{
10,
20,
30
}
Ive looked into Reflection and codeDom which both enable for me to make the Class but i cannot work out how to write to that new classes properties.
Code for codeDom Version:
string className = "BlogPost";
var props = new Dictionary<string, Type>() {
{ "Title", typeof(string) },
{ "Text", typeof(string) },
{ "Tags", typeof(string[]) }
};
createType(className, props);
I Create The Properties and their Types
static void createType(string name, IDictionary<string, Type> props)
{
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "Test.Dynamic.dll", false);
parameters.GenerateExecutable = false;
var compileUnit = new CodeCompileUnit();
var ns = new CodeNamespace("Test.Dynamic");
compileUnit.Namespaces.Add(ns);
ns.Imports.Add(new CodeNamespaceImport("System"));
var classType = new CodeTypeDeclaration(name);
classType.Attributes = MemberAttributes.Public;
ns.Types.Add(classType);
foreach (var prop in props)
{
var fieldName = "_" + prop.Key;
var field = new CodeMemberField(prop.Value, fieldName);
classType.Members.Add(field);
var property = new CodeMemberProperty();
property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
property.Type = new CodeTypeReference(prop.Value);
property.Name = prop.Key;
property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName)));
property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), fieldName), new CodePropertySetValueReferenceExpression()));
classType.Members.Add(property);
}
var results = csc.CompileAssemblyFromDom(parameters, compileUnit);
results.Errors.Cast<CompilerError>().ToList().ForEach(error => Console.WriteLine(error.ErrorText));
}
This is just code i found elsewhere but if this where the code i wanted id do something like
var a = new List<BlogPost>()
and then
a."Property1" = "Title 1"
Hope this is informative
You could use reflection.
Main method to create desired object and populate its properties:
public object GenerateObject(string fullyQualifiedClassName,
Dictionary<string, object> nameToValueMap)
{
var actualObject = GetInstance(fullyQualifiedClassName);
if (actualObject == null)
return actualObject;
foreach (var prop in nameToValueMap)
{
SetPropValue(actualObject, prop.Key, prop.Value);
}
return actualObject;
}
Method to create instance of the desired class, based on fully qualified class name:
public object GetInstance(string fullyQualifiedName)
{
Type type = Type.GetType(fullyQualifiedName);
if (type != null)
return Activator.CreateInstance(type);
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
type = asm.GetType(fullyQualifiedName);
if (type != null)
return Activator.CreateInstance(type);
}
return null;
}
And last but not least, method to set property's value:
public bool SetPropValue<T>(T obj, string propName, object val)
{
if (string.IsNullOrEmpty(propName)) return false;
var prop = obj?.GetType()
.GetProperties()?
.FirstOrDefault(m => m.Name == propName);
if (prop != null)
{
prop.SetValue(obj, val);
return true;
}
return false;
}
Why not use dynamic object using expandoObject?
something like:
dynamic blogPost = new System.Dynamic.ExpandoObject();
blogPost.Tile = "Mary Water";
blogPost.Text= "your text here";

ArgumentException: Parameter cannot be null Parameter name: original in C# code

What is wrong in my code?
Whenever I try to iterate through the IEnumerable return from this CompileClasses. I got error from this line:
assembly = AppDomain.CurrentDomain.Load(memoryStream.ToArray());
But if I iterate through the object it return I don't got any compile error.
lambda_method(Closure , object , object[] )
Microsoft.Extensions.Internal.ObjectMethodExecutor.Execute(object target, object[] parameters)
public static IEnumerable<object> CompileClasses(string csharp)
{
if (string.IsNullOrEmpty(csharp))
{
throw new ArgumentNullException(nameof(csharp));
}
SyntaxTree tree = CSharpSyntaxTree.ParseText(csharp);
CompilationUnitSyntax root = tree.GetCompilationUnitRoot();
var system = SyntaxFactory.IdentifierName("System");
var systemCollections = SyntaxFactory.QualifiedName(system, SyntaxFactory.IdentifierName("Collections"));
var systemCollectionsGeneric = SyntaxFactory.QualifiedName(systemCollections, SyntaxFactory.IdentifierName("Generic"));
var systemLinq = SyntaxFactory.QualifiedName(system, SyntaxFactory.IdentifierName("Linq"));
var systemText = SyntaxFactory.QualifiedName(system, SyntaxFactory.IdentifierName("Text"));
var systemXml = SyntaxFactory.QualifiedName(system, SyntaxFactory.IdentifierName("Xml"));
var declaredUsings = root.Usings.Select(x => x.Name.ToString()).ToList();
if (!declaredUsings.Contains("System"))
{
root = root.AddUsings(SyntaxFactory.UsingDirective(system).NormalizeWhitespace());
}
if (!declaredUsings.Contains("System.Collections"))
{
root = root.AddUsings(SyntaxFactory.UsingDirective(systemCollections).NormalizeWhitespace());
}
if (!declaredUsings.Contains("System.Collections.Generic"))
{
root = root.AddUsings(SyntaxFactory.UsingDirective(systemCollectionsGeneric).NormalizeWhitespace());
}
if (!declaredUsings.Contains("System.Linq"))
{
root = root.AddUsings(SyntaxFactory.UsingDirective(systemText).NormalizeWhitespace());
}
if (!declaredUsings.Contains("System.Text"))
{
root = root.AddUsings(SyntaxFactory.UsingDirective(systemLinq).NormalizeWhitespace());
}
if (!declaredUsings.Contains("System.Xml"))
{
root = root.AddUsings(SyntaxFactory.UsingDirective(systemXml).NormalizeWhitespace());
}
tree = CSharpSyntaxTree.Create(root);
root = tree.GetCompilationUnitRoot();
var compilation = CSharpCompilation.Create("CSharp2Json",
new SyntaxTree[] { tree },
references: new[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location), MetadataReference.CreateFromFile(typeof(Uri).Assembly.Location), MetadataReference.CreateFromFile(typeof(DataSet).Assembly.Location), MetadataReference.CreateFromFile(typeof(EntityKey).Assembly.Location),
MetadataReference.CreateFromFile(typeof(XmlDocument).Assembly.Location)
},
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
);
System.Console.WriteLine(compilation);
// load compiled bits into assembly
Assembly assembly;
using (var memoryStream = new MemoryStream())
{
var result = compilation.Emit(memoryStream);
if (!result.Success)
{
throw new System.ArgumentException("Parameter cannot be null", "original");
}
assembly = AppDomain.CurrentDomain.Load(memoryStream.ToArray());
}
// instantiate object instances from assembly types
foreach (var definedType in assembly.DefinedTypes)
{
Type objType = assembly.GetType(definedType.FullName);
if (objType.BaseType?.FullName != "System.Enum")
{
object instance = null;
try
{
instance = assembly.CreateInstance(definedType.FullName);
}
catch (MissingMethodException)
{
// no default constructor - eat the exception
}
if (instance != null)
{
yield return instance;
}
}
}
}

Get line number of exception of dynamically loaded assembly

I have an external text file, it is C# class with some logic. In my main program I need to compile that file and run whatever method of that class.
Example of the external class:
using System;
public class DymamicClass
{
public string TestValue()
{
var items = new string[] { "item1", "item2" };
return items[9];
}
}
For loading external file I use these steps:
CSharpCodeProvider Compiler = new CSharpCodeProvider();
List<string> importDlls = new List<string>(new string[] { "System.dll", "System.Data.dll" });
CompilerParameters compilerPars = new CompilerParameters(importDlls.ToArray());
compilerPars.GenerateInMemory = true;
compilerPars.IncludeDebugInformation = true;
compilerPars.CompilerOptions += " /debug:pdbonly";
string path = Assembly.GetExecutingAssembly().Location;
compilerPars.ReferencedAssemblies.Add(path);
CompilerResults Results = Compiler.CompileAssemblyFromFile(compilerPars, codePath);
After the file loaded to the program, I try to execute TestValue() method from the loaded class. There is "index was outside the bounds of the array" exception in the method. I need to get the exact line that threw exception. But instead of line "return items[9];" I always get the line "public string TestValue()".
Here is sample how I process exception:
var trace = new System.Diagnostics.StackTrace(exception, true);
if (trace.FrameCount > 0)
{
var frame = trace.GetFrame(trace.FrameCount - 1);
var className = frame.GetMethod().ReflectedType.Name;
var methodName = frame.GetMethod().ToString();
var lineNumber = frame.GetFileLineNumber();
}
How to get the right line of exception?
you need to get Inner Exception. Try in this way :
using System;
using System.Collections.Generic;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
public class Folders
{
public static void Main(string[] args)
{
try
{
var source = #"using System;
public static class DymamicClass
{
public static string TestValue()
{
var items = new string[] { ""item1"", ""item2"" };
return items[9];
}
}";
CSharpCodeProvider Compiler = new CSharpCodeProvider();
List<string> importDlls = new List<string>(new string[] { "System.dll", "System.Data.dll" });
CompilerParameters compilerPars = new CompilerParameters(importDlls.ToArray());
compilerPars.GenerateInMemory = true;
compilerPars.IncludeDebugInformation = true;
compilerPars.CompilerOptions += " /debug:pdbonly";
string path = Assembly.GetExecutingAssembly().Location;
compilerPars.ReferencedAssemblies.Add(path);
CompilerResults Results = Compiler.CompileAssemblyFromSource(compilerPars, source);
Assembly assembly = Results.CompiledAssembly;
Type program = assembly.GetType("DymamicClass");
MethodInfo main = program.GetMethod("TestValue");
main.Invoke(null, null);
}
catch (Exception e)
{
var trace = new System.Diagnostics.StackTrace(e.InnerException, true);
if (trace.FrameCount > 0)
{
var frame = trace.GetFrame(trace.FrameCount - 1);
var className = frame.GetMethod().ReflectedType.Name;
var methodName = frame.GetMethod().ToString();
var lineNumber = frame.GetFileLineNumber();
}
}
}
}

C# CodeProvider Error: Constructor on type not found

I get the error: Constructor on type 'SimpleScript.Generator' not found.
I tried passing the correct parameters but i still get this error, this is my source code, and the script is a very simple piece of code that generates the Array of Element head and body. And also it is compiled successfully but it throws the error at the execution line.
string source = #"
using System;
using MSiteDLL;
namespace SimpleScript
{
public static class Generator
{
public static Document Generate(Data server)
{
"+script+ #"
Block[] blocks = {
new Block(""head"", head),
new Block(""body"", body),
};
return new Document(blocks);
}
}
}
";
Dictionary<string, string> providerOptions = new Dictionary<string, string>
{
{"CompilerVersion", "v4.0"}
};
CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
CompilerParameters compilerParams = new CompilerParameters
{
GenerateInMemory = true,
GenerateExecutable = false,
ReferencedAssemblies = {
"System.dll",
"System.Core.dll",
"MSiteDLL.dll",
}
};
CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);
if (results.Errors.Count != 0)
{
string output = "";
foreach (CompilerError y in results.Errors)
{
output += y.ErrorText + Environment.NewLine;
}
throw new Exception("Compile failed:" + output);
}
object o = results.CompiledAssembly.CreateInstance("SimpleScript.Generator");
MethodInfo mi = o.GetType().GetMethod("Generate");
Data[] parametersArray = new Data[] { server };
Document x = (Document)mi.Invoke(o, parametersArray);
return x;
Since your class is static, you should invoke the method in a static way.
So first, remove this line:
object o = results.CompiledAssembly.CreateInstance("SimpleScript.Generator");
And use those to invoke:
MethodInfo mi = Type.GetType("SimpleScript.Generator").GetMethod("Generate");
Data[] parametersArray = new Data[] { server };
Document x = (Document)mi.Invoke(null, parametersArray);

Compile Func<T1, TResult> from string on the fly

I have a string string input; with some code (all below is in that string)
var x = s.IndexOf("a");
return String.Format(s, x);
Now, I would like to achieve following scenario:
Func<string, string> f = Compile(input);
var test = "dcba - {0}";
var result = f(test);
// result = "dcba - 3";
I assume, that the actual T1, TResult are known (here: string, string), and that input is named "s". I can achieve it this way:
var input = "var x = s.IndexOf(\"a\"); return String.Format(s, x);";
var containerClass = #"using System; class TempClass {{ public string temp_func(string s){{ {0} }} }}";
var code = String.Format(containerClass, input);
// Create a new instance of the C# compiler
var compiler = new CSharpCodeProvider();
var params = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true
};
params.ReferencedAssemblies.Add("System.dll");
var results = compiler.CompileAssemblyFromSource(params, code);
Func<string, string> f;
if (results.Errors.Count == 0)
{
f = s =>
{
var myClass = results.CompiledAssembly.CreateInstance("TempClass");
return (string) myClass.GetType().
GetMethod("temp_func").
Invoke(myClass, new object[] {s});
};
// test:
Console.WriteLine(f(Console.ReadLine()));
}
But it is fairly complicated way. Is there any way to simplify this, if I know that I just want a Func<T1, TResult>, not a whole compiled assembly, instancing classes (or calling a static method on one)?
I can take this code, of course, and dress it nicely - wrap it in a generic class, get T1, TResult type names to put into the TempClass template (String.Format("public {0} temp_func({1} s)",typeof(TResult).Name, typeof(T1).Name);), but it has a feel of greasing an axle of a square wheel to make ride smoother...
I went with something like this:
public class DynamicFunction
{
private static int _counter = 0;
private const string ClassBody = "{2} public static class DynamicFunctionHost{0} {{ {1} }}";
private const string ClassName = "DynamicFunctionHost{0}";
private const string FunctionName = "func";
private const string T1FuncBody = "public static {1} func({0} param1){{ {2} }}";
public static Func<T1, TResult> Get<T1, TResult>(string funcBody, string[] referenced, string[] usingNs)
{
var code = String.Format(ClassBody, _counter,
String.Format(T1FuncBody, typeof (T1).Name, typeof (TResult).Name, funcBody),
String.Join("\n", usingNs.Select(r => String.Format("using {0};", r))));
var result = Compile(code, referenced);
var host =
result.CompiledAssembly.DefinedTypes.Single(
typeinfo => typeinfo.FullName.Equals(String.Format(ClassName, _counter)));
++_counter;
return input => (TResult) host.GetMethod(FunctionName).Invoke(null, new object[] { input });
}
private static CompilerResults Compile(string code, string[] referenced)
{
var compiler = new CSharpCodeProvider();
var parameters = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true
};
foreach (var r in referenced)
parameters.ReferencedAssemblies.Add(r);
var results = compiler.CompileAssemblyFromSource(parameters, code);
if (results.Errors.Count == 0) return results;
// else
var e = new ArgumentException("Errors during compilation", "code");
e.Data.Add("Errors", results.Errors);
throw e;
}
}
The use is fairly simple then:
var f = DynamicFunction.Get<string, string[]>("return param1.ToCharArray()", new []{"System.dll","System.Core.dll"}, new []{"System"});
var x = f("abcd"); // =[a,b,c,d]

Categories