Skip to content

CS Script Runtime Environment (script reflection)

Oleg Shilo edited this page Aug 7, 2025 · 19 revisions

Objective

The script engine is a small runtime environment, and as such, it has some characteristics, which may be accessed/analysed at runtime from the script code itself (reflection). The most important aspects are the name/location of the script file itself and the compiled script assembly. While it may seem to be a trivial task in some cases, it can be challenging. For example, Assembly.GetExecutingAssembly().Location will always be null if the script is executed as in-memory assembly (setting in css_config.xml).

The information about the location of the script can be vital for scenarios that require deterministic knowledge about the location of the modules being executed. For example, a script running a web server may need to service static web content located in the same directory where the script is. Thus, the webserver needs to map the physical location to the virtual path.

Another example is a complicated custom assembly probing that needs to be done from script. In such case it's important to know the exact location of the script assembly even if it's not loaded as a memory copy.

CS-Script stores the information about script-related locations in environment variables, assembly metadata and global CLR object CSSEnvironment. Over the years of intensive use, it became apparent that the low-dependency approaches (envvars and metadata) are more practical, easier to use and in fact more accurate. Though if you are interested in learning about CSSEnvironment class, you can find more details on this legacy documentation page. But this article will only describe the most common scenarios involving the use of environment variables and metadata.

Solution

Location of script file

The simplest way of finding the file name of the script being executed is by querying the environment variable EntryScript:

string scriptFile = Environment.GetEnvironmentVariable("EntryScript");

However, the most accurate approach is to analyse the script assembly metadata. Specifically AssemblyDescriptionAttribute attribute, which the script engine embeds into every compiled script.

static public string GetScriptName(this Assembly assembly)
{
    return assembly.GetCustomAttributes(typeof(AssemblyDescriptionAttribute), false)
                   .Cast<AssemblyDescriptionAttribute>()
                   .FirstOrDefault()?.Description;
}
...
string scriptFile = Assembly.GetExecutingAssembly().GetScriptName();

The second approach is more accurate as you can use it when you host the script engine in your application and potentially multiple scripts can be executed during the session.

Note: CS-Script always injects AssemblyDescriptionAttibute with the path of the script provided that all conditions below are met:

  1. Settins.InjectScriptAssemblyAttribute is set to true. This is the default configuration.
  2. The engine is configured to use the embedded CodeProvider. This is the default configuration.
  3. If a custom CodeProvider is enabled (Settings.UseAlternativeCompiler != "") and the script syntax is C# (file has '.cs' file extension).

Location of the script assembly file

The simplest way of finding the file name of the script being executed is by querying the environment variable EntryScriptAssembly:

string asmFile = Environment.GetEnvironmentVariable("EntryScriptAssembly");

However, similarly to the script name, the most accurate approach is to analyse the data, which is specific to the assembly being executed. This can also be done via the environment variable. The name of the variable is composed of the fixed common prefix and the hash code that identifies the assembly in the current process:

string asmFile = Environment.GetEnvironmentVariable("location:" + Assembly.GetExecutingAssembly().GetHashCode());

Information about the script engine

The script engine always sets some environment variables at startup:

  • CSScriptRuntime - version of the script engine.
  • CSScriptRuntimeLocation - location of the script engine.
  • css_nuget - location of the NuGet packages, scripts can load/reference (applicable only for Windows).
  • cscs_exe_dir - script engine directory.

For the most accurate list of environment variables, execute cscs -help.

Choosing hosting CLR

When running C# scripts, you can control, to a degree, which CLR version it is going to run.

CLI execution (in shell/terminal)

CS-Script release contains at least builds targeting the two latest .NET versions. Thus, if you are using the portable version (zip file), you need to download the package targeting your desired runtime.

image

Thus, when you have multiple SDK installed, CS-Script launcher will even be hosted on the most compatible version of CLR. Below is the test on the environment with both .NET8 and .NET9 installed and .NET9 being the active version:

image

This is the capture of the script execution on .NET8. Note that cscs.exe belonged to the cs-script.win.net8.v*.*.*.*.7z package:

test.cs

using System;
using static dbg;

typeof(System.Threading.Tasks.Task).Assembly.Location.print();
image

If you are installing CS-Script with .NET Tool manager, then both versions will be deployed and configured automatically by the package manager (NuGet). IE If your latest installed SDK is .NET8 then .NET8 version of CS-Script will be installed.

If you have multiple SDK installed, the .NET Tool manager installs all builds from the package. But is mapse the launcher css.exe to the version of the script engine that corresponds to the .NET version configured to be the default on your PC.

image

Hosted execution (in .NET application)

When hosting your script engine in .NET application, the choice of the script C# compiler is fully determined by the host application runtime. If you are running the .NET8 application, then your scripts will be compiled and executed by .NET8.

using CSScriptLib;

namespace ConsoleApp3
{
    internal class Program
    {
        static void Main(string[] args)
        {
            CSScript.EvaluatorConfig.DebugBuild = true;

            System.Console.WriteLine("Host: " + typeof(System.Threading.Tasks.Task).Assembly.Location);

            dynamic script = CSScript
                .Evaluator
                .LoadMethod(@"public void print()
                              {
                                  System.Console.WriteLine(""Script: "" + typeof(System.Threading.Tasks.Task).Assembly.Location);
                              }");

            script.print(); 
        }
    }
}
Image

And if I change it to .NET9:

Image

Hope it helps:

ConsoleApp3.zip

Clone this wiki locally