0

I'm generating and executing C# from within a C# program using as described here.

As you can see, one can call "Execute" method of the compiled code via classType.GetMethod("Execute").Invoke(instance, args).

The variable args is an object array of parameters to be passed to the Execute method. I've been able to pass things relatively easy and without issue using this system.

Now here's the big issue... I need to pass a callback function (eg a Delegate) so that the Execute method will be able to signal stuff to the main program. I've been trying to pass a typed Delegate that matches the signature, but it failed (wouldn't even run). Next I tried passing a Delegate without specifying the type which also failed (this time it failed at when the call was attempted). I also tried passing it as a normal object and cast it back inside the generated code, but it didn't do anything different.

Since I'm not this much into C#, I think I'm missing something very basic. Also, I don't have a valid test case code...but it shouldn't be too difficult to work with the link I provided above..

See also: http://support.microsoft.com/kb/304655

Edit: This is some sample code:

class CompilerTest {

    private object obj;
    private MethodInfo mtd;

    public void on_log(string type, string message){
        MessageBox.Show(type.ToUpper() + ": " + message);
    }

    public void compile(){
        CodeDomProvider c = CodeDomProvider.CreateProvider("CSharp");
        CompilerParameters cp = new CompilerParameters();

        cp.ReferencedAssemblies.Add("system.dll");
        cp.ReferencedAssemblies.Add("system.data.dll");
        cp.ReferencedAssemblies.Add("system.windows.forms.dll");

        cp.CompilerOptions = "/t:library";
        cp.GenerateInMemory = true;

        StringBuilder sb = new StringBuilder("");
        sb.AppendLine("using System;");
        sb.AppendLine("using System.Data;");
        sb.AppendLine("using System.Reflection;");
        sb.AppendLine("using System.Windows.Forms;");

        sb.AppendLine("public delegate void LoggerInternal(string type, string message);");

        sb.AppendLine("public class CSCodeEvalCls{");

        sb.AppendLine("  private LoggerInternal log;");

        sb.AppendLine("  public void E(object lgr){");
        sb.AppendLine("    this.log = (RuleLoggerInternal)lgr;");
        sb.AppendLine("  }");

        sb.AppendLine("  private void L(string type, string message){");
        sb.AppendLine("    this.log(type, message);");
        sb.AppendLine("  }");

        sb.AppendLine("}");

        CompilerResults cr = c.CompileAssemblyFromSource(cp, sb.ToString());
        System.Reflection.Assembly a = cr.CompiledAssembly;

        this.obj = a.CreateInstance("CSCodeEvalCls");
        this.mtd = this.obj.GetType().GetMethod("E");
    }

    public void execute(){
        this.mtd.Invoke(this.obj, new object[]{ this.on_log });
    }

}

CompilerTest test = new CompilerTest();
test.compile();
test.execute();
Community
  • 1
  • 1
Christian
  • 27,509
  • 17
  • 111
  • 155

1 Answers1

1

You will have to pass in a delegate of a different type, and then convert it from inside of the newly-compiled code.

For example, outside of the code you would invoke it like this:

this.mtd.Invoke(this.obj, new object[]{ new Action<string, string>(this.on_log) });

Now you have a few options about how you handle this from your compiled code.

First, you can forgo defining your own LoggerInternal delegate type and just use Action<string, string>.

Second, inside of the compiled code you can convert the delegate through this type:

public void E(object lgr) {
    this.log = new LoggerInternal((Action<string, string>)lgr);
}

Third, if you don't want the compiled code to know what kind of delegate it was passed, you can use Delegate.CreateDelegate():

public void E(object lgr) {
    Delegate d = (Delegate)lgr;

    this.log = (LoggerInternal)Delegate.CreateDelegate(
        typeof(LoggerInternal),
        d.Target,
        d.Method);
}
cdhowie
  • 158,093
  • 24
  • 286
  • 300