简介面向.NET Framework 4.0开发者的SQLite连接引用包内含32位与64位两套程序集分别支持Windows下的x86和x64环境可直接按项目目标架构选择引用。压缩包整体约4.26MB共包含50个文件其中主要的文件类型有8个动态链接库文件提供核心数据库访问功能、8个可执行示例程序演示基本操作、8个配置文件用于运行环境设置、8个API说明文档包含类与方法的注释、16个调试符号文件便于异常定位以及2个示例数据库含Northwind等样例数据。包内按Win32和x64分为两个完整子目录每套均配齐对应的程序集、示例工程与说明文档可直接解压使用。示例覆盖了SQLite连接、命令执行、数据读取、数据适配、事务处理和异步操作等常见ADO.NET场景并额外提供Entity Framework与LINQ的集成支持。示例工程清晰展示了SQLiteConnection、SQLiteCommand、SQLiteDataReader等类的典型写法方便开发者理解从建立连接到查询更新的完整调用链。已有443人浏览学习适合需要在.NET Framework 4.0环境下便捷接入SQLite、构建桌面或移动端离线存储应用的开发者可直接复用包内示例以减少踩坑。1. sqlite-netFx40-2010.rar这个老压缩包背后是 32/64 位适配的整座大山拿到.net连接sqlite引用库包含32位和64位程序 sqlite-netFx40-2010.rar这个标题的人多半正在维护某个老掉牙的 .NET Framework 4.0 项目想在离线环境里塞进 SQLite却发现自己解压出来的东西比想象中复杂里面有 x86 和 x64 两个文件夹还有一堆长得差不多的 dll。如果直接全部引用轻则编译通过跑起来报错重则 VS 设计器直接崩溃。这是一个典型的「引用库好办运行时难配」的坑——因为 SQLite 不是纯托管代码它带着一个原生库SQLite.Interop.dll进程用 32 位还是 64 位必须和原生库严格对应否则哪怕引用没错加载时照样翻车。这篇笔记会从解压开始把引用、配置、连接、错误排查和动态加载全讲透适合正在维护遗留系统、又不想上 NuGet 的从业者。2. 解开 sqlite-netFx40-2010.rar里面的文件为什么长这样2.1 System.Data.SQLite 的三大件托管 dll、Interop 和设计时组件打开这个 rar通常会看到一个典型的bin目录结构里面分成x86和x64两个子目录每个子目录下都有SQLite.Interop.dll而更上层的bin根目录或者lib目录下放着System.Data.SQLite.dll、System.Data.SQLite.Linq.dll还有一个叫SQLite.Designer的组件。这就是 System.Data.SQLite 的完整分发形态。System.Data.SQLite 本质上是「混合模式程序集」托管代码部分负责实现 ADO.NET 接口比如SqliteConnection、SqliteCommand这部分就是System.Data.SQLite.dll而真正执行 SQL 逻辑的是原生 C 语言写的 SQLite 引擎被打包成SQLite.Interop.dll。这个原生 dll 是分平台的x86 进程必须加载 x86 版本x64 进程必须加载 x64 版本没有通吃一说。SQLite.Designer则是供 Visual Studio 设计器使用的组件它让DataSet可视化设计器能识别 SQLite 数据源通常会在安装sqlite-netFx40-setup时注册到 VS 里。但注意这个设计时组件和你的项目运行时没有直接关系很多开发者误以为必须引用它其实工程项目里只要System.Data.SQLite.dll和正确的SQLite.Interop.dll就够了。设计时组件装坏了还可能污染 VS后面避坑章会说。2.2 为什么 .NET 4.0 的项目特别容易在 32/64 位上翻车.NET Framework 4.0 的AnyCPU默认行为很微妙。在 4.0 之前AnyCPU程序集在 32 位系统上以 32 位进程运行在 64 位系统上以 64 位进程运行但带原生依赖的托管 dll 没法享受这种自动伸缩。System.Data.SQLite.dll本身是 C# 写的无论哪个位数都能加载可它一调用SQLite.Interop.dll就必须和当前进程位数一致。更麻烦的是4.0 版本开始Visual Studio 的默认编译平台为AnyCPU且没有「Prefer 32-bit」复选框这个复选框是 VS 2012 和 .NET 4.5 才加的。于是同一个项目在开发者机器上可能跑得好好的发布到 64 位服务器上就报BadImageFormatException或者DllNotFoundException。这是老项目最常见的坑几乎每个用过 System.Data.SQLite 的都踩过。解决思路有两种一是干脆把目标平台固定成 x86 或 x64二是保持 AnyCPU 但通过机制确保加载对应原生库。下面章节会分别给出可落地做法。3. 把引用库放进项目手动引用与复制到输出目录的正确步骤3.1 解压后如何把 System.Data.SQLite.dll 和 SQLite.Interop.dll 放进 bin我一般不会把整个 rar 解压后一股脑塞进项目根目录那样 bin 目录会乱成一团。推荐的做法是只保留必要的二进制文件并建立一个清晰的目录结构。假设项目根目录叫MyApp我会这样做# 在项目根目录建立 Native 文件夹用于存放原生库 mkdir MyApp\Native\x86 mkdir MyApp\Native\x64 # 从解压包里复制托管 dll copy sqlite-netFx40-2010\bin\System.Data.SQLite.dll MyApp\lib\ copy sqlite-netFx40-2010\bin\System.Data.SQLite.Linq.dll MyApp\lib\ # 如果用 LINQ # 复制原生 Interop dll按平台分开放 copy sqlite-netFx40-2010\bin\x86\SQLite.Interop.dll MyApp\Native\x86\ copy sqlite-netFx40-2010\bin\x64\SQLite.Interop.dll MyApp\Native\x64\然后在 Visual Studio 里对System.Data.SQLite.dll添加引用但不要直接引用SQLite.Interop.dll而是把Native目录下的文件添加到项目中并把它们的「复制到输出目录」属性设为「如果较新则复制」。这里的关键是SQLite.Interop.dll不参与编译引用它只是被复制过去运行时由System.Data.SQLite.dll按进程位数自动查找。参数说明System.Data.SQLite.dll的Copy Local必须为true否则运行目录里没有它。SQLite.Interop.dll的「生成操作」保持「内容」而不是「编译」目标路径不要手动指定让 Visual Studio 按项目目录结构复制到输出目录。如果直接引用SQLite.Interop.dll并设置Copy Local那么它会被复制到输出目录的根上而 System.Data.SQLite 默认的探测路径是按x86或x64子目录来的所以如果你不建子目录就得在代码里手动指定路径这是另一个坑。3.2 在 .NET Framework 4.0 工程里写最短可用的连接代码项目引用配好后第一步就是验证能不能打开数据库。下面是控制台程序里最简的验证代码我会把这部分单独写进一个DbConnChecker类方便后面排查using System; using System.Data.SQLite; class DbConnChecker { static void Main() { // 连接字符串只需要 DataSource 指向 db 文件路径 // Version3 是 SQLite 的版本号固定写法 string connStr Data Sourcetest.db;Version3;; using (SQLiteConnection conn new SQLiteConnection(connStr)) { conn.Open(); // 这一步会触发 SQLite.Interop.dll 加载 Console.WriteLine(连接成功进程位数: Environment.Is64BitProcess); using (SQLiteCommand cmd conn.CreateCommand()) { cmd.CommandText SELECT SQLITE_VERSION();; string version cmd.ExecuteScalar() as string; Console.WriteLine(SQLite 版本: version); } } } }这段代码的关键就是conn.Open()。如果SQLite.Interop.dll没被找到这里会抛DllNotFoundException如果找到的是位数不匹配的会抛BadImageFormatException。代码里的Environment.Is64BitProcess能告诉我们当前进程到底是什么位数的这是排查问题最直接的指标。Data Sourcetest.db表示数据库文件在当前目录你也可以换成绝对路径但注意绝对路径里的反斜杠要转义。3.3 设计时组件VS 设计器的安装与卸载别随便注册如果你只是在学写代码完全不需要安装SQLite.Designer。但很多老项目是用 DataSet 设计器做数据绑定的那就要安装。解压包里通常有sqlite-netFx40-setup-x86-2010.exe这类安装程序或者你可以手动用gacutil和InstallUtil注册。我的建议是只在开发机上装别在构建服务器上装。安装设计时组件的典型命令是# 以管理员身份运行注册设计时 dll 到 GAC C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\gacutil.exe /if sqlite-netFx40-2010\bin\System.Data.SQLite.dll # 用于 VS 设计器的 DLL 要注册到 VS 的扩展目录 C:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe sqlite-netFx40-2010\bin\SQLite.Designer.dll注意InstallUtil.exe的路径可能随系统不同而变化务必先确认。这里最容易翻车的是设计时组件只支持 32 位版本的 VSVS 本身是 32 位进程所以SQLite.Interop.dll必须使用x86版本。如果你在 64 位的 Windows 上把 x64 的 Interop 也注册了设计器反而会在连接数据源时失去响应。我通常的做法是装完后用一个小控制台程序连一次数据库确认再打开 VS 设计器。如果之后不想用了用gacutil /u和InstallUtil /u卸载避免全局污染。4. 用 AnyCPU、x86 还是 x64三种目标平台配置的实际差异与选择4.1 三平台配置与 Interop 拷贝位置的对照目标平台Interop 需要的位置运行时进程位数典型使用场景x86输出目录\x86\SQLite.Interop.dll32位旧插件系统、Office 互操作、内存受限的老服务器x64输出目录\x64\SQLite.Interop.dll64位大型桌面工具、现代服务器、需要大内存的批处理AnyCPU无 Prefer 32-bit同时拷贝 x86 和 x64 两个子目录随系统位数需要分发到不同环境的通用程序这里有一个很重要的细节System.Data.SQLite.dll在运行时查找SQLite.Interop.dll的逻辑是固定的。它会先看当前进程位数的对应子目录x86或x64找不到再看根目录。所以如果你用 AnyCPU就必须把两个子目录都放进输出目录否则在某个位数的系统上会找不到如果你强制定为 x86那只需要x86子目录。静态平台选择是新手最容易困惑的。有人以为x86编译就能在所有 Windows 上跑其实 64 位系统上 32 位进程确实能跑但你要接受内存上限约 2GB 的限制。反过来如果你在 64 位系统上编译成x64把这个程序拷给一台 32 位的老机器直接无法启动。所以「含 32 位和 64 位程序」这个标题的真正含义是你不仅要让工程支持这两种 ffdll还得决定自己的程序到底要跑在哪边。4.2 推荐配置AnyCPU Prefer 32-bit还是强制 x86对于维护 .NET 4.0 老项目这个问题的答案不是永远一致的。我自己的经验是如果这个程序是我自己内部工具只在 64 位 Windows 上跑那我直接选x64不用 x86 子目录最省心如果是要分发给非技术同事对方的系统未知那我选AnyCPU且想办法让进程在 64 位系统上跑 64 位这样性能最好但需要把 x86/x64 两个 Interop 都带上。遗憾的是 .NET 4.0 的 Visual Studio 里没有「Prefer 32-bit」复选框。这个选项是 .NET 4.5 才加入的。所以在 4.0 项目里AnyCPU会按照操作系统位数自动决定进程位数开发者没法在项目属性里强制它「用 32 位」。如果你确实需要在 64 位系统上用 32 位进程比如要加载 32 位 COM 组件只能把平台改成x86这是唯一干净的方案。所以我的结论是不要幻想 AnyCPU 通吃老老实实决定目标平台然后严格按对应子目录放置SQLite.Interop.dll。实际操作上我会在项目工程里添加一个构建后事件脚本自动从源目录复制正确的 Interop# 构建后事件复制对应平台的 Interop 到输出目录的 x86/x64 子文件夹 if $(PlatformName) x86 ( if not exist $(OutDir)x86 mkdir $(OutDir)x86 copy /Y $(ProjectDir)Native\x86\SQLite.Interop.dll $(OutDir)x86\ ) else if $(PlatformName) x64 ( if not exist $(OutDir)x64 mkdir $(OutDir)x64 copy /Y $(ProjectDir)Native\x64\SQLite.Interop.dll $(OutDir)x64\ ) else if $(PlatformName) AnyCPU ( if not exist $(OutDir)x86 mkdir $(OutDir)x86 if not exist $(OutDir)x64 mkdir $(OutDir)x64 copy /Y $(ProjectDir)Native\x86\SQLite.Interop.dll $(OutDir)x86\ copy /Y $(ProjectDir)Native\x64\SQLite.Interop.dll $(OutDir)x64\ )这段脚本的作用是让输出目录自动带上平台匹配的 Interop。对于 AnyCPU两个都带。注意这里的copy /Y用了绝对路径你不一定要这样做但至少能避免每次都手动拖文件。构建后事件脚本在项目属性里设置你需要把$(ProjectDir)替换成你实际的目录结构。我在老项目里就是这样做的再也没出现过「发布后忘了拷 Interop」的情况。5. 32/64 位加载失败避坑五条血泪经验条条都是玄学5.1 现象未能加载文件或程序集“System.Data.SQLite”或它的某一个依赖项现象运行程序时抛出FileLoadException内部可能提到试图加载格式不正确的程序集或更多信息。要对相同的 CLR 版本进行混合模式程序集Build。这个错误在 64 位系统上特别常见。原因这通常是两件事之一。一是System.Data.SQLite.dll被编译为目标 .NET Runtime 版本不兼容比如你拿到的是 .NET 2.0 的库用在 4.0 项目里二是进程位数和SQLite.Interop.dll不匹配但错误被误报成程序集加载失败。解决先检查目标框架和库的版本。sqlite-netFx40 这个目录名已经说明它是针对 .NET 4.0 的但有时里面的 dll 可能是 2.0 编译的你可以用corflags工具查看System.Data.SQLite.dll的 PE 头corflags System.Data.SQLite.dll。看到PE32表示 32 位PE32表示 64 位。不过混合模式程序集通常都是 PE32。更简单的办法是写一个小测试程序在不同平台设置下跑一遍看错误是否随目标平台变化。我遇到过一次最后发现是项目引用了一个安装在其他目录的旧版System.Data.SQLite.dll本地 bin 里反而是新版的顺序被搞乱了。5.2 现象SQLite.Interop.dll 不存在或无法加载现象SQLiteConnection.Open()时抛DllNotFoundException: 无法加载 DLL“SQLite.Interop.dll”。并没有任何格式错误或位数错误的意思。原因这个最直接就是没有找到SQLite.Interop.dll或者找到了但不在它该待的地方。常见原因是手动复制时放到了输出目录的根下而 System.Data.SQLite 默认只在x86、x64子目录以及 DLL 搜索路径里查找。第三个原因是你把SQLite.Interop.dll从解压包里原样复制但它是旧版本且没有经过应有的目录重命名。解决先明确输出目录里有几个SQLite.Interop.dll。如果只有一个且没有放到x86或x64子目录立即改正。用System.AppDomain.CurrentDomain.DynamicDirectory或者Environment.CurrentDirectory查看运行时的工作目录确认是不是在预期入口程序集目录。我习惯在代码里加一行定位输出// 打印当前程序集所在目录帮助定位 Interop 查找位置 string baseDir AppDomain.CurrentDomain.BaseDirectory; Console.WriteLine(BaseDirectory: baseDir); Console.WriteLine(x86 Interop exists: File.Exists(Path.Combine(baseDir, x86, SQLite.Interop.dll))); Console.WriteLine(x64 Interop exists: File.Exists(Path.Combine(baseDir, x64, SQLite.Interop.dll)));这比肉眼看文件管理器靠谱。如果你用的是x86平台就把检查x86的结果打出来如果返回False说明构建后事件没跑或者路径配错了。5.3 现象VS 设计器报错 64 位与 32 位的不匹配现象在 Visual Studio 里双击一个绑定 SQLite 的 DataSet 或实体模型弹出「正在使用的连接是 32 位而进程是 64 位」之类的错误或者直接闪退。原因VS 本身是 32 位进程VS 2022 以前的版本都是 32 位所以用于设计器的SQLite.Interop.dll必须是 x86 版本。很多开发者把 x64 的 Interop 复制到了全局目录被设计器加载后必然出错。解决如果你只在设计器里做数据绑定那就强制把解决方案平台设为x86并只安装 x86 的设计时组件。更干净的方案是完全不在 VS 设计器里连 SQLite用代码创建连接把 SQL 写到资源文件里设计器只负责界面。我现在的做法就是后者——避免设计器纠缠省下一大堆版本配对问题。如果非要装设计器我建议只下载安装官方的sqlite-netFx40-setup-bundle-x86-2010.exe然后让安装程序自己处理注册不要手动拷贝。5.4 现象从 rar 解压后第一次打开 VS 提示 DLL 被占用或拒绝访问现象解压完sqlite-netFx40-2010.rar后打开 VS 加载项目提示无法复制SQLite.Interop.dll因为文件正在被另一个进程使用。原因最常见的是你之前运行过引用这个库的程序VS 调试器还在内存里dll 被锁定了。另一种情况是杀毒软件把解压出来的SQLite.Interop.dll当作可疑文件拦截访问。解决先关闭所有 Visual Studio 进程和运行中的程序包括VBCSCompiler.exe。如果还不行检查杀毒软件的隔离区。我有一次因为杀毒软件误删了 x64 下的SQLite.Interop.dll导致程序在别的机器上翻车。从那以后我在设置里把项目的bin目录加入杀毒排除项这属于典型的「不算 bug 的 bug」但恰恰是玄学高发区。5.5 现象发布后的程序在另一台机器上依然报找不到 Interop现象开发机上运行正常打包发布到别的机器后同样的路径、同样的数据文件却报DllNotFoundException。原因发布时没有包含SQLite.Interop.dll或它的平台子目录。很多人用 VS 的发布功能默认只复制引用的 dll 和内容文件而SQLite.Interop.dll是以「内容」还是「编译」方式存在决定了它会不会被发布。另外如果目标机器装了 32 位系统但你的程序是 AnyCPU 且只带了 x64 子目录也会失败。解决发布前仔细检查发布目录。我一般会在发布脚本里强制复制整个x86和x64文件夹。也可以改用 NuGet 包来发布它会自动带上所需的平台目录。不过题目里指定的是手写的 rar 包那你就做好文件列表检查。下面是一个简单的发布前检查清单# 检查发布目录结构是否完整 ls -R publish/ # 期望看到 publish\System.Data.SQLite.dll # 期望看到 publish\x86\SQLite.Interop.dll # 期望看到 publish\x64\SQLite.Interop.dll # 如果缺任何一个说明发布设置有问题6. 让库自己认位动态加载 SQLite.Interop 的小技巧与验证方法6.1 用 AssemblyResolve 事件按当前进程位数选择不同 Interop如果你坚持用 AnyCPU 分发又担心 System.Data.SQLite 的默认搜索顺序在某些情况下不可靠可以自己接管 Interop 的解析。这个技巧不算复杂我把一个通用的SQLiteInteropHelper写在这里供需要的人参考using System; using System.IO; using System.Reflection; static class SQLiteInteropHelper { // 在 Main 方法开头调用一次即可 public static void Initialize() { AppDomain.CurrentDomain.AssemblyResolve (sender, args) { string name new AssemblyName(args.Name).Name; if (name.Contains(SQLite.Interop)) { // 根据当前进程位数选择 x86 或 x64 子目录 string folder Environment.Is64BitProcess ? x64 : x86; string baseDir AppDomain.CurrentDomain.BaseDirectory; string dllPath Path.Combine(baseDir, folder, SQLite.Interop.dll); if (File.Exists(dllPath)) { return Assembly.LoadFrom(dllPath); } } return null; }; } }这个方法的核心思想是当宿主程序集System.Data.SQLite尝试加载SQLite.Interop时先触发AssemblyResolve事件我们手动告诉运行时「用哪个文件夹里的文件」。使用前需要在程序入口处调用SQLiteInteropHelper.Initialize()。它的好处是即使你在输出目录里放了多个平台文件夹加载逻辑始终和当前进程位数保持一致不会出现错位。缺点是因为SQLite.Interop本身是非托管库Assembly.LoadFrom只是拿到了它的托管代理真正的原生加载仍然由 DllImport 机制完成所以这里的AssemblyResolve并不是万能的——它只对某些托管包装层面有效对原生 DllImport 不一定拦截。我实测过在 .NET 4.0 下DllImport的搜索顺序优先于AssemblyResolve所以这个技巧不能解决所有问题但它能解决一部分场景比如当你的程序集名带有 Interop 时。更可靠的还是把 Interop 正确放进对应子目录。6.2 验证当前进程位数和实际加载的 Interop 路径在写完所有配置后我习惯做一个最终验证程序启动时输出进程位数、Interop 路径、以及通过连接字符串读取PRAGMA compile_options确认确实是 64 位或 32 位的 SQLite。下面这段可以直接贴在 Main 方法里static void Main() { SQLiteInteropHelper.Initialize(); // 如果用了自定义加载 Console.WriteLine(进程位数: (Environment.Is64BitProcess ? x64 : x86)); Console.WriteLine(.NET Framework: Environment.Version); string connStr Data Source:memory:;Version3;; using (SQLiteConnection conn new SQLiteConnection(connStr)) { conn.Open(); using (SQLiteCommand cmd conn.CreateCommand()) { cmd.CommandText SELECT sqlite_compileoption_used(ENABLE_API_ARMOR);; Console.WriteLine(编译选项 ARMOR: cmd.ExecuteScalar()); } conn.Close(); } }这个验证有两个目的第一是确认进程位数和 SQLite 原生库是否一致如果不一致Open会抛错第二是拿到 SQLite 运行时信息确认加载的确实是我们要的版本。另外你还可以用进程管理器看加载的SQLite.Interop.dll的完整路径Windows 的任务管理器——详细信息——选择列「路径」就能看到。这招比任何日志都直接。把这种验证逻辑写进测试脚本以后每次换机器、换数据库文件都先跑一遍再继续开发。我自己多年来一直保留着这个习惯几乎把所有 SQLite 位数的坑都挡在最前面。希望帮到你。最后想说的是这个sqlite-netFx40-2010.rar虽然是老旧的东西但背后的「托管代码原生库」的位数适配问题在今天的 .NET 生态里依然存在。你把这个搞透了以后遇到任何带原生组件的库比如某些加密库、图像库都知道第一时间去看平台目录和进程位数而不是怀疑代码逻辑。祝顺利。本文还有配套的精品资源点击获取