Domanda

I'm compiling code on-the-fly using System.CodeDom.Compiler. Everything inside the compiled source works well, whatever I'm putting inside this source. I know how to call my functions:

o = results.CompiledAssembly.CreateInstance("Foo.Bar");
MethodInfo mi = o.GetType().GetMethod("SayHello");
mi.Invoke(o, null);

But let's say I'm using a WebClient to retrieve a string asynchronously using WebClient.DownloadStringAsync. Or any other context where I want my compiled source to tell to the host "Hey, I got a nice string ready for you." For the example, I've used a WebBrowser. Basically, I know how to deal with each of the two instances: My hosting program and the compiled program, but I want my compiled program to communicate with the host. By the way, I'm not a super-experimented programmer, so no obvious method comes to my mind.

What I've tried:

1 . I don't really need to try it because it would work, but I could use a timer reading a strings stack or tasks queue inside the compiled source, but the purpose of my application is to have +- 60 scripts able to execute ponctual tasks, not continuous background processes, so it wouldn't be efficient on the CPU.

2 . I've passed the handler to the compiled source like if it was in the hosting app:

    //In the hosting app
    MethodInfo mi2 = o.GetType().GetMethod("attachCallbackToHost");
    mi2.Invoke(o2, new object[] { new WebBrowserNavigatedEventHandler (wb_navigated) });

    //... And the handler
    public static void wb_navigated(object sender, WebBrowserNavigatedEventArgs e)
    {            
        string browserHtmlFromCompiledSource = ((WebBrowser)sender).DocumentText;
        MessageBox.Show(browserHtmlFromCompiledSource);
    }

    // Plain text from the compiled source code
    public void attachCallbackToHost(WebBrowserNavigatedEventHandler handlerFromTheHost)
    {
         wb.Navigated += handlerFromTheHost;
    }

And it did nothing.

3 . Maybe I could share a class or variable by passing it to the compiled assembly?

So, the question is either this or the other:

  • How to watch efficiently for change inside a specific variable or property inside the compiled program?

  • How to attach a callback to the host?

È stato utile?

Soluzione

Ok. I got it: In order to access the host from the compiled source, the only thing required is to add the host assembly to the refered assemblies in the compiler parameters:

compilerParams.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);

So no need for any special callback or INotifier.

Here's the full code that strictly answers my question and nothing more:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;

namespace MamaProgram
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            string source =
           @"
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using MyMama = MamaProgram;              
namespace Baby
{
    public class Program
    {
        public WebBrowser wb = new WebBrowser();        

        public void navigateTo(string url)
        {
            wb.Navigated += wb_navigated;            
            wb.Navigate(url);            
        }

        public void wb_navigated(object sender, WebBrowserNavigatedEventArgs e)
        {            
            MyMama.Form1.getResult(wb.DocumentText);            
        }
    }
}
            ";

            Dictionary<string, string> providerOptions = new Dictionary<string, string>
                {
                    {"CompilerVersion", "v3.5"}
                };

            CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);

            CompilerParameters compilerParams = new CompilerParameters
            {
                GenerateInMemory = true,
                GenerateExecutable = false,
                TreatWarningsAsErrors = false                
            };

            compilerParams.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location);
            compilerParams.ReferencedAssemblies.Add("System.Data.dll");            
            compilerParams.ReferencedAssemblies.Add(typeof(System.Linq.Enumerable).Assembly.Location); // Trick to add assembly without knowing their name            
            compilerParams.ReferencedAssemblies.Add(typeof(System.ComponentModel.Component).Assembly.Location); // Trick to add assembly without knowing their name                        
            compilerParams.ReferencedAssemblies.Add("System.Windows.Forms.dll");
            CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);

            if (results.Errors.Count != 0)
                throw new Exception("Compilation failed");

            object o = results.CompiledAssembly.CreateInstance("Baby.Program");
            MethodInfo mi2 = o.GetType().GetMethod("navigateTo");
            mi2.Invoke(o, new object[] { "http://www.google.com" });                        
        }

        public static void getResult(string result)
        {
            MessageBox.Show(result);
        }        
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top