单文件编译时如何将e_sqlite3.dll嵌入主exe文件?
我尝试编译基于Microsoft.Data.Sqlite的单文件二进制应用,但始终生成两个运行必需的文件:1. {ProjectName}.exe;2. e_sqlite3.dll。请问是否可以将e_sqlite3.dll嵌入到exe中?
注:System.Data.Sqlite存在相同情况,对应的依赖文件为SQLite.Interop.dll。
示例代码
注:以下代码仅用于演示编译过程,未涉及SQLite实际交互。
ProjectName.fsproj
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net7.0</TargetFramework> <PublishSingleFile>true</PublishSingleFile> <SelfContained>true</SelfContained> <RuntimeIdentifier>win-x64</RuntimeIdentifier> <PublishReadyToRun>true</PublishReadyToRun> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.Data.Sqlite" version="7.*" /> </ItemGroup> <ItemGroup> <Compile Include="Program.fs" /> </ItemGroup> </Project>
Program.fs
module ProjectName.Program open System [<EntryPoint>] let main (argv : string[]) = printfn "Hello world" 0
编译命令:
dotnet publish .\ProjectName.fsproj -c Release
解决方案
可以将原生依赖库嵌入单文件EXE中,以下提供两种可行方法:
方法一:手动嵌入并加载原生库(通用方案)
该方法适用于Microsoft.Data.Sqlite(依赖e_sqlite3.dll)和System.Data.Sqlite(依赖SQLite.Interop.dll):
修改项目文件,添加嵌入资源配置
打开ProjectName.fsproj,根据使用的库添加对应配置(替换版本号为你实际使用的包版本):针对Microsoft.Data.Sqlite:
<ItemGroup> <EmbeddedResource Include="$(NuGetPackageRoot)microsoft.data.sqlite.core\7.0.14\runtimes\win-x64\native\e_sqlite3.dll"> <LogicalName>e_sqlite3.dll</LogicalName> </EmbeddedResource> </ItemGroup>针对System.Data.Sqlite:
<ItemGroup> <EmbeddedResource Include="$(NuGetPackageRoot)system.data.sqlite.core\1.0.118\runtimes\win-x64\native\SQLite.Interop.dll"> <LogicalName>SQLite.Interop.dll</LogicalName> </EmbeddedResource> </ItemGroup>添加启动时代码,提取并加载库
修改Program.fs,在入口函数前添加提取和加载原生库的逻辑:module ProjectName.Program open System open System.IO open System.Reflection open System.Runtime.InteropServices let extractAndLoadNativeLibrary resourceName = // 创建唯一临时目录避免冲突 let tempDir = Path.Combine(Path.GetTempPath(), $"App_{Guid.NewGuid()}") Directory.CreateDirectory(tempDir) |> ignore let tempPath = Path.Combine(tempDir, resourceName) // 从嵌入资源中读取库文件并写入临时目录 use resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName) if resourceStream <> null then use fileStream = new FileStream(tempPath, FileMode.Create) resourceStream.CopyTo(fileStream) // 加载原生库 NativeLibrary.Load(tempPath) // 根据使用的库调用对应函数 extractAndLoadNativeLibrary "e_sqlite3.dll" // 若使用System.Data.Sqlite则替换为: // extractAndLoadNativeLibrary "SQLite.Interop.dll" [<EntryPoint>] let main (argv : string[]) = printfn "Hello world" 0重新发布
执行原编译命令即可生成包含嵌入库的单文件EXE:dotnet publish .\ProjectName.fsproj -c Release
方法二:使用预打包嵌入库(简化方案)
针对Microsoft.Data.Sqlite,可以直接使用SQLitePCLRaw.bundle_e_sqlite3包,它已将e_sqlite3.dll嵌入到托管程序集中,无需手动处理:
修改项目文件,添加包引用
打开ProjectName.fsproj,在PackageReference组中添加:<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" version="2.*" />重新发布
执行原编译命令,生成的单文件EXE将包含所有依赖,不会额外输出e_sqlite3.dll。
内容的提问来源于stack exchange,提问作者pim

