使用Mono.Cecil生成.NET 8 HelloWorld IL程序时加载System.Private.CoreLib失败
问题:生成的.NET 8 IL可执行程序运行时抛出
FileNotFoundException 环境:Windows 11系统,已安装.NET 8 SDK与运行时,使用Rider开发,通过Mono.Cecil v0.11.5生成IL程序(代码基于旧.NET Framework示例修改)。
生成的EXE文件运行时抛出异常:
Unhandled Exception: System.IO.FileNotFoundException: Could not load file or assembly 'System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e' or one of its dependencies. The system cannot find the file specified.
原生成代码如下:
// Slightly modified from: // https://mycodingplace.wordpress.com/2016/11/10/mono-cecil-hello-world/ public static void Emit() { // Create a new assembly var assembly = AssemblyDefinition.CreateAssembly( new AssemblyNameDefinition("HelloWorld", new Version()), "HelloWorld", ModuleKind.Console); // This is the main module you need to work with var module = assembly.MainModule; // Create a new type called "Program" and add it to main module var programType = new TypeDefinition( "HelloWorld", "Program", TypeAttributes.Class | TypeAttributes.Public, module.Import(typeof(object))); module.Types.Add(programType); // create a new method called 'Main' method and add it to 'Program' type var mainMethod = new MethodDefinition( "Main", MethodAttributes.Public | MethodAttributes.Static, module.Import(typeof(void))); programType.Methods.Add(mainMethod); // Get ILProcessor for the method body var ilProcessor = mainMethod.Body.GetILProcessor(); // Load the string "Hello World" to stack ilProcessor.Emit(OpCodes.Ldstr, "Hello World"); // Call Console.WrtieLine(string) var writline = module.ImportReference( typeof(Console).GetMethod("WriteLine", new[] { typeof(string) })); ilProcessor.Emit(OpCodes.Call, writline); // Call Console.ReadKey() var readKey = module.ImportReference(typeof(Console).GetMethod("Read")); ilProcessor.Emit(OpCodes.Call, readKey); // You must pop out the return value before you leave the method ilProcessor.Emit(OpCodes.Pop); // Return ilProcessor.Emit(OpCodes.Ret); // Because this is an executable assembly, you must define an entry point assembly.EntryPoint = mainMethod; // Save the assembly to disk assembly.Write(@"c:\temp\HelloWorld.exe"); }
适配.NET 8运行时的修改方案
原代码默认生成.NET Framework兼容的程序集格式,要适配.NET 8需做以下核心调整:
1. 指定运行时版本与目标框架属性
给模块添加.NET 8的运行时版本标识,并为程序集添加TargetFrameworkAttribute明确目标框架:
// 设置模块运行时版本为.NET 8 module.RuntimeVersion = "v8.0"; // 添加TargetFrameworkAttribute,指定目标框架为net8.0 var targetFrameworkAttrType = module.ImportReference(typeof(System.Runtime.Versioning.TargetFrameworkAttribute)); var targetFrameworkCtor = targetFrameworkAttrType.Resolve().GetConstructors().First(); var targetFrameworkAttr = new CustomAttribute(module.ImportReference(targetFrameworkCtor)); targetFrameworkAttr.ConstructorArguments.Add(new CustomAttributeArgument(module.TypeSystem.String, "net8.0")); assembly.CustomAttributes.Add(targetFrameworkAttr);
2. 明确入口点标记(可选但规范)
给Main方法添加IsEntryPoint标记,与程序集入口点设置形成双重确认:
mainMethod.IsEntryPoint = true;
3. 使用.NET Core兼容的写入选项
写入程序集时指定符合.NET Core规范的参数,避免生成旧格式PE文件:
assembly.Write(@"c:\temp\HelloWorld.exe", new WriterParameters { WriteSymbols = false // 按需决定是否写入符号文件 });
修改后的完整代码
public static void Emit() { // 创建新程序集 var assembly = AssemblyDefinition.CreateAssembly( new AssemblyNameDefinition("HelloWorld", new Version(1, 0, 0, 0)), "HelloWorld", ModuleKind.Console); var module = assembly.MainModule; // 设置运行时版本为.NET 8 module.RuntimeVersion = "v8.0"; // 添加TargetFrameworkAttribute,指定net8.0 var targetFrameworkAttrType = module.ImportReference(typeof(System.Runtime.Versioning.TargetFrameworkAttribute)); var targetFrameworkCtor = targetFrameworkAttrType.Resolve().GetConstructors().First(); var targetFrameworkAttr = new CustomAttribute(module.ImportReference(targetFrameworkCtor)); targetFrameworkAttr.ConstructorArguments.Add(new CustomAttributeArgument(module.TypeSystem.String, "net8.0")); assembly.CustomAttributes.Add(targetFrameworkAttr); // 创建Program类 var programType = new TypeDefinition( "HelloWorld", "Program", TypeAttributes.Class | TypeAttributes.Public, module.Import(typeof(object))); module.Types.Add(programType); // 创建Main方法并标记为入口点 var mainMethod = new MethodDefinition( "Main", MethodAttributes.Public | MethodAttributes.Static, module.Import(typeof(void))); mainMethod.IsEntryPoint = true; programType.Methods.Add(mainMethod); var ilProcessor = mainMethod.Body.GetILProcessor(); // 加载字符串并调用Console.WriteLine ilProcessor.Emit(OpCodes.Ldstr, "Hello World"); var writeLineMethod = typeof(Console).GetMethod("WriteLine", new[] { typeof(string) }); var writeline = module.ImportReference(writeLineMethod); ilProcessor.Emit(OpCodes.Call, writeline); // 调用Console.Read()并弹出返回值 var readMethod = typeof(Console).GetMethod("Read"); var read = module.ImportReference(readMethod); ilProcessor.Emit(OpCodes.Call, read); ilProcessor.Emit(OpCodes.Pop); // 返回 ilProcessor.Emit(OpCodes.Ret); // 设置程序集入口点 assembly.EntryPoint = mainMethod; // 写入程序集 assembly.Write(@"c:\temp\HelloWorld.exe", new WriterParameters { WriteSymbols = false }); }
验证步骤
- 生成EXE后,通过
dotnet --info确认.NET 8运行时已正确安装 - 直接双击EXE或在命令行执行
dotnet c:\temp\HelloWorld.exe测试运行
内容的提问来源于stack exchange,提问作者SAJ14SAJ
相关产品推荐
相关产品推荐

