Reputation: 71
I am trying to add C# scripting to my application and decided to use AppDomain(s) so that I can successfully load and unload scripts without causing memory issues. My current code has a ScriptManager that loads scripts within a folder using a watcher to check for changes:
private static void LoadScript(string scriptPath)
{
string scriptName = Path.GetFileNameWithoutExtension(scriptPath);
AppDomain domain = AppDomain.CreateDomain(scriptName);
domains.Add(domain);
ScriptCompiler scriptCompiler = (ScriptCompiler)domain.CreateInstanceFromAndUnwrap(Assembly.GetExecutingAssembly().Location, "Milkshake.Tools.ScriptCompiler");
scriptCompiler.Compile(scriptPath);
}
It creates a new appdomain instance of my scriptcompiler and then attempts to compile the script code and finally instantiates the script:
public void Compile(string filepath)
{
string code = File.ReadAllText(filepath);
CSharpCodeProvider codeProvider = new CSharpCodeProvider(new Dictionary<String, String> { { "CompilerVersion", "v3.5" } });
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.GenerateExecutable = false;
//Add references used in scripts
parameters.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location); //Current application ('using milkshake;')
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("System.Core.dll");
parameters.ReferencedAssemblies.Add("System.Data.dll");
parameters.ReferencedAssemblies.Add("System.Data.DataSetExtensions.dll");
parameters.ReferencedAssemblies.Add("System.Xml.dll");
parameters.ReferencedAssemblies.Add("System.Xml.Linq.dll");
CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, code);
if (results.Errors.HasErrors)
{
string error = "Error in script: " + Path.GetFileNameWithoutExtension(filepath);
foreach (CompilerError e in results.Errors)
{
error += "\n" + e;
}
Log.Print(LogType.Error, error);
}
else
{
//Successful Compile
Log.Print(LogType.Debug, "Script Loaded: " + Path.GetFileNameWithoutExtension(filepath));
assembly = results.CompiledAssembly;
type = assembly.GetTypes()[0];
//Instansiate script class.
Activator.CreateInstance(type);
}
}
Now the code actually works fine, it loads the script and unloads it correctly, however when I try call a method in my main application from the script, it has the wrong data. For example, this script will print out "Test", and then print out how many players are on the server and finally send the message "Test" to all of the players.
class TestScript
{
public TestScript()
{
Log.Print(LogType.Debug, "Test!");
//WorldServer.Sessions is missing data.
Log.Print(LogType.Debug, WorldServer.Sessions.Count);
WorldServer.Sessions.ForEach(s => s.sendMessage("Test!!"));
}
}
Now the interesting thing here is that my WorldServer.Sessions.Count (WorldServer is static) always comes out as 0 when I call it from the script (when I call it from my main application it comes out as 1). Everytime I try and call a method or get data from my main application, it has problems. I am guessing this is to do with cross-appdomain stuff, so I really don't know what the best approach is.
Upvotes: 2
Views: 291
Reputation: 726569
The reason why your script prints zero is that in .NET app domains provide a very high level of isolation: when the same class is loaded in two different app domains, the two copies behave completely independently of each other. Among other things, each copy gets its own set of static variables, each copy runs its static initialization code, and so on.
As the result, communicating between two app domains cannot be done implicitly through static variables or memory sharing constructs* . It needs to be done explicitly as if you were communicating with code in a different process.
If the amount of data that you must exchange with the script is small, you can use [SetData'][1] and [
GetData`]2 methods to avoid setting a more complex system of callbacks.
* Although it appears that app domains share the virtual address space, I was not able to find a proof of it in any of the standards.
Upvotes: 1