如何在 Windows 机器上运行 Mono.Cecil 生成的程序集?

问题描述

我最近一直在玩 Mono.Cecil,主要是为了我计划编写的编译器。我首先尝试了 this answer 中的代码代码如下:

var myHelloWorldApp = AssemblyDeFinition.CreateAssembly(
    new AssemblyNameDeFinition("HelloWorld",new Version(1,0)),"HelloWorld",ModuleKind.Console);

var module = myHelloWorldApp.MainModule;

// create the program type and add it to the module
var programType = new TypeDeFinition("HelloWorld","Program",Mono.Cecil.TypeAttributes.Class | Mono.Cecil.TypeAttributes.Public,module.TypeSystem.Object);

module.Types.Add(programType);

// add an empty constructor
var ctor = new MethodDeFinition(".ctor",Mono.Cecil.MethodAttributes.Public | Mono.Cecil.MethodAttributes.HideBySig
    | Mono.Cecil.MethodAttributes.SpecialName | Mono.Cecil.MethodAttributes.RTSpecialName,module.TypeSystem.Void);

// create the constructor's method body
var il = ctor.Body.GetILProcessor();

il.Append(il.Create(OpCodes.Ldarg_0));

// call the base constructor
il.Append(il.Create(OpCodes.Call,module.ImportReference(typeof(object).GetConstructor(Array.Empty<Type>()))));

il.Append(il.Create(OpCodes.nop));
il.Append(il.Create(OpCodes.Ret));

programType.Methods.Add(ctor);

// define the 'Main' method and add it to 'Program'
var mainMethod = new MethodDeFinition("Main",Mono.Cecil.MethodAttributes.Public | Mono.Cecil.MethodAttributes.Static,module.TypeSystem.Void);

programType.Methods.Add(mainMethod);

// add the 'args' parameter
var argsParameter = new ParameterDeFinition("args",Mono.Cecil.Parameterattributes.None,module.ImportReference(typeof(string[])));

mainMethod.Parameters.Add(argsParameter);

// create the method body
il = mainMethod.Body.GetILProcessor();

il.Append(il.Create(OpCodes.nop));
il.Append(il.Create(OpCodes.Ldstr,"Hello World"));

var writeLineMethod = il.Create(OpCodes.Call,module.ImportReference(typeof(Console).getmethod("WriteLine",new[] { typeof(string) })));

// call the method
il.Append(writeLineMethod);

il.Append(il.Create(OpCodes.nop));
il.Append(il.Create(OpCodes.Ret));

// set the entry point and save the module
myHelloWorldApp.EntryPoint = mainMethod;
myHelloWorldApp.Write("HelloWorld.exe");

请注意,我将 module.Import 更改为 module.ImportReference,因为前者显然已过时。

我将其放入一个 .NET 5 项目中,并创建了一个 HelloWorld.exe。由于我在 macOS 上,我尝试使用单声道运行 exe:

mono HelloWorld.exe

它打印了“Hello World”。到目前为止一切顺利。

当我将这个 HelloWorld.exe 发送给使用 Windows 计算机的朋友时,问题就出现了。当他这样运行时(注意他在 Windows 上没有单声道):

.\HelloWorld.exe

输出错误

未处理的异常:System.IO.FileNotFoundException:无法加载文件或程序集“System.Console,版本=5.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a”或其依赖项之一。系统找不到指定的文件

HelloWorld.Program.Main(String[] args)

我尝试查找错误消息,但所有结果都无法找到System.RuntimeSystem.Console 甚至是一个程序集吗?不是上课吗?

如何在 Windows 机器上运行 exe? 代码中有什么我需要更改的地方吗?还是Windows机器需要安装什么东西?我认为这可能与我使用 .NET 5 有关,但 Windows 机器只有 .NET Framework。


问题到此结束,以下是我的发现:

作为“对照组”,我尝试做最简单的Hello World C#程序:

class Program {
    public static void Main() {
        System.Console.WriteLine("Hello World");
    }
}

在 macOS(即 Mono 编译器)上使用 csc 编译它,并在 Windows 上运行输出 exe。这是有效的,所以我反汇编(使用 dotnet-ildasm)Mono.Cecil 生成的 exe 和 csc 生成的 exe,并比较它们。我发现的最有趣的区别是“损坏的”exe 中有这些额外的程序集引用:

.assembly extern System.Private.CoreLib
{
  .publickeytoken = ( 7C EC 85 D7 BE A7 79 8E ) // ....y.
  .ver 5:0:0:0
}


.assembly extern System.Console
{
  .publickeytoken = ( B0 3F 5F 7F 11 D5 0A 3A ) // .._.....
  .ver 5:0:0:0
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)