1

My current project is an application that allows you to write code in C# and then execute it using CSharpCodeProvider. It works fine at this point as long as the code is a complete application in itself, for example:

using System;

namespace RuntimeCode {
    public static class Program {
        public static void Main() {
            Console.WriteLine("Hello, world!");
        }
    }
}

The application then compiles the given code using CompileAssemblyFromSource with GenerateInMemory = true, and then invoking the Main method within the compiled assembly.

My question is, how can I let the compiled code access objects in the main application?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Booers
  • 351
  • 4
  • 8
  • Is this for use in a production application, or for fun/learning? If it's the latter, you should check out Roslyn: http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx Note that it does not have a proper release license meaning you can't distribute your application to others. – JoshVarty Dec 12 '12 at 05:19
  • 1
    Search for "C# scripting" - there are plenty of tutorials how to use C# for scripting and correspondingly to pass data to new scripts. – Alexei Levenkov Dec 12 '12 at 05:25
  • 1
    @Alexei: Seems that OP asks not how to pass data from script but access host application from script. – abatishchev Dec 12 '12 at 05:38
  • How and where do you load the resulting assembly? See my comment to my answer below too. – abatishchev Dec 12 '12 at 05:49

3 Answers3

1

Note that runtime generated code is being executed as a separate process. Thus you need to perform a cross-process communication: between your main, host application and newly created.

A good way to implement cross-process or cross-domain commutation is to host a special service inside your main application or default app domain. And a resulting assembly will be a client.

I'd recommend to use WCF and NetNamedPipeBinding. You need just to configure contracts and endpoints.


Also note that the only thing GenerateInMemory=true controls is to load whether or not resulting assembly from disk to memory. That's it.


If you load your script into default AppDomain, that's a piece of cake even more. Just create a static member:

namespace MyNamespace
{
    public class Core
    {
        public static Core Instance { get; set; }
    }
}

and access it by full type name:

MyNamespace.Core.Instance;
abatishchev
  • 98,240
  • 88
  • 296
  • 433
  • I'm not sure if Manning starts separate process for the compiled code (which not even saved to disk): "then invoking the Main method within the compiled assembly" sound to me like reflection call... – Alexei Levenkov Dec 12 '12 at 05:43
  • @Alexei: CompileAssemblyFromSource leads me to [CodeDomProvider.CompileAssemblyFromSource](http://msdn.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider.compileassemblyfromsource.aspx) which produces an assembly. Here's I see several options: load it into current AppDomain (silly), new AppDomain (tricky) and just start as an executable (easy). But still I think you're correct and OP peforms something from #1 or #2. But still again - WCF/pipe looks for me the easiest way to achieve what OP does want. – abatishchev Dec 12 '12 at 05:48
  • if `CompilerResults results = codeProvider.CompileAssemblyFromFile(parameters, "test.cs");` succeeds than `results.CompiledAssembly` contains reference to assembly already loaded in the current domain (for `GenerateInMemory=true` case). – Alexei Levenkov Dec 12 '12 at 05:56
  • @Alexei: Thanks, you gave me a valuable thoughts and directions. – abatishchev Dec 12 '12 at 06:07
0

The application then compiles the given code […], and then invoking the Main method within the compiled assembly.

I think that almost answers your question. There is no reason why the code you're compiling has to have a parameterless Main() method. For example, if the methods you need to access are in the IHost interface, then you could require your scripts to have a Main method with a parameter of type IHost. You would then invoke that, passing in the current instance of IHost.

svick
  • 236,525
  • 50
  • 385
  • 514
0

Your injected code can't access classes and objects defined in the main application.(to access classes defined in main application you can use reflection in the injected code again but the objects are inaccessible.)

But you can simply send data to injected code as a parameter:

using System;

namespace RuntimeCode {
    public static class Program {
        public static void StartPoint(object obj) {
            Console.WriteLine("Hello, world!");
        }
    }
}

Then invoke StartPoint method by sending obj object that contains the data it needs.

you can use any type of object you need. But it should be recognized by injected code.

Mostafa Vatanpour
  • 1,328
  • 13
  • 18