【问题标题】:How to write to an at runtime generated .dll file in C#?如何在 C# 中写入运行时生成的 .dll 文件?
【发布时间】:2020-02-20 19:49:39
【问题描述】:

我必须在运行时更改 .dll 文件的内容,但不能这样做,因为它正在使用中并得到一个

无效操作异常

改为。

我目前正在研究一种在运行时为 Unity 制作的游戏编译 C# 代码的方法。使用Microsoft.CSharp.CSharpCodeProviderSystem.CodeDom.Compiler.CompilerParamters 类,我有一个系统工作,允许我编译代码并将其输出为.dll 文件,因此我可以将它与其他类一起使用。如果您需要了解更多关于我这样做的方式,请查看我使用的the tutorial(以及下面提到的更改)。

但是,编译只工作一次,因为下次运行编译器时,.dll 文件已经存在,我收到以下错误消息:

无法写入文件“fileName”。 Win32 IO 返回 1224。路径:path/fileName.dll

这些是我的代码中最重要的部分:

public void Compile() {
  CSharpCodeProvider provider = new CSharpCodeProvider();
  CompilerParameters parameters = new CompilerParameters();
  //...
  parameters.GenerateInMemory = false; //generates actual file
  parameters.GenerateExecutable = false; //generated .dll instead of .exe
  //...
  parameters.OutputAssembly = Application.dataPath + className + ".dll";
  CompilerResults results = provider.CompileAssemblyFromSource(parameters, code);
  //...
  Assembly assembly = results.CompiledAssembly;
  Type program = assembly.GetType("GameLevel." + className);
  MethodInfo excecuteMethod = program.GetMethod("Excecute");

  excecuteMethod.Invoke(null, null);
}

我真的不想每次都给文件一个不同的名字,因为那样会让在其他类中使用它很痛苦。我假设这可以通过以某种方式告诉游戏不再使用旧的 .dll 文件来解决,因为在方法执行后甚至不应该是这种情况,对吧?

感谢您的回答!

【问题讨论】:

  • File.Delete(Application.dataPath + className + ".dll");跑之前?
  • "因为在方法被执行之后甚至不应该是这种情况,对吧?" - 方法在类型中(DynamicMethod 除外);类型在程序集中;程序集位于应用程序域中,无法卸载;释放 dll 的唯一方法是杀死应用程序域或进程,在 .NET Core 中:应用程序域 不存在 (所以:你必须杀死进程)。根据您的复杂程度,我想知道您是否可以使用可收集的 DynamicMethod / Expression 等待
  • 只需给新的 dll 起一个不同的名称。并确保当你实例化类时,它来自新的 dll
  • 顺便说一句,我实际上这样做并且工作正常。我的 dll 名称和类名也是动态的。
  • @bizzehdee 给我错误:System.UnauthorizedAccessException:对路径“path/fileName.dll”的访问被拒绝。

标签: c# code-generation


【解决方案1】:

正如我在 cmets 中所说的那样,我经常在做这件事时有所改变。 我有一个编译公式的微积分引擎。每次公式更改时,都会重新编译。 每次我需要运行一个公式时,我都会实例化它的类。 但.... 每次重新编译时,我都会创建一个具有不同名称的新 dll。 所以我对名称使用时间戳,对类名使用时间戳。 每次实例化时,我都会寻找最新的 dll

所以我在 dll 中的类看起来像:

public class MyGeneratedClass_20191024103000 {
// do stuff
}

程序集创建(伪代码):

aseemblyManager.CreateLibrary(OUTPUT_DLLS_PATH + "\\Calculus_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".dll", refs, sourcecode) ... etc

组装加载:

string pathNewest = ListFolderSortByDate(); //you should also get the timestamp
assembly = Assembly.LoadFrom(pathNewest ); //register dll
mytype =  assembly.GetType("mycalculus"); 

最后,实例化:

 myobject= Activator.CreateInstance(mytype , new object[] { some parameters });
mytype .GetMethod("Calculate" + timestamp).Invoke(myobject, arrParam);

【讨论】:

  • 那么这行得通吗?如果没有,请将信息编辑到您的问题中,不要将其作为答案发布。
  • 老实说,我不认为我会用日期来做,而是保持简单,只使用数字。但是你让我走上了正轨,所以谢谢你!可惜它不像我上面描述的那样工作。
  • @LasseVågsætherKarlsen “如果不是”是什么意思。我说过“我经常这样做”。我的答案不是 100% 真实代码,而是真实代码的简化/编辑版本,不仅可以工作,而且每天都能养活我的家人……所以我认为这是一个真实的答案……
  • @Schred 这就是我的想法。您可以只使用生成迭代的全局计数器。就我而言,我将代码存储在数据库中并提供追溯功能(用户仍然可以执行旧版本的公式)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-29
  • 2020-01-08
  • 1970-01-01
  • 2022-01-14
  • 2011-02-01
  • 1970-01-01
相关资源
最近更新 更多