 |
|
 |
How does one go about exposing a public class to the evaluated code (e.g. to enable scripting for a program)
Namely hooking events.
Cheers :D
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Mark (and others),
I noticed your past on CodeProject.com regarding the code that can be used to create an "Eval"-type function in C#. I'm trying to utilize the "EvalToObject" method in that I want to convert a statement similar to:
document.Reports.Add(new EandG_MonthlyReport(document));
where the class name "EandG_MonthReport" is a string variable.
How would I set this up?
I've tried code similar to:
object o = Evaluator.EvalToObject("document.Reports.Add(new " + WebReport.PDFReportAppClassName.ToString() + "(document))");
where "WebReport.PDFReportAppClassName.ToString() " is the name of the class I want to instantiate, but I get errors stating " variable is not defined."
Your help is greatly appreciated!
Thank you, Kevin Gravelle
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
think u first! today i made a test ,but i find the same code didn't work the same. when i do it in a winform ,it's ok. but when i do it in a webForm,it gives message like "can't find "customer.dll"
the code: public Assembly CreateAssembly(string sourceCode,string dllName) { if (sourceCode.Length == 0) { throw new System.Exception("no source code"); }
CodeDomProvider codeProvider = null; codeProvider = new CSharpCodeProvider();
ICodeCompiler compiler = codeProvider.CreateCompiler();
CompilerParameters compilerParams = new CompilerParameters(); compilerParams.CompilerOptions = "/target:library"; compilerParams.OutputAssembly = dllName; compilerParams.GenerateExecutable = false; compilerParams.GenerateInMemory = true; compilerParams.IncludeDebugInformation = false;
compilerParams.ReferencedAssemblies.Add( "mscorlib.dll"); compilerParams.ReferencedAssemblies.Add( "System.dll"); compilerParams.ReferencedAssemblies.Add( "System.Data.dll" ); compilerParams.ReferencedAssemblies.Add( "System.Drawing.dll" ); compilerParams.ReferencedAssemblies.Add( "System.Xml.dll" ); compilerParams.ReferencedAssemblies.Add( "Neighbor.Model.dll" );
CompilerResults results = compiler.CompileAssemblyFromSource( compilerParams, sourceCode ); string errors = "" ; if (results.Errors.Count > 0) { foreach (CompilerError error in results.Errors) errors =errors + error.ErrorText ;
throw new Exception(errors); } Assembly generatedAssembly = results.CompiledAssembly; return generatedAssembly; } public object GetObject(string sourceCode,string objectName ) { //Assembly asm =this.CreateAssembly(sourceCode,"test.dll"); Assembly asm =this.CreateAssembly(sourceCode); object o = asm.CreateInstance( objectName ); return o ; }
public Assembly CreateAssembly(string strRealSourceCode) {
//Create an instance whichever code provider that is needed CodeDomProvider codeProvider = null;
codeProvider = new CSharpCodeProvider();
//create the language specific code compiler ICodeCompiler compiler = codeProvider.CreateCompiler();
//add compiler parameters CompilerParameters compilerParams = new CompilerParameters(); compilerParams.CompilerOptions = "/target:library"; // you can add /optimize compilerParams.GenerateExecutable = false; compilerParams.GenerateInMemory = true; compilerParams.IncludeDebugInformation = false;
// add some basic references compilerParams.ReferencedAssemblies.Add( "mscorlib.dll"); compilerParams.ReferencedAssemblies.Add( "System.dll"); compilerParams.ReferencedAssemblies.Add( "System.Data.dll" ); compilerParams.ReferencedAssemblies.Add( "System.Drawing.dll" ); compilerParams.ReferencedAssemblies.Add( "System.Xml.dll" ); compilerParams.ReferencedAssemblies.Add( "CC.dll" ); compilerParams.ReferencedAssemblies.Add( "System.Data1.dll" ); compilerParams.ReferencedAssemblies.Add( "PDT.Persist.dll" );
//actually compile the code CompilerResults results = compiler.CompileAssemblyFromSource( compilerParams, strRealSourceCode ); string e = "" ; //Do we have any compiler errors if (results.Errors.Count > 0) { foreach (CompilerError error in results.Errors) { e = e + error.ErrorText; }
throw new Exception(e); }
//get a hold of the actual assembly that was generated Assembly generatedAssembly = results.CompiledAssembly;
return generatedAssembly; }
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
 |
Nice work. Thanks for sharing the idea. But I think you must update the article with its uses.
I was aware of dynamic compilation, but kept scratching my head on what the use of this eval function...until I could point its synonymous usage in javascript
For people who want to know eval's real power(basically dynamic compilation) , see the link below
http://builder.com.com/5100-6371-5169823.html[^]
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Way too slow to compile on the fly! Eval.JScriptEvaluate is so much better. Too bad it has been obsoleted in .NET 2.0.
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
 |
After a little research, JScriptEval can still be used. The code below accomplishes this.
using System; using System.CodeDom.Compiler; using System.Reflection; using Microsoft.JScript;
namespace Expressions { public class JScriptEvaluator { public static int EvalToInteger(string statement) { string s = EvalToString(statement); return int.Parse(s.ToString()); }
public static double EvalToDouble(string statement) { string s = EvalToString(statement); return double.Parse(s); }
public static string EvalToString(string statement) { object o = EvalToObject(statement); return o.ToString(); }
public static object EvalToObject(string statement) { return _evaluatorType.InvokeMember( "Eval", BindingFlags.InvokeMethod, null, _evaluator, new object[] { statement } ); }
static JScriptEvaluator() { CodeDomProvider provider = new Microsoft.JScript.JScriptCodeProvider();
CompilerParameters parameters; parameters = new CompilerParameters(); parameters.GenerateInMemory = true;
CompilerResults results; results = provider.CompileAssemblyFromSource(parameters, _jscriptSource);
Assembly assembly = results.CompiledAssembly; _evaluatorType = assembly.GetType("Evaluator.Evaluator");
_evaluator = Activator.CreateInstance(_evaluatorType); }
private static object _evaluator = null; private static Type _evaluatorType = null; private static readonly string _jscriptSource =
@"package Evaluator { class Evaluator { public function Eval(expr : String) : String { return eval(expr); } } }"; } }
Mark
|
| Sign In·View Thread·PermaLink | 5.00/5 (4 votes) |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
Can you tell me where is this new version you spoke about in this thread?
Also, what is the downside of either this version or the Jscript version beside speed?
And now that you've seen both methods, which one would you prefer to use?
And if anyone is interested, I can certainly tell you how a utility like this can be used. We are in the middle of a conversion of a large App to C#, and we have built into our previous App a Query Builder which "evaluates" users, currently displayed table values, etc. and has an Expression feature that allows queries to be built on the fly with custom expressions in the where or select clauses.
My concern has been the memory issue. But we also have an import wizard that may process and "crunch" data from outside sources and often has to "Evaluate" stuff and it could process 1000s of records, so speed is another issue I am concerned with.
Thanks for your input. 
Bob Bartel MarketWare Systems
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Can you please tell me what you mean by "new version arrived with AppDomain implemented..."?
Are you referring to C# or your Eval Function? If Eval Function, can I get a copy of the new version?
Thanks.
Bob Bartel MarketWare Software bob@marketware.org Sandy, Utah USA
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Do you think that this is an appropriate function for evaluating mathematical expressions? I am creating a calculator and the use of an Eval function makes life a lot simpler however I am unsure as to if this eval method is the best way to do it.
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
 |
I'm a bit late, but what kind of expressions do you want to eavl? Are they really complicate or like 100 * 1000 / 20, because if you need variables and complex opperation, it would be a good thing to think about a little script language witch wrapps your math expression (inputed as one expression or via config (ie. hp style)) and then executes a genereted code over the eval function. If you just want to calculate some numbers you can write a text parser and do the calculation without eval.
IMPORTANT: If you use this (thi 1st) source example (2nd is coming soon - sooon!!!), make sure to load the new assembly into an new AppDomain, else it will eat up all your memory
greez kim
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thanks for all your response. Sorry for the long time it took to answere and the less documentation i'd supply with the example, but I'm working on a real interesting project at the moment and it's really time consuming, at least for me . To this example and the code provided: this is just a mix of a vb.net example and some c# snippets i've found, and should show a basic meaning of dynamically compiling code in c# (it's a conversion from vb.net). - till then use the EvalCSCode.cs class and customize to your needs :->
I agree with some of you, that there should be more introducion to the problem it solves. I think I'll update this topic soon with an new example and text. thanx again for review and keep on coding & posting - regards dave
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
It's great, but... why don't you use a static method? I think it would be faster than a new instance. 
|
| Sign In·View Thread·PermaLink | 1.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
 |
It's Excellent, but every time when the func is called, a temp assembly will be created and loaded into the application, these assemblies will stay there until current appdomain unload. The more the func is callled, the more temp assemblies will be loaded. So, if current appdomain is the default appdomain of the application then ...
|
| Sign In·View Thread·PermaLink | 2.00/5 (1 vote) |
|
|
|
 |
|
|
 |
|
 |
Used this is a project I'm working on. It's a slight modification of what you posted. I also tried to figure out the AppDomain, this is where I got when I ran into a brick wall. In order to load the code into the new AppDomain, you will need 2 classes, the first class creates the AppDomain, and loads the 2nd class into it, similar to what I have at the bottom. The second class will inherit MarshalByRefObject and will do all the dynamic compilation. When/if I get around to finishing this, I'll post it as well.
using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using Microsoft.CSharp;
namespace Test { class CSEvaluator { public List Assemblies; public List References; public string Namespace;
public CSEvaluator() { References = new List(); References.Add("System"); References.Add("System.Drawing"); References.Add("System.Windows.Forms"); References.Add("System.Xml");
Assemblies = new List(); Assemblies.Add("System"); Assemblies.Add("System.Drawing"); Assemblies.Add("System.Drawing.Drawing2D"); Assemblies.Add("System.Windows.Forms"); Assemblies.Add("System.Xml");
Namespace = this.GetType().Namespace.ToString(); }
public object Eval(string sCSCode, string sParms, object[] oParms) { CompilerParameters cp = new CompilerParameters();
foreach (string r in References) cp.ReferencedAssemblies.Add(r + ".dll");
cp.CompilerOptions = "/t:library"; cp.GenerateInMemory = true;
StringBuilder sb = new StringBuilder(""); foreach (string s in Assemblies) sb.Append("using " + s + ";\n");
sb.Append("\nnamespace " + Namespace + "\n{\n"); sb.Append("\tpublic class temp_Eval\n\t{\n"); sb.Append("\t\tpublic object Eval(" + sParms + ")\n\t\t{\n"); sb.Append("\t\t\treturn " + sCSCode + "; \n"); sb.Append("\t\t}\n"); sb.Append("\t}\n"); sb.Append("}\n");
CompilerResults cr = CodeDomProvider.CreateProvider("CSharp").CompileAssemblyFromSource(cp, sb.ToString()); if (cr.Errors.Count > 0) { MessageBox.Show("ERROR: " + cr.Errors[0].ErrorText, "Error evaluating cs code", MessageBoxButtons.OK, MessageBoxIcon.Error); return null; }
System.Reflection.Assembly a = cr.CompiledAssembly; object o = a.CreateInstance(Namespace + ".temp_Eval");
Type t = o.GetType(); System.Reflection.MethodInfo mi = t.GetMethod("Eval");
object ec = mi.Invoke(o, oParms);
return ec; } } }
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I havent downloaded it so far to see it working. But I really appreciate your work in advance.
I've been working on javascript for a quite long time and admire its "eval" function, and I was missing it in C#. You have really done great job having same in C#. I'll bookmark this page because I know I'll need it one day.
And as some1 said above; not every1 knows how powerful this function is.
--------- Tittle
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This is certainly an elegant, and beautifuly simple, solution! Thank you for sharing it. However, it would be nice for most of us out here reading it if there were a bit more discussion up front about the problem that it solves. I suspect that it will not be utilized as much as it could be (or rated as high, for that matter!) because many won't realize how powerful it is.
Anyway, thanks again for giving it to us - it surely gets my "5".
Herman Chelette "Damn the specs! Find the elegant solution!!"
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thanks for your comment, i think about a update of this topic in the near future. Due to my permanent lack of time i didn't post realy much - sorry for that.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Good article. I am curious as to whether or not you could get a list of all assemblies already in memory and reference them. That way, if a user has an eval statement against custom objects, it would still work.
|
| Sign In·View Thread·PermaLink | 3.00/5 (3 votes) |
|
|
|
 |
|
 |
try using this:
using System.Reflection; Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach( Assembly assembly in loadedAssemblies ){ try{ Console.WriteLine(assembly.FullName); }catch( TypeLoadException tlexec ){}
regards dave
|
| Sign In·View Thread·PermaLink | 2.50/5 (4 votes) |
|
|
|
 |