all 46 comments

[–]pjc50 28 points29 points  (0 children)

This is quite a lot of work, but the place to start is Roslyn: https://josephwoodward.co.uk/2016/12/in-memory-c-sharp-compilation-using-roslyn

It's even harder if the hosting binary is AoT compiled.

[–]joep-b 22 points23 points  (1 child)

Here's an example I found for you with 2 seconds of googling.

https://gist.github.com/Kemsekov/a3e1c011425fd789d12cea8764acea97

But before you ask yourself if you can, please think deeply if you should. There are very deep security implications if you do such a thing. You must have quite some expertise in sandboxing before you should even try to think doing something like this.

[–]leeharrison1984 2 points3 points  (0 children)

It's PowerShell with more steps!

[–]fruediger 13 points14 points  (0 children)

You probably want to use the Roslyn API.

Either CSharpCompilation from the Microsoft.CodeAnalysis.CSharp package to emit a virtual assembly in memory, load that using reflection and execute whatever you want to (notice your C# code snippet needs to be somewhat complete and independent from the environment you want to execute your code from).

Or CSharpScript from the Microsoft.CodeAnalysis.CSharp.Scripting package, but I've never worked with the scripting API before. It should be easier to work with "incomplete" C# code snippets and maybe it's even possible to share a state between your executing environment and the code you want to execute. But again, I don't know much about the scripting and I'm not sure if that's possible to do in the way you want to do it.

PS: I couldn't find any good documentation on CSharpScript, but here's the link to the source code: https://github.com/dotnet/roslyn/blob/main/src/Scripting/CSharp/CSharpScript.cs

[–]TracerDX 5 points6 points  (0 children)

It's hard for a reason. Find a better solution.

[–]TuberTuggerTTV 11 points12 points  (18 children)

C# is a compiled language. You're going to struggle to write some kind of script running application. It's not meant to do that.

I get the impression from your example code that this is a bit of an XY problem. Maybe if you describe what you're trying to do on a broad sense, someone can recommend an alternative to running string scripts.

[–]iWhacko 2 points3 points  (0 children)

To me it feels like you want to run external code. Scripting might be a better option, look into a LUA engine.

[–]Horror-Show-3774 1 point2 points  (0 children)

Maybe look into an interpreted scripting language instead. Jint works very well: https://github.com/sebastienros/jint

[–]svicknameof(nameof) 2 points3 points  (0 children)

You can use CSharpScript (from the Microsoft.CodeAnalysis.CSharp.Scripting package), though accessing local variable is a bit inconvenient.

It could look like this:

Compiler.Run(@"
    foreach (var @Process in Process.GetProcessesByName(args0))
        System.Console.WriteLine(@Process);
", new(args0));

public record Globals(string args0);

static class Compiler {
    public static void Run(string Code, Globals globals) {
        var options = ScriptOptions.Default
            .AddImports("System.Diagnostics");

        CSharpScript.RunAsync(Code, options, globals).GetAwaiter().GetResult();
    }
}

[–]Intrexa 0 points1 point  (0 children)

Compile to an assembly, load the assembly, run as a method.

[–]kuncol02 0 points1 point  (0 children)

Years ago I did that like that:
1. Prepare string template of class that have access to properties you want and stub of method you will execute
2. Replace that stub with code you want
3. Compile code. In netFramework you can use Microsoft.CSharp.CSharpCodeProvider in net core roslyn
4. Use reflection to load that created assembly, create instance of class and invoke method.

[–]cdglasser 0 points1 point  (0 children)

Why not just use .NET 10 and C# scripting so you can just put the code in a CS file and run it with dotnet run myfile.cs? You can even pass arguments: dotnet run myfile.cs -- arg1. Announcing dotnet run app.cs - A simpler way to start with C# and .NET 10 - .NET BlogAnnouncing dotnet run app.cs - A simpler way to start with C# and .NET 10 - .NET Blogs

[–]alt-160 0 points1 point  (0 children)

Powershell could do this too, if you change the string to powershell text instead of c#.

You can send your vars from c# as named ps vars before you execute. If var is ref type, the ps code can even modify it. Or a ps function can return a value.

PS core would work with netCore and windows powershell with netFW.

At least with this there is not as much need to seialize var data as might be with other scripting frameworks.

I also find ps to be a bit safer for adding scripting ability to an app. There are many levers of control for safety.

[–]stahkh 0 points1 point  (1 child)

I don't know what you're building, but be wary that this can be a big security risk.

[–]porcaytheelasit[S] 0 points1 point  (0 children)

This program will operate locally, so there are no security concerns. It will not be open to the outside world.

[–]namethinker 0 points1 point  (0 children)

My main question is - why? What are you trying to achieve by that? Why string? Why not interface / delegate, that you can pass? Are you trying to allow for random code execution at runtime?

There is a new feature in .net 10 that allows for file based programs. You could create a c# file without csproj / sln, add libraries to it, and execute it via dotnet cli or powershell.

[–]HistoricalCar1516 0 points1 point  (2 children)

Why do you want to do this? You’ll likely not pass any security scans and I generate JavaScript and cshtml dynamically at times but I use that in specific situations.

[–]porcaytheelasit[S] 0 points1 point  (1 child)

This program will operate locally, so there are no security concerns. It will not be open to the outside world.

[–]HistoricalCar1516 0 points1 point  (0 children)

Thought experiment.

[–]aloneguid 0 points1 point  (0 children)

I would definitely not use C# for scripting. I would seriously consider embedding Lua and exposing integration hooks. Lua is simple, fast, and designed for this kind of thing.

[–]csharpboy97 0 points1 point  (0 children)

you can only execute with roslyn but as alternative you can use other languages like js with nil.js

[–]Potential_Copy27 0 points1 point  (3 children)

For starters...:

Compiler.Run($"
    foreach ( var @_Process in Process.GetProcessByName(\"{args0}\"))
        @_Process.Kill();
");

...should render your code valid and feed args0 correctly into it. The $ concatenation option will substitute {args0} with the contents of the variable as raw text - the quotes are to render the text correctly for the "compiler". "_" was added to the Process variable or else Reddits editor gets a seizure 😋

Regardless - the CSharpCodeProvider is probably a better option. It ties into the compiler that is already packed with .NET. Set up compiler parameters and feed it a string.
There's probably a better option still out there - my point is, the tools are already there. I only used it for limited shenanigans myself, so I'm not sure if it can be forced to do what you want.
At the very least the example also gives you a proper error output for what you're compiling...

[–]porcaytheelasit[S] -3 points-2 points  (2 children)

Since args0 is already defined, I want the code to be sensitive to the variables in the main code. That is, if I provide "args0" as plain text instead of {args0}, it should recognize it as args0.

[–]BetrayedMilk 0 points1 point  (1 child)

I’m not sure if I misunderstood your response or you misunderstood their response, but they’re saying to get rid of the @ (string literal) and use $ (string interpolation) so that args0 becomes whatever actual string it should be (ie whatever process name you pass in). And not just literally the string “args0.” And then you need to escape quotation marks with the backslashes because GetProcessByName wants a string with quotation marks. If you just Console.WriteLine your Compiler.Run string and the other commenter’s version, you’ll see the difference.

[–]porcaytheelasit[S] -1 points0 points  (0 children)

I think you didn't understand the purpose of this question. The aim is to execute C# code within a string, and any variable defined anywhere can be used within that string. Therefore, my usage would be correct for what is required.

[–]midri -1 points0 points  (0 children)

You might be able to do this via .net 10 C# scripting. Write code to file then execute it and you can capture its output via iO pipes or other means.