Since CompileAssemblyFromSource add custom functions in a smart way was ignored im going to ask this question differently so people will bother to read it.
cutting at the chase,i am making a language by "translating" the new syntax into c# and compiling it in memory in this fashion.
using (Microsoft.CSharp.CSharpCodeProvider CodeProv =
new Microsoft.CSharp.CSharpCodeProvider())
{
CompilerResults results = CodeProv.CompileAssemblyFromSource(
new System.CodeDom.Compiler.CompilerParameters()
{
GenerateInMemory = true
},
code);
var type = results.CompiledAssembly.GetType("MainClass");
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine(output);
}
basically i am executing a "main" function written inside the code variable.
and i am using some functions in the code variable i would like to include without adding it as a string at the bottom like this:
code += #"public void Write(string path, object thevar)
{
if (thevar.GetType() == typeof(string))
{
System.IO.File.WriteAllText(path,(string)thevar);
}
if (thevar.GetType() == typeof(string[]))
{
System.IO.File.WriteAllLines(path,(string[])thevar);
}
}";
Can i somehow add a class from my Actual main project in VS and let the compiled in memory code access it? without adding it as a string.
You can embed your source code file(s) as resources. With this technique you can edit the file in Visual Studio and access the contents of the files as if it was a string during run-time.
This link shows how to do it:
https://stackoverflow.com/a/433182/540832
Related
With Mono.Cecil it looks quite simple when we can just set the Body of the target MethodDefinition to the Body of the source MethodDefinition. For simple methods, that works OK. But for some methods whereas a custom type is used (such as to init a new object), it won't work (with an exception thrown at the time writing the assembly back).
Here is my code:
//in current app
public class Form1 {
public string Test(){
return "Modified Test";
}
}
//in another assembly
public class Target {
public string Test(){
return "Test";
}
}
//the copying code, this works for the above pair of methods
//the context here is of course in the current app
var targetAsm = AssemblyDefinition.ReadAssembly("target_path");
var mr1 = targetAsm.MainModule.Import(typeof(Form1).GetMethod("Test"));
var targetType = targetAsm.MainModule.Types.FirstOrDefault(e => e.Name == "Target");
var m2 = targetType.Methods.FirstOrDefault(e => e.Name == "Test");
var m1 = mr1.Resolve();
var m1IL = m1.Body.GetILProcessor();
foreach(var i in m1.Body.Instructions.ToList()){
var ci = i;
if(i.Operand is MethodReference){
var mref = i.Operand as MethodReference;
ci = m1IL.Create(i.OpCode, targetType.Module.Import(mref));
}
else if(i.Operand is TypeReference){
var tref = i.Operand as TypeReference;
ci = m1IL.Create(i.OpCode, targetType.Module.Import(tref));
}
if(ci != i){
m1IL.Replace(i, ci);
}
}
//here the source Body should have its Instructions set imported fine
//so we just need to set its Body to the target's Body
m2.Body = m1.Body;
//finally write to another output assembly
targetAsm.Write("modified_target_path");
The code above was not referenced from anywhere, I just tried it myself and found out it works for simple cases (such as for the 2 methods Test I posted above). But if the source method (defined in the current app) contains some Type reference (such as some constructor init ...), like this:
public class Form1 {
public string Test(){
var u = new Uri("SomeUri");
return u.AbsolutePath;
}
}
Then it will fail at the time writing the assembly back. The exception thrown is ArgumentException with the following message:
"Member 'System.Uri' is declared in another module and needs to be imported"
In fact I've encountered a similar message before but it's for method calls like (string.Concat). And that's why I've tried importing the MethodReference (you can see the if inside the foreach loop in the code I posted). And really that worked for that case.
But this case is different, I don't know how to import the used/referenced types (in this case it is System.Uri) correctly. As I know the result of Import should be used, for MethodReference you can see that the result is used to replace the Operand for each Instruction. But for Type reference in this case I totally have no idea on how.
All my code posted in my question is fine BUT not enough. Actually the exception message:
"Member 'System.Uri' is declared in another module and needs to be imported"
complains about the VariableDefinition's VariableType. I just import the instructions but not the Variables (which are just referenced exactly from the source MethodBody). So the solution is we need to import the variables in the same way as well (and maybe import the ExceptionHandlers as well because an ExceptionHandler has CatchType which should be imported).
Here is just the similar code to import VariableDefinition:
var vars = m1.Body.Variables.ToList();
m1.Body.Variables.Clear();
foreach(var v in vars){
var nv = new VariableDefinition(v.Name, targetType.Module.Import(v.VariableType));
m1.Body.Variables.Add(nv);
}
I work in a research group and I've been tasked with adding in scripting functionality to a data acquisition program. Ideally, I want to have the ability to write scripts while the data acquisition software is running (and save those scripts as files on the go). A command line might also be nice.
I'm not very experienced at all with C#, but I do have quite a bit of coding experience in other language (Objective-C, Python). I saw this link https://blogs.msdn.microsoft.com/cdndevs/2015/12/01/adding-c-scripting-to-your-development-arsenal-part-1/ which details the "Roselyn Scripting Package" but I'm not sure if that's my best option.
Can anybody suggest the easiest way of getting full scripting functionality? (I'm trying to avoid losing months of my life here =p). Links to start at/advice is much appreciated.
Thanks!
You posted an interesting link for me because I just prototyped something like that some weeks ago, but have not implemented yet. My goal is to create a C# "immediate" console on a webpage.
Have some minor issues regarding loading some assemblies programatically and have to reference them explicitly.
Here is the code behind, please later post your solution, I would be interested to know.
This alows to write c# code at runtime and also get a String return.
protected void getImmediateResult_Click(object sender, EventArgs e)
{
//building the code
string source = #"using System;
class MyType{
public static String Evaluate(){
<!expression!>
}}";
string expression = this.txtimmediate.Text;
string finalSource = source.Replace("<!expression!>", expression);
textcodeCheck.Text = finalSource;
var compileUnit = new CodeSnippetCompileUnit(finalSource);
//preparing compilation
CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();
// Create the optional compiler parameters
//this correctly references the application but no System.Web etc
string[] refArray = new string[2];
UriBuilder uri = new UriBuilder(Assembly.GetExecutingAssembly().CodeBase);
refArray[0] = uri.Path;
//this works
refArray[1] = "System.Web" + ".dll";
////NOT WORKING for non microsoft assemblies
//var allRefs = Assembly.GetExecutingAssembly().GetReferencedAssemblies();
//string[] refArray = new string[allRefs.Length + 1];
//int i = 1;
//foreach (AssemblyName refer in allRefs)
//{
// refArray[i] = refer.Name + ".dll";
// i++;
//}
var compilerParameters = new CompilerParameters(refArray);
CompilerResults compilerResults = provider.CompileAssemblyFromDom(compilerParameters, compileUnit);
if (compilerResults.Errors.Count > 0)
{
//1st error
this.txtResult.Text = compilerResults.Errors[0].ErrorText;
return;
}
//running it
Type type = compilerResults.CompiledAssembly.GetType("MyType");
MethodInfo method = type.GetMethod("Evaluate");
String result = (String)method.Invoke(null, null);
this.txtResult.Text = result;
}
If you're willing to use IronPython, you can execute scripts directly in C#:
using IronPython.Hosting;
using Microsoft.Scripting.Hosting;
private static void doPython()
{
ScriptEngine engine = Python.CreateEngine();
engine.ExecuteFile(#"test.py");
}
Get IronPython here.
I imported the taglib-sharp dll (that had been copied to the bin/debug folder of my project) in my C# application and then used types and methods from the library in the following way:
using TagLib;
private void method()
{
TagLib.File file = TagLib.File.Create("C:\\temp\\some.mp3");
TagLib.Tag tag = file.GetTag(TagLib.TagTypes.Id3v2);
}
Now I want to link the dll dynamically. How can I implement the same functional in this case?
That, what I've tried:
using System.Reflection
private void method()
{
Assembly TagLib = Assembly.Load("taglib-sharp");
Type TagLibFile = TagLib.GetType("File");
dynamic LibFile = Activator.CreateInstance(TagLibFile);
TagLibFile file = LibFile.Create("c:\\temp\\some.mp3");
}
In this implementation, VisualStudio says that I can't use the tagLibFile variable as a type. I supposed that when I get a type from dll, I will be able to create variables of this type.
By the way, is this approach is correct?
P.S. Also, I tried to use the invoke method, but I was not sure what object I should pass as a first argument.
UPD
Based on #nawfal's awnser below, I've got the following working code:
using System.Reflection
private void method()
{
Assembly TagLib = Assembly.Load("taglib-sharp");
// get the File type
var fileType = TagLib.GetType("TagLib.File");
// get the overloaded File.Create method
var createMethod = fileType.GetMethod("Create", new[] { typeof(string) });
// get the TagTypes method that contains Id3v2 field
Type tagTypes = TagLib.GetType("TagLib.TagTypes");
// get the overloaded File.GetTag method
var getTagMethod = fileType.GetMethod("GetTag", new[] {tagTypes});
// obtain the file
dynamic file = createMethod.Invoke(null, new[] { "C:\\temp\\some.mp3" });
// obtain the Id3v2 field value
FieldInfo Id3TagField = tagTypes.GetField("Id3v2");
var Id3Tag = Id3TagField.GetValue(tagTypes);
// obtain the actual tag of the file
var tag = getTagMethod.Invoke(file, new[] { Id3Tag });
}
You should be doing something like this:
private void method()
{
var assembly = Assembly.Load("taglib");
var type = assembly.GetType("namespace.File"); // namespace qualified class name
// assuming you only have one Create method, otherwise use reflection to resolve overloads
var method = type.GetMethod("Create");
dynamic file = method.Invoke(null, new[] { "C:\\temp\\some.mp3" }); // null for static methods
var tag = file.GetTag(TagLib.TagTypes.Id3v2); // not sure if you can pass those params,
// may be do reflection to get them too
}
Kindly rethink if you want it to be dynamic. If you can reference the dll then you can still get the benefits of strong typing.
Save it as object.
object file = LibFile.Create(fi.FullName);
Should work.
Dynamic loading dlls works much different.
I'm writing a 3rd party app that needs to read in .cs files and be able to manipulate classes, then ultimately save back to file.
The type of code I am looking at would be something like:
var classManager = new classManager();
var classes = classManager.LoadFromFile(filePath);
var class = classes[0]; // Just illustrating more than 1 class can exist in a file
var prop = new ClassProperty {Type=MyType.GetType() };
prop.AddGet("return x+y < 50");
//stuff like prop.ReadOnly = true;
class.AddProperty(prop);
var method = new ClassMethod {signature="int id, string name"};
method.MethodBody = GetMethodBodyAsString(); //not writing out an entire method body here
class.AddMethod(method);
class.SaveToFile(true); //Format code
Does such a library exist?
The .NET Compiler Platform Roslyn is what you're looking for. It supports parsing and editting cs files. Check out this post for an example
On my form I have a button click
private void button1_Click(object sender, EventArgs e)
{
do something
}
How on the click would I load my do something from a text file, for example my text file looks like this:
MessageBox.Show("hello");
label1.Text = "Hello";
on click it does everything in my text file, if possible.
Here is a very simple example, just to prove this is possible. Basically, you use CodeDomProvider to compile source at runtime, then execute using reflection.
var provider = CodeDomProvider.CreateProvider("C#");
string src=#"
namespace x
{
using System;
public class y
{
public void z()
{
Console.WriteLine(""hello world"");
}
}
}
";
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
type.GetMethod("z").Invoke(instance, null);
}
Edit
As #Agat points out, the OP seems to require a sort of scripting framework (it makes use of label1, a property of the current object), whereas my answer above obviously does not provide that. The best I can think of is a limited solution, which would be to require dependencies to be specified explicitly as parameters in the "script". Eg, write the scripted code like this:
string src = #"
namespace x
{
using System.Windows;
public class y
{
public void z(Label label1)
{
MessageBox.Show(""hello"");
label1.Text = ""Hello"";
}
}
}
";
Now you can have the caller examine the parameters, and pass them in from the current context, again using reflection:
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
var method = type.GetMethod("z");
var args = new List<object>();
// assume any parameters are properties/fields of the current object
foreach (var p in method.GetParameters())
{
var prop = this.GetType().GetProperty(p.Name);
var field = this.GetType().GetField(p.Name);
if (prop != null)
args.Add(prop.GetValue(this, null));
else if (field != null);
args.Add(field.GetValue(this));
else
throw new InvalidOperationException("Parameter " + p.Name + " is not found");
}
method.Invoke(instance, args.ToArray());
}
Like the other answers have stated, it isn't an easy thing to implement and can possibly be done through reflection depending on how advanced your scripts are.
But no one #BrankoDimitrijevic mentioned Roslyn and it is a great tool. http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx
It hasn't been updated in quite awhile (Sept.2012) and doesn't have all of the features of C# implemented, however, it did have a lot of it implemented when I played around with this release.
By adding your assembly as a reference to the scripting session, you're able to gain access to all of your assembly's types and script against them. It also supports return values so you can return any data that a scripted method generates.
You can find what isn't implemented here.
Below is a quick and dirty example of Roslyn that I just wrote and tested. Should work right out of box after installing Roslyn from NuGet. The small bloat at the initialization of the script engine can easily be wrapped up in a helper class or method.
The key is passing in a HostObject. It can be anything. Once you do, your script will have full access to the properties. Notice that you just call the properties and not the host object in the script.
Basically, your host object will contain properties of the data you need for your script. Don't necessarily think of your host object as just a single data object, but rather a configuration.
public class MyHostObject
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public class RoslynTest
{
public void Test()
{
var myHostObject = new MyHostObject
{
Value1 = "Testing Value 1",
Value2 = "This is Value 2"
};
var engine = new ScriptEngine();
var session = engine.CreateSession(myHostObject);
session.AddReference(myHostObject.GetType().Assembly.Location);
session.AddReference("System");
session.AddReference("System.Core");
session.ImportNamespace("System");
// "Execute" our method so we can call it.
session.Execute("public string UpdateHostObject() { Value1 = \"V1\"; Value2 = \"V2\"; return Value1 + Value2;}");
var s = session.Execute<string>("UpdateHostObject()");
//s will return "V1V2" and your instance of myHostObject was also changed.
}
}
No. You can not.
At least in any simple way.
The thing you want is something like eval('do something') from javascript.
That's not possible to do with C#. C# is a language which needs compilation before execution unlike javascript (for instance).
The only way to implement that is to build your own (pretty complicated as for beginner) parser and execute it in such way.
UPDATED:
Actually, as JDB fairly noticed, that's really not the only way. I love programming! There are so many ways to make a freakky (or even sometimes that really can be necessary for some custom interesting tasks (or even learning)!) code. he he
Another approach I've got in my mind is building some .cs file, then compiling it on-the-fly and working with it as some assembly or some other module. Right.