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();
}
}
}
}
Related
If using System.Linq; is present and System.Linq is among the referenced assemblies, I expect Completions on an int[] array to return LINQ extension methods, e.g. Select<>(...), Where<>(...), etc. In fact I am only getting public methods and properties of the int[] type.
Here is the full code:
static void Main(string[] args)
{
string code = #"using System;
using System.Linq;
namespace RoslynCompletionTests
{
public static class MyTestClass1
{
public static void Print()
{
int[] array = {1,2,3,4,5,6};
var result = array.Select(i => new { I = i }).Select(v => v.I);
}
}
}";
var host = MefHostServices.Create(MefHostServices.DefaultAssemblies);
Type[] types =
{
typeof(object),
typeof(Enumerable),
typeof(IEnumerable),
typeof(Console),
typeof(Assembly),
typeof(List<>),
typeof(Type)
};
ImmutableArray<string> imports = types.Select(x => x.Namespace).Distinct().ToImmutableArray();
ImmutableArray<MetadataReference> references =
types.Select(t => MetadataReference.CreateFromFile(t.Assembly.Location) as MetadataReference)
.ToImmutableArray();
AdhocWorkspace workspace = new AdhocWorkspace(host, "Custom");
string name = "MyTestProj";
ProjectId id = ProjectId.CreateNewId(name);
ParseOptions parseOptions = new CSharpParseOptions();
CompilationOptions compilationOptions =
new CSharpCompilationOptions
(
OutputKind.DynamicallyLinkedLibrary,
usings: imports,
allowUnsafe: true);
ProjectInfo projInfo =
ProjectInfo.Create
(
id,
VersionStamp.Create(),
name,
name,
LanguageNames.CSharp,
parseOptions: parseOptions,
compilationOptions: compilationOptions,
metadataReferences: references);
Project proj = workspace.AddProject(projInfo);
SourceText text = SourceText.From(code);
Document doc = proj.AddDocument("MyDoc.cs", text);
SemanticModel semanticModel = doc.GetSemanticModelAsync().Result;
CompletionService completionService = CompletionService.GetService(doc);
string strToFind = "array.";
int idx = text.ToString().IndexOf(strToFind) + strToFind.Length;
var results = completionService.GetCompletionsAsync(doc, idx).Result;
}
Am I doing something wrong?
It turned out I had to add the references to some assemblies that form the MetadataReferences:
var assemblies = types.Select(t => t.Assembly).Concat(new[]
{
Assembly.Load("System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"),
typeof(Microsoft.CSharp.RuntimeBinder.Binder).Assembly,
});
after that everything started to work.
I believe that this is a bug in the CSharpCompilation class.
Workaround: in your code string, use:
var result = System.Linq.Enumerable.Select.Select(System.Linq.Enumerable.Select(array, i => new { I = i }), v => v.I);
...and yes, you do have to fully qualify System.Linq.Enumerable. Adding it as a using does not work.
Yuck.
I have an application where user can create its own formula it works fine
for all case but when i have DateTime Fields in my formula the compilation part of the code take around 132 ms for one formula where at the same time i
have 31 formula each with different value.
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
namespace abc
{
public class Abc
{
static void Main()
{
var code = #"
using System;
namespace First
{
public class program
{
public static int Main()
{
if(Convert.ToDateTime(""01-04-2019 10:25:00"")>Convert.ToDateTime(""01-04-2019 15:00:00""))
{
return 1;
}
else
{
return 0;
}
}
}
}";
Console.WriteLine(code);
var options = new CompilerParameters();
options.GenerateExecutable = false;
options.GenerateInMemory = false;
var provider = new CSharpCodeProvider();
var compile = provider.CompileAssemblyFromSource(options, code);
var type = compile.CompiledAssembly.GetType("First.program");
var abc = Activator.CreateInstance(type);
var method = type.GetMethod("Main");
var result = method.Invoke(abc, null);
Console.WriteLine(result); //output: abc
}
}
}
At this point
var compile = provider.CompileAssemblyFromSource(options, code);
It takes 132 ms but if don't use any date fields it takes 1 ms
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;
}
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);
I'm looking into solutions to convert a string into executeable code. My code is extremely slow and executes at 4.7 seconds. Is there any faster way of doing this?
String: "5 * 5"
Output: 25
The code:
class Program {
static void Main(string[] args) {
var value = "5 * 5";
Stopwatch sw = new Stopwatch();
sw.Start();
var test = Execute(value);
sw.Stop();
Debug.WriteLine($"Execute string at: {sw.Elapsed}");
}
private static object Execute(string content) {
var codeProvider = new CSharpCodeProvider();
var compilerParameters = new CompilerParameters {
GenerateExecutable = false,
GenerateInMemory = true
};
compilerParameters.ReferencedAssemblies.Add("system.dll");
string sourceCode = CreateExecuteMethodTemplate(content);
CompilerResults compilerResults = codeProvider.CompileAssemblyFromSource(compilerParameters, sourceCode);
Assembly assembly = compilerResults.CompiledAssembly;
Type type = assembly.GetType("Lab.Cal");
MethodInfo methodInfo = type.GetMethod("Execute");
return methodInfo.Invoke(null, null);
}
private static string CreateExecuteMethodTemplate(string content) {
var builder = new StringBuilder();
builder.Append("using System;");
builder.Append("\r\nnamespace Lab");
builder.Append("\r\n{");
builder.Append("\r\npublic sealed class Cal");
builder.Append("\r\n{");
builder.Append("\r\npublic static object Execute()");
builder.Append("\r\n{");
builder.AppendFormat("\r\nreturn {0};", content);
builder.Append("\r\n}");
builder.Append("\r\n}");
builder.Append("\r\n}");
return builder.ToString();
}
}
Well, there is an easier hack:
var _Result = new DataTable().Compute("5*5"); // _Result = 25
var _Result2 = new DataTable().Compute("5+5*5"); // _Result2 = 30
It also has more options. Please have a look at the Documentation.