This code snippet was written to compile the code at run-time.
The compiled code works like any other program.
Reflection can be accessed with.
I want to do a little different things.
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));
}
}
For Example :
My Code is Textbox1.Text="123"; it is in a file or in database.
There is a form.A Textbox in form.
I want to use string code as part of my program on runtime.
To do this, you can change the function name to string_Function instead of Main. Save the above code snippet as .cs file. Say Code_Snippet.cs. Now add this file to your MainProject. Wherever you want to use this code snippet you have to write line of code e.g. Code_Snippet.String_Function(TextBox.Text);
Related
Below is my code to create a directory in my PC.
using System;
using System.IO;
using System.Text;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
DirectoryInfo dataDir = new DirectoryInfo(#"C:\CsharpData");
Console.WriteLine(dataDir.Attributes);
Console.ReadLine();
}
}
}
But, the result looks like this.
Attribute is equal to -1, and I can't get my desired directory.
Can anyone let me know what my mistake is?
Use below code. You need to use create ,ethod for creating the directory.
DirectoryInfo dataDir = new DirectoryInfo(#"C:\CsharpData");
if(!dataDir.Exists)
{
dataDir.Create();
}
System.Console.WriteLine(dataDir.Attributes);
System.Console.ReadLine();
We have an issue where simple c# code executed via CSharpCodeProvider doesn't work the same on running local command line and docker.
Code example is below, and it will not return any Types of the assembly when run on Roslyn, but works fine locally.
I have literally no idea how to debug this from here - any help welcome!
using System;
using Microsoft.CodeDom.Providers.DotNetCompilerPlatform;
using System.IO;
using System.Reflection;
using System.Text;
using System.CodeDom.Compiler;
namespace TestContainerIssue
{
class Program
{
static void Main(string[] args)
{
using (var codeProvider = new CSharpCodeProvider())
{
var compilerParameters = new CompilerParameters
{
GenerateExecutable = false,
GenerateInMemory = true
};
compilerParameters.CompilerOptions = String.Format("/lib:\"{0}\"", Path.GetDirectoryName(Uri.UnescapeDataString((new UriBuilder(Assembly.GetExecutingAssembly().CodeBase)).Path)));
Console.WriteLine(compilerParameters.CompilerOptions);
compilerParameters.ReferencedAssemblies.Add("System.dll");
compilerParameters.ReferencedAssemblies.Add("System.Core.dll");
CompilerResults compilerResults = codeProvider.CompileAssemblyFromSource(compilerParameters, ExecutionWrapperCode);
if (compilerResults.Errors.HasErrors)
{
StringBuilder errors = new StringBuilder();
foreach (CompilerError error in compilerResults.Errors)
errors.AppendLine(error.ErrorText);
throw new Exception(errors.ToString());
}
Console.WriteLine(compilerResults.PathToAssembly);
Assembly assembly = compilerResults.CompiledAssembly;
Console.WriteLine(assembly.FullName);
Console.WriteLine("Types:");
foreach (Type t in assembly.GetTypes())
{
Console.WriteLine(t);
}
Type type = assembly.GetType("Validation.Execution");
Console.WriteLine("Type:");
Console.WriteLine(type); //Empty when run in Docker (both mcr.microsoft.com/windows:1909 and mcr.microsoft.com/windows/servercore:ltsc2019
var methodInfo = type.GetMethod("Execute");
Console.WriteLine(methodInfo);
}
}
private const string ExecutionWrapperCode = #"
using System;
namespace Validation
{
public static class Execution
{
public static string Execute()
{
return ""test"";
}
}
}";
}
}
I tried below docker file (I tried two windows image: mcr.microsoft.com/windows:1909 and mcr.microsoft.com/windows/servercore:ltsc2019
FROM mcr.microsoft.com/windows/servercore:ltsc2019
ADD /bin/Debug /
ENTRYPOINT TestContainerIssue.exe
EDIT: I built the two dlls, and compared them in dotPeek - as you can see the one in Docker is missing the namespace. They are exactly the same bytes length though.
It turns out this line of code:
compilerParameters.CompilerOptions = String.Format("/lib:\"{0}\"", Path.GetDirectoryName(Uri.UnescapeDataString((new UriBuilder(Assembly.GetExecutingAssembly().CodeBase)).Path)));
was causing the problem. I don't know why it causes it and doesn't throw an error in compiler, but perhaps will help someone else.
Was
I'm trying to compile code at runtime in C#, then from the compiled code call a function or initialize a class which is defined in the original code.
The code I currently have:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
namespace CTFGame
{
class Program
{
static void Main(string[] args)
{
string code = #"
using System;
namespace CTFGame
{
public class MyPlayer
{
public static void Main ()
{
Console.WriteLine(""Hello world"");
}
/*public void DoTurn ()
{
Program.SayHello();
}*/
}
}
";
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.HasErrors)
{
string errors = "";
foreach (CompilerError error in results.Errors)
{
errors += string.Format("Error #{0}: {1}\n", error.ErrorNumber, error.ErrorText);
}
Console.Write(errors);
}
else
{
Assembly assembly = results.CompiledAssembly;
Type program = assembly.GetType("CTFGame.MyPlayer");
MethodInfo main = program.GetMethod("Main");
main.Invoke(null, null);
}
}
public static void SayHello()
{
Console.WriteLine("I'm awesome ><");
}
}
}
Now, Running the runtime loaded method 'Main' is a success, and the message "Hello world" is printed. The problem starts here: in the original code I have a method called "SayHello". I want to call this method from my runtime loaded code.
If I uncomment the "DoTurn" method, a compiler error will show in runtime:
Error #CS0103: The name 'Program' does not exist in the current context
My question is - is this possible, and how?
Putting the runtime loaded code in the same namespace doesn't help (and that makes sense), so what is the correct way to do that?
Thanks.
Adding a reference to the current assembly solved the problem:
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
//The next line is the addition to the original code
parameters.ReferencedAssemblies.Add(Assembly.GetEntryAssembly().Location);
More about:
Compiling c# at runtime with user defined functions
I have a string. For example
string str="if(a>b) {return a;} else {return b;}"
I want to evaluate or make function, say func(int a, int b) which will have the code of 'str'.
you may need to use CSharpCodeProvider as in this answer
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));
}
}
In general, this is not an easy thing to do, but the System.CodeDom namespace is where your journey will start.
Look at the following CodeProject article on the matter as a start: http://www.codeproject.com/Articles/26312/Dynamic-Code-Integration-with-CodeDom
The basics of it is as follows (as taken from the codeproject article):
private static Assembly CompileSource( string sourceCode )
{
CodeDomProvider cpd = new CSharpCodeProvider();
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("System.dll");
//cp.ReferencedAssemblies.Add("ClassLibrary1.dll");
cp.GenerateExecutable = false;
// Invoke compilation.
CompilerResults cr = cpd.CompileAssemblyFromSource(cp, sourceCode);
return cr.CompiledAssembly;
}
The resultant assembly will have the class/method/code you are interested in, and then you can use reflection to call your method. Since your example just uses a code fragment, you will probably have to wrap it in a class/method before passing it to this method.
I hope that helps, but dynamic code generation in C# is not easy and this is just a start.
I'm a little confused as to how to go about adding references when using Roslyn to execute C# scripts.
I'm using the latest version of the API (1.2.20906.2), installed via NuGet.
I've searched lots of posts on Google, but there is significant change in the API since many of the examples I've found.
To illustrate what I'm trying to achieve:
using System;
using Roslyn.Scripting.CSharp;
namespace Test.ConsoleApp
{
public class Program
{
static void Main(string[] args)
{
new ScriptRunner().RunScripts();
}
}
public class ScriptRunner
{
public void RunScripts()
{
var engine = new ScriptEngine();
var session = engine.CreateSession();
session.AddReference("System");
session.AddReference("System.Linq");
// The following script runs successfully
session.Execute(#"using System;
var arr = new[] {1, 2, 6};
foreach (var i in arr)
{
if(i > 1)
{
Console.WriteLine(i);
}
}"
);
// The following script using Linq fails
session.Execute(#"using System;
using System.Linq;
var arr = new[] {1, 2, 6};
var arrResult = arr.Where(x => x > 1);
foreach (var i in arrResult)
{
Console.WriteLine(i);
}"
);
Console.ReadLine();
}
}
}
UPDATE - Included modification suggested in answer, plus referencing by path to DLL:
using System;
using Roslyn.Scripting.CSharp;
namespace Test.ConsoleApp
{
public class Program
{
static void Main(string[] args)
{
new ScriptRunner().RunScripts();
}
}
public class ScriptRunner
{
public void RunScripts()
{
var engine = new ScriptEngine();
var session = engine.CreateSession();
session.AddReference("System");
session.AddReference("System.Core"); // This reference is required to use Linq language features
session.AddReference("System.Linq");
session.Execute(#"using System;
using System.Linq;
var arr = new[] {1, 2, 6};
var arrResult = arr.Where(x => x > 1);
foreach (var i in arrResult)
{
Console.WriteLine(i);
}"
);
// Example use of adding reference to external project by path to dll
session.AddReference(#"E:\SVN\CSharpRoslynTesting\CSharpRoslynTesting\Test.ExternalLibraryTest\bin\Debug\Test.ExternalLibraryTest.dll");
session.Execute(#"using System;
using Test.ExternalLibraryTest;
var externalTestClass = new ExternalTestClass();
externalTestClass.TestOutput();
"
);
Console.ReadLine();
}
}
}
It is working for me, although I am using v1.2.20906.1. I didn't try your code, I just executed version I wrote for Roslyn presentation one month back.
Try to add System.Core:
session.AddReference("System.Core");
That's the only significant difference I can see so far.
Update: I just tried your code and missing reference I mentioned above was indeed the culprit. You even get nice exception:
(5,51): error CS1061: 'int[]' does not contain a definition for
'Where' and no extension method 'Where' accepting a first argument of
type 'int[]' could be found (are you missing a using directive or an
assembly reference?)