如何将原生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
相关产品推荐
相关产品推荐

