如何运行Mono生成的程序集.塞西尔在Windows电脑上?



我一直在玩Mono。最近,主要是为了一个编译器,我打算写。我首先尝试了这个答案中的代码。下面是代码:

var myHelloWorldApp = AssemblyDefinition.CreateAssembly(
new AssemblyNameDefinition("HelloWorld", new Version(1, 0, 0, 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上,我试着用mono:

运行exe。
mono HelloWorld.exe

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

问题出现了,当我发送这个HelloWorld.exe给我的朋友谁是在Windows机器上。当他像这样运行它时(注意他在Windows上没有mono):

.HelloWorld.exe

输出错误:

未处理的异常:System. io . filenotfoundexception: Could not load file or assembly 'System。Console, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'或其依赖项之一。系统找不到指定的文件

atHelloWorld.Program.Main(String[] args)

我试图查找错误信息,但所有的结果都是关于无法找到System.RuntimeSystem.Console是一个集合吗?这不是一堂课吗?

如何在Windows机器上运行exe ?代码中是否有需要修改的地方?还是Windows机器需要安装一些东西?我认为这可能与我使用。net 5有关,但Windows机器只有。net框架。


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

作为"对照组",我试着做最简单的Hello World c#程序:

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

在macOS上用csc编译(即Mono编译器),并在Windows上运行输出exe。这是有效的,所以我反汇编(使用dotnet-ildasm)两个由Mono产生的exe。Cecil和csc生成的exe,并将它们进行比较。我发现的最有趣的区别是,在"broken"中有这些额外的程序集引用。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
}

如果您使用typeof(...),则您正在从当前运行时(我猜你想为。NET Framework编译,因为Windows上安装了。NET Framework。你需要在Windows或mono上从。net Framework中导入mscorlib(不知道它在Mac上的位置):

// Load mscorlib from .NET framework or mono
// C:WindowsMicrosoft.NETFramework64v4.0.30319mscorlib.dll (Windows, 64-bit)
// C:WindowsMicrosoft.NETFrameworkv4.0.30319mscorlib.dll (Windows, 32-bit)
// /usr/lib/mono/4.5/mscorlib.dll (Linux)
// /usr/lib/mono/4.8-api/mscorlib.dll (Linux, reference-only)
var mscorlib = ModuleDefinition.ReadModule("/usr/lib/mono/4.8-api/mscorlib.dll");

调用基类构造函数时,调用加载的mscorlib中对象的构造函数,而不是.NET 5中的构造函数:

// module.TypeSystem.Object.Resolve() will resolve to .NET 5
var objectCtor = mscorlib.GetType("System.Object").Methods.Where(m => m.IsConstructor && !m.HasParameters).First();
// call the base constructor
il.Append(il.Create(OpCodes.Call, module.ImportReference(objectCtor)));

同样,不要从当前运行时导入字符串:

// add the 'args' parameter
var argsParameter = new ParameterDefinition("args",
Mono.Cecil.ParameterAttributes.None, new ArrayType(module.TypeSystem.String));

如果你查找System。控制台文档,你看到它在mscorlib中,所以你可以得到类型引用:

// Obtain System.Console from .NET Framework
var netfxConsole = mscorlib.GetType("System.Console");
// Obtain the correct WriteLine method, there might be a better way
var writeLine = netfxConsole
.Methods
.Where(m => m.Name == "WriteLine" && m.Parameters.Count == 1 && m.Parameters[0].ParameterType == mscorlib.TypeSystem.String)
.First();
// Use .NET Framework's Console.WriteLine
var writeLineMethod = il.Create(OpCodes.Call, module.ImportReference(writeLine));

这适用于mono (Linux)和Windows (VM)

然而,反汇编程序(monodis, ilspycmd)显示有2个mscorlib引用(一个来自module.TypeSystem,一个来自加载的mscorlib,看起来是一样的):

.assembly extern mscorlib
{
.ver 4:0:0:0
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .zV.4..
}
.assembly extern mscorlib
{
.ver 4:0:0:0
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .zV.4..
}
.assembly 'HelloWorld'
{
.hash algorithm 0x00000000
.ver  1:0:0:0
}
.module HelloWorld // GUID = {CB393199-8BA7-4CB5-8B55-1F81ED0FA68C}

最新更新