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

如何将原生DLL嵌入各架构的C#单文件EXE?(Rider环境)

问题描述

我的项目依赖多个带原生库的组件,包括OpenTK(glfw3.dll)、ImGui.NET(cimgui.dll)和AssimpNet(assimp.dll)。这些原生库会因架构不同而有差异,我想知道能不能把对应架构的这些DLL嵌入到EXE文件里。我用Rider开发。

当前项目已经配置成发布单文件,生成的EXE包含了所有托管依赖,但没包含原生DLL。项目属性配置如下:

<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>net7.0</TargetFramework>
  <ImplicitUsings>enable</ImplicitUsings>
  <Nullable>enable</Nullable>
  <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  <RuntimeIdentifiers>win-x64;win-x86;win-arm64</RuntimeIdentifiers>
  <PublishSingleFile>true</PublishSingleFile>
  <SelfContained>false</SelfContained>
</PropertyGroup>

我用这个命令构建项目:dotnet publish -c Release -r win-x64


解决方案

要把对应架构的原生DLL嵌入单文件EXE,可按以下步骤操作:

1. 修改项目文件,配置原生DLL为嵌入资源

在项目的.csproj文件中添加以下配置,针对每个原生库设置嵌入规则,同时根据运行时架构筛选对应DLL:

<ItemGroup>
  <!-- 嵌入OpenTK的glfw3.dll -->
  <EmbeddedResource Include="runtimes\win-x64\native\glfw3.dll" Condition="'$(RuntimeIdentifier)' == 'win-x64'">
    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    <LogicalName>native.glfw3.dll</LogicalName>
  </EmbeddedResource>
  <EmbeddedResource Include="runtimes\win-x86\native\glfw3.dll" Condition="'$(RuntimeIdentifier)' == 'win-x86'">
    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    <LogicalName>native.glfw3.dll</LogicalName>
  </EmbeddedResource>
  <EmbeddedResource Include="runtimes\win-arm64\native\glfw3.dll" Condition="'$(RuntimeIdentifier)' == 'win-arm64'">
    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    <LogicalName>native.glfw3.dll</LogicalName>
  </EmbeddedResource>

  <!-- 嵌入ImGui.NET的cimgui.dll -->
  <EmbeddedResource Include="runtimes\win-x64\native\cimgui.dll" Condition="'$(RuntimeIdentifier)' == 'win-x64'">
    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    <LogicalName>native.cimgui.dll</LogicalName>
  </EmbeddedResource>
  <EmbeddedResource Include="runtimes\win-x86\native\cimgui.dll" Condition="'$(RuntimeIdentifier)' == 'win-x86'">
    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    <LogicalName>native.cimgui.dll</LogicalName>
  </EmbeddedResource>
  <EmbeddedResource Include="runtimes\win-arm64\native\cimgui.dll" Condition="'$(RuntimeIdentifier)' == 'win-arm64'">
    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    <LogicalName>native.cimgui.dll</LogicalName>
  </EmbeddedResource>

  <!-- 嵌入AssimpNet的assimp.dll -->
  <EmbeddedResource Include="runtimes\win-x64\native\assimp.dll" Condition="'$(RuntimeIdentifier)' == 'win-x64'">
    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    <LogicalName>native.assimp.dll</LogicalName>
  </EmbeddedResource>
  <EmbeddedResource Include="runtimes\win-x86\native\assimp.dll" Condition="'$(RuntimeIdentifier)' == 'win-x86'">
    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    <LogicalName>native.assimp.dll</LogicalName>
  </EmbeddedResource>
  <EmbeddedResource Include="runtimes\win-arm64\native\assimp.dll" Condition="'$(RuntimeIdentifier)' == 'win-arm64'">
    <CopyToOutputDirectory>Never</CopyToOutputDirectory>
    <LogicalName>native.assimp.dll</LogicalName>
  </EmbeddedResource>
</ItemGroup>

注意:runtimes\win-xxx\native\是NuGet包默认的原生库存放路径,如果你的项目中路径不同,需要对应调整。

2. 添加启动代码,提取嵌入的原生DLL

在程序入口(比如Program.cs)添加以下代码,将嵌入的DLL提取到临时目录,让CLR能找到并加载:

using System;
using System.IO;
using System.Reflection;

namespace YourNamespace
{
    class Program
    {
        static void Main(string[] args)
        {
            // 提取原生DLL到临时目录
            ExtractNativeDll("native.glfw3.dll", "glfw3.dll");
            ExtractNativeDll("native.cimgui.dll", "cimgui.dll");
            ExtractNativeDll("native.assimp.dll", "assimp.dll");

            // 后续程序逻辑
            // ...
        }

        private static void ExtractNativeDll(string embeddedName, string outputName)
        {
            var assembly = Assembly.GetExecutingAssembly();
            using var stream = assembly.GetManifestResourceStream(embeddedName);
            if (stream == null)
                throw new FileNotFoundException($"未找到嵌入的资源:{embeddedName}");

            var tempDir = Path.Combine(Path.GetTempPath(), "YourAppNativeDlls", Environment.ProcessId.ToString());
            Directory.CreateDirectory(tempDir);

            var outputPath = Path.Combine(tempDir, outputName);
            if (!File.Exists(outputPath))
            {
                using var fileStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write);
                stream.CopyTo(fileStream);
            }

            // 将临时目录添加到系统PATH,确保CLR能找到DLL
            var currentPath = Environment.GetEnvironmentVariable("PATH");
            Environment.SetEnvironmentVariable("PATH", $"{tempDir};{currentPath}", EnvironmentVariableTarget.Process);
        }
    }
}

替换YourNamespace为你的项目命名空间,YourAppNativeDlls可以改成你自己的标识名。

3. 重新发布项目

保持之前的发布命令执行即可:

dotnet publish -c Release -r win-x64

发布后的单文件EXE会包含对应架构的原生DLL,程序启动时会自动提取到临时目录并加载。

额外说明

  • 因为设置了<SelfContained>false</SelfContained>,发布后的EXE需要依赖系统已安装的.NET 7运行时。如果要完全独立,可以把SelfContained改成true,但文件体积会变大。
  • 临时目录以进程ID命名,避免多个实例冲突,程序退出后可选择手动清理临时文件(系统也会自动清理临时目录)。

内容的提问来源于stack exchange,提问作者PlanetTeamSpeak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 05:53:20