You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在.NET 9 NativeAOT控制台程序中嵌入文件并在代码中访问的实现方法

在.NET 9 NativeAOT控制台程序中嵌入文件并在代码中访问的实现方法

嘿,刚好我前段时间在做.NET9 NativeAOT项目时踩过这个坑,给你整理两个实用的方案,完美适配你的需求:

方法一:编译时生成静态访问器(跨平台推荐)

这个是.NET 8+ NativeAOT官方支持的特性,能让编译器自动生成静态类来直接访问嵌入的资源,完全替代原来的Assembly.GetManifestResourceStream,而且跨平台可用。

步骤1:修改.csproj配置

把你原来的Content节点改成EmbeddedResource,并且开启静态访问器生成:

<ItemGroup>
  <EmbeddedResource Include="icon.ico">
    <GenerateEmbeddedResourceAccessor>true</GenerateEmbeddedResourceAccessor>
    <!-- 可选:指定逻辑名,让生成的类名更规范,比如这里设置后访问类会对应`你的项目命名空间.Resources.icon_ico` -->
    <LogicalName>MyApp.Resources.icon.ico</LogicalName>
  </EmbeddedResource>
</ItemGroup>

同时可以删掉原来的<CopyToOutputDirectory>,因为我们要把文件嵌入exe,不是复制到输出目录。

步骤2:代码中直接访问资源

编译器会根据你的项目命名空间,自动生成一个静态资源访问类。比如你的项目根命名空间是MyNativeAOTConsole,那么直接通过以下代码就能拿到资源的字节数组或流:

// 获取资源的字节数组
var iconBytes = MyNativeAOTConsole.Resources.icon_ico.Bytes;

// 或者打开资源流(用完记得释放,最好用using包裹)
using var iconStream = MyNativeAOTConsole.Resources.icon_ico.OpenStream();

// 举个测试例子:把提取的图标保存到本地
File.WriteAllBytes("extracted_from_aot.ico", iconBytes);

提示:如果不知道生成的类名,可以看项目的obj目录下的generated文件,或者直接在代码里输入你的命名空间+.Resources,IDE会自动补全资源对应的属性名。

方法二:读取Windows PE资源(仅Windows平台)

因为你已经在.csproj里设置了<ApplicationIcon>icon.ico</ApplicationIcon>,这个图标其实已经作为Windows PE资源嵌入到exe里了。如果你只需要在Windows平台运行,可以用Windows API直接读取这个资源,不需要额外修改csproj。

代码实现

直接在项目里加这段P/Invoke代码,通过Windows API提取PE资源中的图标:

using System.Runtime.InteropServices;

namespace YourProjectNamespace
{
    internal static class ResourceHelper
    {
        // Windows API声明
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string? lpModuleName);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr FindResource(IntPtr hModule, IntPtr lpName, IntPtr lpType);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr LockResource(IntPtr hResData);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo);

        // 图标资源的类型标识
        private static readonly IntPtr RT_ICON = (IntPtr)3;
        // 应用程序图标的默认ID
        private static readonly IntPtr IDI_APPLICATION = (IntPtr)32512;

        public static byte[] GetEmbeddedAppIcon()
        {
            // 获取当前进程的模块句柄(即当前exe)
            var hModule = GetModuleHandle(null);
            if (hModule == IntPtr.Zero)
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());

            // 查找图标资源
            var hResInfo = FindResource(hModule, IDI_APPLICATION, RT_ICON);
            if (hResInfo == IntPtr.Zero)
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());

            // 加载并锁定资源
            var hResData = LoadResource(hModule, hResInfo);
            var pResData = LockResource(hResData);
            var resourceSize = SizeofResource(hModule, hResInfo);

            // 把非托管内存的资源拷贝到托管字节数组
            var iconBytes = new byte[resourceSize];
            unsafe
            {
                Marshal.Copy((nint)pResData, iconBytes, 0, (int)resourceSize);
            }

            return iconBytes;
        }
    }
}

然后在Main方法里调用:

var extractedIcon = ResourceHelper.GetEmbeddedAppIcon();
File.WriteAllBytes("pe_extracted_icon.ico", extractedIcon);

注意事项

  1. 方法一是跨平台的,如果你以后要编译到Linux/macOS,优先用这个方法;方法二仅支持Windows,依赖Windows API。
  2. 如果你嵌入的不是ico,而是txt、json这类通用文件,方法一完全通用,只要修改EmbeddedResource的Include路径即可。
  3. 如果你之前设置了<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>,记得删掉,因为我们要的是把文件嵌入exe内部,不是复制到输出目录(NativeAOT单文件发布后,复制的文件会被打包到单文件里,但访问方式是读文件系统,不是嵌入资源)。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 11:43:08