I want to implement C# as the scripting language in my game.
My problem is, that my script will not compile if I want to use classes defined in the game core (exe).
The script looks like this:
using System;
using ConsoleApplication1;
class Script
{
private static void Call()
{
Console.WriteLine("called");
}
public static void Init()
{
Console.WriteLine("Script");
Call();
GameObject myO; // THIS IS WHAT I WANT TO GET WORKED,
//IF THIS IS COMMENTED OUT, IT COMPILES FINE, GAMEOBJECT
// IS DEFINED IN THE "ConsoleApplication1" NAMESPACE.
}
}
The script is compiled like in the MDX sample:
static void Main(string[] args)
{
CodeDomProvider provider = new CSharpCodeProvider();
CompilerParameters cp = new CompilerParameters();
//cp.CompilerOptions = "/target:library";
cp.GenerateExecutable = false;
cp.GenerateInMemory = true;
cp.IncludeDebugInformation = false;
cp.ReferencedAssemblies.Add("ConsoleApplication2.exe");
cp.ReferencedAssemblies.Add("System.dll");
CompilerResults cr = provider.CompileAssemblyFromFile(cp, "script.cs");
if (!cr.Errors.HasErrors)
{
Console.WriteLine("Success");
cr.CompiledAssembly.GetType("Script").GetMethod("Init").Invoke(null, null);
}
Console.ReadLine();
}
Is there any way to call functions or create objects defined in the "ConsoleApplication1" namespace via the script?
This is a daily programming problem. It's not working and you think it should be working. So break it down. Instead of working on a big problem, work on a smaller problem. Just tackle trying to compile the script outside of your program. Once you get that working, then you can try to compile it as a script from inside your program, knowing that you've got the basic problem of references and compiler issues sorted out.
Something like:
csc /reference:ConsoleApplication1.exe script.cs
From the looks of it, it might be as simple as changing the reference from ConsoleApplication2.exe to ConsoleApplication1.exe.
Related
I am currently reading the "500 Lines or Less" book, the chapter for creating a Template Engine from Ned Batchelder.
Their example is using Python. In their template engine they are building code as a string and then they are calling exec (docs) to evaluate the string as Python code.
def get_globals(self):
"""Execute the code, and return a dict of globals it defines."""
# A check that the caller really finished all the blocks they started.
assert self.indent_level == 0
# Get the Python source as a single string.
python_source = str(self)
# Execute the source, defining globals, and return them.
global_namespace = {}
exec(python_source, global_namespace)
return global_namespace
This is very convenient, because they can easily evaluate expressions in the template such as {{object.property.property}}
With C# as my main programming language I am wondering how can this be achieved (in the context of building a template engine as in the book)?
Research and thoughts
First I don't believe there is an exec equivalent in C#.
One way I can think of it is to recursively use Reflection to get the List of properties of an object (handling checks for Null References), but I don't like this from performance point of view.
Another way is to use Roslyn's ScriptEngine class (which I haven't used so correct me if I am wrong). But I am afraid that this won't be good because this is supposed to be a library and it won't be able to be used with older versions of C# and .NET. Example
Q: First I don't believe there is an exec equivalent in C#.
As for compling C# code, CS-Script library can be used to achieve this in various ways.
For example:
dynamic script = CSScript.Evaluator
.LoadCode(#"using System;
using Your.Custom.Relevant.Namespace;
public class Executer
{
public object Execute()
{
return SomeStaticClass.array[123];
}
}");
int result = script.Execute();
//shorter way
int a = (int)CSScript.Evaluator.Evaluate("some.namespace.SomeStaticClass.array[123]");
Read more here: http://www.csscript.net/
CS-Script isn't made for templating.
Unless you create it yourself by manipulating the strings before you compile them.
But how can I pass some Context for the template engine
You can pass a context into a function like this:
dynamic script = CSScript.Evaluator
.LoadCode(#"
using System;
using Namespace.Of.The.Context;
public class Executer {
public string Execute(Context ctx) {
return ctx.Person.Firstname + ctx.Person.Lastname;
}
}");
int result = script.Execute(new Context(new Person("Rick", "Roll")));
Q: Can I call CSScript from a normal C# application lets say a Web App?
A: Yes.
S-Script currently targets Microsoft implementation of CLR (.NET
2.0/3.0/3.5/4.0/4.5) with full support on Mono.
Basically if it runs C#, it can be compiled accordingly to the .net-framework that the library is executed on, so if your project is ran on .net4.5, any feature of that .net version is available including any external references in your project too.
You can use Microsoft.CSharp.CSharpCodeProvider in order to compile code on fly.
https://msdn.microsoft.com/en-us/library/microsoft.csharp.csharpcodeprovider.aspx
Like this:
static void Main(string[] args)
{
string source =
#"
namespace Test
{
public class Test
{
public void HelloWorld()
{
System.Console.WriteLine(""Hello World"");
}
}
}
";
var options = new Dictionary<string, string> { {"CompilerVersion", "v3.5"} };
var provider = new CSharpCodeProvider(options);
var compilerParams = new CompilerParameters{GenerateInMemory = true, GenerateExecutable = false };
var results = provider.CompileAssemblyFromSource(compilerParams, source);
var method = results.CompiledAssembly.CreateInstance("Test.Test");
var methodInfo = method.GetType().GetMethod("HelloWorld");
methodInfo.Invoke(method, null);
}
So, I have strange question (maybe so stupid), but...
So, my task.
I have same class which gives me same functionality. So, in the main program, which I realize (yes, it's client-server app)) , I want to dynamically create ".exe wrapper" for this class - simplest code like this:
class Program
{
private SameClass mySameClass;
static void Main(string[] args)
{
mySameClass = new mySameClass(args);
Console.Readline();
}
}
In general, I want to create main app which creates slaves in the independent proccesses via dynamically code generation.
So, how to make it and control it?
Thank you.
So SameClass is supposed to contain the same functions and functionality as the process you want to have run multiple times... I guess what you need are Threads.
Not too sure what you mean by dynamically but let's say you have an event that triggers whenever you need a new SameClass process. Just run
SameClass newClone = new SameClass(args);
Trhead _thread = new Thread(new ThreadStart(newClone.Start));
_trhead.Start();
You can probably do this a little more elegant and refactor within SameClass but it's pretty hard to understand your question, so I guess this is the best I can do you hopefully answer your question.
Found solution based on CodeDom. Yes, It doesn't need reflection, sorry.
Code example:
using System;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Diagnostics;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
var compiler = new CSharpCodeProvider();
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, "foo.exe", true);
parameters.CompilerOptions = "/platform:x64";
parameters.GenerateExecutable = true;
CompilerResults results = compiler.CompileAssemblyFromSource(parameters,
#"using System;
class Program {
public static void Main(string[] args) {
Console.WriteLine(""Hello world!"");
Console.ReadLine();
}
}");
var testProcess = new Process();
testProcess.StartInfo.FileName = results.CompiledAssembly.CodeBase;
testProcess.Start();
Console.WriteLine("I've run slave!");
Console.ReadLine();
}
}
}
I found a technet blog article the said it was possible to have PowerShell use C# code.
Article: Using CSharp (C#) code in Powershell scripts
I found the format I need to get the C# code to work in PowerShell, but if it don't pass the Main method an argument ([namespace.class]::Main(foo)) the script throws an error.
Is there a way I can pass a string of "on" or "off" to the main method, then depending on which string is passed run an if statement? If this is possible can you provide examples and/or links?
Below is the way I'm currently trying to structure my code.
$Assem = #( //assemblies go here)
$source = #"
using ...;
namespace AlertsOnOff
{
public class onOff
{
public static void Main(string[] args )
{
if(args == on)
{//post foo }
if(arge == off)
{ //post bar }
}
"#
Add-Type -TypeDefinition $Source -ReferencedAssumblies $Assem
[AlertsOnOff.onOff]::Main(off)
#PowerShell script code goes here.
[AlertsOnOff.onOff]::Main(on)
Well to start, if you are going to compile and run C# code, you need to write valid C# code. On the PowerShell side, if you invoke Main from PowerShell, you need to pass it an argument. PowerShell will automatically put a single argument into an array for you, but it won't insert an argument if you don't have one. That said, its not clear why this is in a Main method. It's not an executable. It could very well just have two static methods, TurnOn and TurnOff. The code below compiles and runs, modify as you see fit:
$source = #"
using System;
namespace AlertsOnOff
{
public class onOff
{
public static void Main(string[] args)
{
if(args[0] == `"on`")
{
Console.WriteLine(`"foo`");
}
if(args[0] == `"off`")
{
Console.WriteLine(`"bar`");
}
}
}
}
"#
Add-Type -TypeDefinition $Source
[AlertsOnOff.onOff]::Main("off")
# Other code here
[AlertsOnOff.onOff]::Main("on")
This seems like there is just a simple error that I cannot figure out. I am using IronPython in a C# WPF application and am getting the following error when trying to run a function from a custom C# class:AttributeError: 'MyScriptingFunctions' object has no attribute 'Procedure'.
The python script I'm running is very simple and has two lines. Line 1 executes fine, the error occurs on line 2.
txt.Text = "some text"
MyFunc.Procedure(5)
MyScriptingFunctions.cs:
class MyScriptingFunctions
{
public MyScriptingFunctions() {}
public void Procedure(int num)
{
Console.WriteLine("Executing procedure " + num);
}
}
Here is how I setup the IronPython engine:
private void btnRunScript_Click(object sender, RoutedEventArgs e)
{
MyScriptingFunctions scriptFuncs = new MyScriptingFunctions();
ScriptEngine engine = Python.CreateEngine();
ScriptScope scope = engine.CreateScope();
ScriptRuntime runtime = engine.Runtime;
runtime.LoadAssembly(typeof(String).Assembly);
runtime.LoadAssembly(typeof(Uri).Assembly);
//Set Variable for the python script to use
scope.SetVariable("txt", fullReadResultsTextBox);
scope.SetVariable("MyFunc", scriptFuncs);
string code = this.scriptTextBox.Text;
try
{
ScriptSource source = engine.CreateScriptSourceFromString(code, SourceCodeKind.Statements);
source.Execute(scope);
}
catch (Exception ex)
{
ExceptionOperations eo;
eo = engine.GetService<ExceptionOperations>();
string error = eo.FormatException(ex);
MessageBox.Show(error, "There was an Error");
return;
}
}
I am simply setting two variables:txt which is of type System.Windows.Controls.TextBox, and MyFunc which is an object of my custom class MyScriptingFunctions.
What am I doing wrong and why does the python script execute the TextBox methods correctly and not my custom class's methods?
The only think I can see that might be an issue, or just a copy-paste error, is that the MyScriptingFunctions in not public. That shouldn't be an issue because you're passing an instance in, not trying to import the class, but it's worth a try. Otherwise everything looks fine.
The documentation for compiling C++/CLI using the frameworks CodeDom classes is somewhat lacking.
The background: I'm trying to write system tests for a piece of software I have written. The software generates code, and I want to test that the code can be compiled, and that the compiled code works as intended.
The code to be compiled and tested resides in a couple of source files. I want to compile the source file into an assembly, and then dynamically invoke the methods in it. I have experimented with doing the same C# sources, and I had a successful build and test in minutes. The C++ compiler always throws System.NotImplementedException.
Here's the code that blows up on me:
namespace CppCompilerTest {
class Program {
static void Main(string[] args) {
try {
string strSourceDir = #"D:\Projects\CppCompilerTest\CppSource";
using (var codeProvider = new CppCodeProvider7()) {
var options = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = false };
options.ReferencedAssemblies.Add("System.dll");
CompilerResults results = codeProvider.CompileAssemblyFromFile(
options,
new[] {
Path.Combine(strSourceDir, "SourceFile1.cpp"),
Path.Combine(strSourceDir, "SourceFile2.cpp")
}
);
}
} catch(Exception ex) {
Console.WriteLine(ex);
}
Console.WriteLine("Press any key to continue");
Console.ReadKey();
}
}
}
Have I totally overlooked something? I'm wondering if maybe the cl.exe way is the faster route...