C# 带有CSharpCodeProvider生成的DLL的TargetInvocationException异常

C# 带有CSharpCodeProvider生成的DLL的TargetInvocationException异常,c#,scripting,C#,Scripting,我正在尝试创建一个脚本系统,它使用C的CSharpCodeProvider在运行时构建C代码。这是我正在开发的一个简单的游戏引擎,用XNA4.0编写。 目标是让用户能够通过C#修改游戏元素,而无需访问游戏引擎的具体细节(渲染代码、物理代码、网络等)。脚本在运行时由引擎编译成DLL。目前,我已经建立了引擎和编译脚本DLL之间的通信。(我创建了一个Player.cs脚本,编译后它可以从脚本DLL调用引擎的“engine.Print”(“Foobar”);方法)引擎也可以使用脚本的方法(编译后引擎搜索

我正在尝试创建一个脚本系统,它使用C的CSharpCodeProvider在运行时构建C代码。这是我正在开发的一个简单的游戏引擎,用XNA4.0编写。 目标是让用户能够通过C#修改游戏元素,而无需访问游戏引擎的具体细节(渲染代码、物理代码、网络等)。脚本在运行时由引擎编译成DLL。目前,我已经建立了引擎和编译脚本DLL之间的通信。(我创建了一个Player.cs脚本,编译后它可以从脚本DLL调用引擎的“engine.Print”(“Foobar”);方法)引擎也可以使用脚本的方法(编译后引擎搜索脚本中定义的所有新类,并在编译后调用它们的“OnCompile()”方法

问题从脚本间通信开始:我有两个脚本,库存和播放器:

Inventory.cs:

public class Inventory  
{  
    int foobar;  

    public Inventory()
    {
        foobar = 42;
    }

    public static void OnCompile()
    {
        // This method exists in the Engine DLL, linked to this script
        Engine.Print("OnCompile Inventory");     
    }
}
Player.cs:

using Scripts.Inventory;

public class Player
{
    Inventory inventory;  

    public Player()  
    {
        //inventory = new Inventory();  
        Engine.Print("Player created");  
    }


    public static void OnCompile()  
    {
        Engine.Print("OnCompile Player");  
        Player test = new Player();
    }
}
此代码起作用,调试输出打印:

OnCompile目录
奥运选手
玩家创建

但是,一旦我取消对播放器构造函数中的inventory=newinventory()的注释 调试输出如下所示:

OnCompile目录
奥运选手

未处理的异常:System.Reflection.TargetingException:调用的目标已引发异常。-->System.IO.FileNotFoundException:无法加载文件或程序集'Inventory.cs,Version=0.0.0,Culture=neutral,PublicKeyToken=null'或其依赖项之一。系统找不到指定的文件。 在Scripts.Player.Player..ctor()处 在Scripts.Player.Player.OnCompile()中

我已确保我的Player.cs.dll引用了Inventory.cs.dll。我的编译代码如下:

    public static bool Compile(string fileName, bool forceRecompile = false)
    {
        // Check to see if this assembly already exists.
        // If it does, then just return a reference to it, unless
        // it is told to forceRecompile, in which case
        // it will delete the old, and continue compiling
        if (File.Exists("./" + fileName + ".dll"))
        {
            if (forceRecompile)
            {
                File.Delete(fileName + ".dll");
            }
            else
            {
                return true;
            }
        }


        // Generate a name space name. this means removing the initial ./
        // of the path, and replacing all subsequent /'s with .'s
        // Also removing the .cs at the end

        // i.e: ./Scripts/Player.cs becomes
        //      Scripts.Player

        string namespaceName = "";

        if (fileName.LastIndexOf('.') != -1)
        {
            fileName = fileName.Remove(fileName.LastIndexOf('.'));
        }

        namespaceName = fileName.Replace('/', '.');
        namespaceName = namespaceName.Substring(2);

        // Add references, starting with ScriptBase.dll.
        // ScriptBase.dll is a helper library that provides
        // access to debug functions such as Console.Write

        List<string> references = new List<string>() 
        { 
            "./ScriptBase.dll",
            "System.dll"        // TODO: remove later
        };


        // Open the script file wit ha StreamReader
        StreamReader fileStream;
        string scriptSource = "";

        fileStream = File.OpenText("./" + fileName + ".cs");


        // Preprocess the script. This is important, as it resolves
        // using statements, so that if a script references another
        // script, it will have the dependency registered before
        // compiling.
        do
        {
            string line = fileStream.ReadLine();

            string[] words = line.Split(' ');

            // Found a using statement:
            if (words[0] == "using")
            {
                // Get the namepsace name:
                string library = words[1];

                library = library.Remove(library.Length - 1); // get rid of semicolon

                // Convert back to a path
                library = library.Replace('.', '/');


                // See if the assembly exists, or we are forcing the recompilation
                if (!File.Exists("./" + library + ".cs.dll") || forceRecompile)
                {
                    // We need to compile it now.
                    // See if the script file exists...
                    if (File.Exists("./" + library + ".cs"))
                    {
                        // if it does, compile that, if it doesn't then we bail
                        if (!Compile("./" + library + ".cs", forceRecompile))
                        {
                            return false;
                        }
                    }
                    else
                    {
                        return false;
                    }
                }
                // Now that it's compiled, and we need it link it with our reference list...
                references.Add("./" + library + ".cs.dll");
            }
            // Piece it back together as one string, line by line.
            scriptSource = scriptSource + line + "\n";

        } while (!fileStream.EndOfStream);


        fileStream.Close();

        // Automagically add our namepsace to the script, so the scriptor doesn't have to, also automatically
        // include ScriptBase
        // This is where Engine class is found for Print() debug method        

        string source = "using ScriptBase; namespace " + namespaceName + "{" + scriptSource + "}";


        // Set up the compiler:
        Dictionary<string, string> providerOptions = new Dictionary<string, string> 
        { 
            { "CompilerVersion", "v3.5" } 
        };

        CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);


        // Create compilation params... Here we link our references, and append ".cs.dll" to our file name
        // So now for example, ./Scripts/Player.cs compiles to ./Script/Player.cs.dll
        CompilerParameters compilerParams = new CompilerParameters(references.ToArray(), fileName + ".cs.dll")
        {
            GenerateInMemory = true,
            GenerateExecutable = false, // compile as DLL

        };

        // Compile and check errors
        CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, source);
        if (results.Errors.Count != 0)
        {
            foreach (CompilerError error in results.Errors)
            {
                // Write out any errors found:
                Console.WriteLine("Syntax Error in " + error.FileName + " (" + error.Line + "): " + error.ErrorText);
            }

            return false;
        }

        // Return our Script struct, which keeps all the information together,
        // and registers it so that Script.GetCompiledScript("./Scripts/Player.cs.dll");
        // returns the compiled script, or null if it's never been compiled

        Assembly.LoadFrom(fileName + ".cs.dll");

        foreach (Type type in results.CompiledAssembly.GetTypes())
        {
            new ScriptClass(fileName + ".cs.dll", type);
        }

        return true;
    }
}
公共静态bool编译(字符串文件名,bool forceRecompile=false) { //检查此程序集是否已存在。 //如果是,则只返回对它的引用,除非 //它被告知强制重新编译,在这种情况下 //它将删除旧的,并继续编译 如果(File.Exists(“./”+文件名+“.dll”)) { 如果(强制重新编译) { 删除(文件名+“.dll”); } 其他的 { 返回true; } } //生成名称空间名称。这意味着删除首字母/ //,并将所有后续的/'s替换为''s' //同时删除结尾处的.cs //即:./Scripts/Player.cs成为 //脚本,播放器 字符串namespaceName=“”; 如果(fileName.LastIndexOf('.')!=-1) { fileName=fileName.Remove(fileName.LastIndexOf('.'); } namespaceName=fileName.Replace(“/”,“.”); namespaceName=namespaceName.Substring(2); //添加引用,从ScriptBase.dll开始。 //ScriptBase.dll是一个帮助程序库,提供 //对调试函数(如Console.Write)的访问 列表引用=新列表() { “/ScriptBase.dll”, “System.dll”//TODO:稍后删除 }; //使用ha StreamReader打开脚本文件 StreamReader文件流; 字符串scriptSource=“”; fileStream=File.OpenText(“./”+fileName+“.cs”); //预处理脚本。这很重要,因为它解决了 //使用语句,以便如果脚本引用另一个 //脚本,它将在之前注册依赖项 //编译。 做 { string line=fileStream.ReadLine(); string[]words=line.Split(“”); //找到一个using语句: 如果(字[0]=“使用”) { //获取名称共享名称: 字符串库=单词[1]; library=library.Remove(library.Length-1);//去掉分号 //转换回路径 library=library.Replace('.','/'); //看看程序集是否存在,或者我们正在强制重新编译 如果(!File.Exists(“./”+library+“.cs.dll”)| |强制重新编译) { //我们现在需要编译它。 //查看脚本文件是否存在。。。 如果(文件.Exists(“./”+库+“.cs”)) { //如果有,编译它,如果没有,我们就退出 如果(!Compile(“./”+库+“.cs”,强制重新编译)) { 返回false; } } 其他的 { 返回false; } } //现在它已经编译好了,我们需要它和我们的参考列表链接起来。。。 添加(“./”+库+“.cs.dll”); } //一行一行地把它拼成一根绳子。 scriptSource=scriptSource+line+“\n”; }而(!fileStream.EndOfStream); fileStream.Close(); //自动地将我们的名字添加到脚本中,这样脚本编写者就不必自动添加名字 //包括脚本库 //这是为Print()调试方法找到引擎类的地方 string source=“使用ScriptBase;命名空间”+namespaceName+“{”+scriptSource+“}”; //设置编译器: Dictionary providerOptions=新字典 { {“compilervision”,“v3.5”} }; CSharpCodeProvider provider=新的CSharpCodeProvider(providerOptions); //创建编译参数…这里我们链接引用,并将“.cs.dll”附加到文件名 //例如,现在,./Script