尧图精选

64位C#2012调用SQLite数据库并设置密码的完整源码

🕒 发布时间:2026/10/2 0:23:55 📁 来源:尧图网络
简介这份资源面向64位Windows环境下使用C#语言配合Visual Studio 2012开发工具调用SQLite数据库的开发者着重演示如何基于System.Data.SQLite组件完成创建数据库、建立数据表、插入数据、查询数据等常用操作并讲解在连接字符串中设置密码以保护数据库文件的安全做法。资源包共包含42个文件以C#源代码、项目配置文件、可执行程序以及调试符号文件为主要类型压缩包大小为2.48MB目录结构清晰便于开发者对照学习。目前已有850人学习下载。通过该工程读者可以查看完整的窗体界面源码掌握带密码参数的SQLite连接字符串写法了解SQLiteCommand参数化增删改查和SQLiteDataReader读取结果集的具体实现同时还能参考SQLitePassWord项目文件中关于密码保护的组织方式。适合初、中级C#开发者快速上手轻量级嵌入式数据库的应用开发。1. 64位C#2012调用SQLite数据库这套源码真正难的是密码和进程位数VS2012 里写 C# 上位机数据要落到本地文件最顺手的选择就是 SQLite 数据库免安装、单文件、标准 SQL 语法。但真把工程切到 64 位、再想给库文件加个密码时很多人会发现“连上了”和“跑对了”是两回事——System.Data.SQLite.dll 和 SQLite.Interop.dll 的分工、x64 目录结构、密码 API 的执行时机随便一个环节不对就是运行时翻车。这篇文章把 64 位 C#2012 调用 SQLite 的完整源码思路拆开讲从选型、建库、增删改查到给数据库设置密码并验证密码真的生效照着搭就能直接跑。2. 选型System.Data.SQLite 的三种调用方式与 64 位部署形态2.1 C# 里调用 SQLite 的三条路ADO.NET、官方包、原生 P/InvokeC# 侧接 SQLite 从来不止一种方式但适合 VS2012 工程的并不多。第一条路是 ADO.NET 提供程序 System.Data.SQLite它实现了 SqlConnection 那套接口规范老项目迁移成本最低也是这套源码采用的方式。第二条路是微软官方的 Microsoft.Data.Sqlite它在 .NET Core 时代才出现VS2012 默认的 .NET Framework 4.5 工程用不了直接排除。第三条路是 P/Invoke 直接调 sqlite3.dll需要自己封装大量 C API而且官方 sqlite3.dll 不带加密集密码功能还得额外找 SQLCipher 编译版本工程量大得没必要。对于“64 位 C#2012 调用 SQLite 数据库源码含设置密码”这个诉求System.Data.SQLite 几乎是唯一能同时满足几个条件的方案兼容 .NET 4.5、原生支持 x64 部署、内部自带加密 API。它在 NuGet 上的包全名是 System.Data.SQLite.Core我一般只装 Core 包不带设计时组件。2.2 两个 DLL 的分工System.Data.SQLite.dll 与 SQLite.Interop.dll很多第一次做 64 位 SQLite 的人只拷了一个 System.Data.SQLite.dll 到 bin 目录然后运行时报错找不到原生库。这不是玄学是 System.Data.SQLite 的架构决定的System.Data.SQLite.dll 是托管壳层负责 ADO.NET 连接管理、SQL 语句调度和类型转换真正执行 SQL、读写页文件的是原生 DLL SQLite.Interop.dll。托管壳只是把调用转发给原生引擎。更重要的是加载规则。System.Data.SQLite.dll 会根据当前进程位数去应用目录下的 x86 或 x64 子目录里寻找对应位数的 SQLite.Interop.dll。也就是说它的目录结构必须是这个形态bin\ System.Data.SQLite.dll x86\SQLite.Interop.dll x64\SQLite.Interop.dll项目跑在 64 位进程里就去 x64\SQLite.Interop.dll跑在 32 位进程里就去 x86\SQLite.Interop.dll。如果只把原生 DLL 放在根目录、或者忘了建子目录System.Data.SQLite.dll 会直接抛找不到文件。这也是 64 位部署时最容易踩的一步。2.3 用 NuGet 在 VS2012 里安装并确认目录结构VS2012 自带 NuGet 包管理器安装操作很直接Install-Package System.Data.SQLite.Core -Version 1.0.96.0版本号不一定要卡这个装最新兼容 .NET 4.5 的即可。安装完NuGet 会自动处理三件事把 System.Data.SQLite.dll 放到输出目录根、把 x86\x64 两个子目录连同 SQLite.Interop.dll 一起拷进 bin、并把 Copy Local 属性设置好。装完建议立刻检查输出目录这个动作在后续排错里经常能救命。从这条命令往后整个工程引用的就是这套“托管壳 原生引擎”组合。在这个基础上做的连接、读写、加密代码跟直接用原始 sqlite3.dll 是完全不同的体验你可以用 SqliteConnection、SqliteCommand 这种标准 ADO.NET 对象写业务逻辑而不是手推指针和回调。3. 主流程源码从连接串到增删改查的完整可运行代码3.1 连接串与建库版本、路径、超时参数先把最小的可运行代码立起来后面所有加密逻辑都挂在这套连接之上。我一般会新建一个控制台工程目标框架选 .NET 4.5然后写入口代码using System; using System.Data.SQLite; namespace SQLiteDemo { class Program { static void Main(string[] args) { // 数据库文件放在程序目录下避免工作目录不一致导致找不到文件 string dbPath AppDomain.CurrentDomain.BaseDirectory app.db; string connStr Data Source dbPath ;Version3;Default Timeout30;; using (SQLiteConnection conn new SQLiteConnection(connStr)) { conn.Open(); string createSql CREATE TABLE IF NOT EXISTS t_meter( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, value REAL); using (SQLiteCommand cmd new SQLiteCommand(createSql, conn)) { cmd.ExecuteNonQuery(); } Console.WriteLine(数据库创建成功 dbPath); } } } }连接串里的三个参数分别有讲究。Data Source 指定库文件路径推荐拼 BaseDirectory 绝对路径防止调试时当前目录漂移Version3 是固定写法SQLite 3.x 版本都这样写Default Timeout30 是命令执行超时单位秒遇到锁竞争时这个参数比想象中更重要。建表语句加了 IF NOT EXISTS重复运行不会报错。AUTOINCREMENT 不是必需的但如果你需要保证 id 严格递增且不重用就保留它。这里有个容易犯的错有人把连接串写成DataSource而不是Data Source中间少个空格。System.Data.SQLite 对老写法的兼容时好时坏与其赌兼容性不如直接用带空格的规范写法。3.2 参数化增删改查与类型映射连接能打开只是第一步业务代码迟早要处理增删改查。下面这段是参数化写法和读取的类型映射也是这套源码里被复用最多的模板using (SQLiteConnection conn new SQLiteConnection(connStr)) { conn.Open(); // 插入参数化是必须的杜绝拼接 SQL string insertSql INSERT INTO t_meter(name, value) VALUES(name, value); using (SQLiteCommand cmd new SQLiteCommand(insertSql, conn)) { cmd.Parameters.AddWithValue(name, A相电压); cmd.Parameters.AddWithValue(value, 220.5); int affected cmd.ExecuteNonQuery(); Console.WriteLine(插入行数 affected); } // 查询条件也参数化 string selectSql SELECT id, name, value FROM t_meter WHERE value min; using (SQLiteCommand cmd new SQLiteCommand(selectSql, conn)) { cmd.Parameters.AddWithValue(min, 200.0); using (SQLiteDataReader reader cmd.ExecuteReader()) { while (reader.Read()) { // SQLite 的 INTEGER 映射到 C# 的 long long id (long)reader[id]; string name reader[name].ToString(); // REAL 映射到 double double value (double)reader[value]; Console.WriteLine(${id} | {name} | {value}); } } } }关于 AddWithValue有两个细节值得注意。第一传 null 时要用DBNull.Value否则行为不确定第二高频写入场景不建议用 AddWithValue因为参数类型每次都推断有开销建议改成cmd.Parameters.Add(value, DbType.Double).Value value。读取侧的映射规则是固定的INTEGER 类型读出来是 longREAL 是 doubleTEXT 是 stringBLOB 是 byte[]。如果库里存的是INTEGER你却用(int)reader[id]转换会抛 InvalidCastException这就是为什么上面的代码用 long 接。3.3 平台目标设为 x64 并正确部署原生 DLL代码层面没问题后还要让程序真跑在 64 位进程里。VS2012 的操作路径是项目属性 → 生成 → 平台目标 → 选 x64然后重新生成解决方案。为什么不建议用默认的 AnyCPUVS2012 的 AnyCPU 在 64 位系统上默认就是 64 位进程表面看也能跑但有两个隐患一是后续迁移到高版本 Visual Studio 时项目可能会被自动勾选“首选 32 位”进程位数悄悄变回 x86二是 System.Data.SQLite.dll 内部通过Environment.Is64BitProcess决定加载哪个 InteropAnyCPU 加上某些第三方 32 位组件的引用后容易整体降级成 32 位。直接锁死 x64行为最可预期。部署时只需要确认输出目录里同时存在三件东西bin\x64\ System.Data.SQLite.dll实际引用的是根目录那份托管 DLL SQLite.Interop.dll原生引擎x64 版 bin\项目.exe bin\System.Data.SQLite.dll验证是否真的进了 64 位可以在入口打一行Console.WriteLine(Environment.Is64BitProcess);输出 True 就说明 x64 部署成功。这一步是后面所有密码功能的前提位数不对加密库的读写行为都会变得不可捉摸。4. 给 SQLite 设置密码加密库创建、改密与真实性验证4.1 新建数据库时直接创建加密库先说清楚一个前提SQLite 官方版本本身没有密码机制密码功能是 System.Data.SQLite 这个发行版自己实现的页级加密扩展。它跟 SQLCipher 不是同一套格式所以“SQLCipher 的库拿到 System.Data.SQLite 里开”或者反过来都打不开。这套源码里的密码都基于 System.Data.SQLite 的 API用同族工具链来处理即可。新建加密库非常简单连接串里加一个 Password 参数string dbPath AppDomain.CurrentDomain.BaseDirectory encrypted.db; // 关键点建库时带上 Password生成的库文件本身就是加密的 string connStr Data Source dbPath ;Version3;PasswordMyPass123;; using (SQLiteConnection conn new SQLiteConnection(connStr)) { conn.Open(); string createSql CREATE TABLE IF NOT EXISTS t_secret(k TEXT, v TEXT); using (SQLiteCommand cmd new SQLiteCommand(createSql, conn)) { cmd.ExecuteNonQuery(); } Console.WriteLine(加密库创建成功文件头已不是明文); }这段代码运行后磁盘上的 encrypted.db 文件头不再是 “SQLite format 3” 这串明文而是一堆不可读的密文。从这一刻起任何不带密码的连接字符串去打开这个库都会抛“file is encrypted or is not a database”异常。这个行为本身可以直接当作加密是否生效的试金石。4.2 给已有明文库补上密码ChangePassword 的用法与时机更多的现实场景是手里已经有一个跑了好久的明文库想在不丢失数据的前提下加上密码。这时不能只改连接串因为连接串里的 Password 只在“新建加密库”或者“连接加密库”时起作用对一个已存在的明文库连接串写 Password 并不能让它变成加密库。必须调用 ChangePasswordstring oldDb AppDomain.CurrentDomain.BaseDirectory plain.db; string connStr Data Source oldDb ;Version3;; using (SQLiteConnection conn new SQLiteConnection(connStr)) { conn.Open(); // 把当前打开的明文库立即重写为加密库 conn.ChangePassword(MyPass123); Console.WriteLine(明文库已转换为加密库); }ChangePassword 是 SQLiteConnection 的实例方法调用前提是连接已经处于 Open 状态而且在它之后不要再执行任何其它事务操作。它的执行时机很关键连上明文库后第一时间调用确保所有页都带着密钥重写回文件。修改已有加密库的密码也是同一个方法用旧密码打开连接再调用 ChangePassword 传新密码旧密钥就被替换成新密钥。如果你想反过来把加密库恢复成明文库调用conn.ChangePassword(null)即可。这个操作会把整个库重写为无密码文件相当于把密码功能彻底拆掉。4.3 验证密码真的生效文件头检查与错误密码试连设置密码之后不能只看“没报错”就认为完成我用两个手段验证缺一不可。第一个手段是检查文件头。SQLite 明文库的前 16 个字节固定是 “SQLite format 3\0”被 System.Data.SQLite 加密后的文件这 16 个字节变成了密文。写一个 5 行的判断函数就行static bool IsPlainSqlite(string path) { byte[] header new byte[16]; using (System.IO.FileStream fs new System.IO.FileStream(path, System.IO.FileMode.Open)) { fs.Read(header, 0, 16); } return System.Text.Encoding.ASCII.GetString(header).StartsWith(SQLite format 3); }加密库返回 False明文库返回 True这个函数肉眼可验证比任何解释都有说服力。第二个手段是用错误密码去连一次确认系统真的会拒绝try { using (SQLiteConnection conn new SQLiteConnection( Data Source dbPath ;PasswordWrongPass;)) { conn.Open(); } } catch (SQLiteException ex) { // 数据库是加密的密码错误时这里必然走进来 Console.WriteLine(错误密码被拒绝 ex.Message); }这里要提醒一个常见误区DB Browser for SQLite 虽然能打开带密码的库但它支持的加密格式跟 System.Data.SQLite 不通用。你用 DB Browser 输密码去开 System.Data.SQLite 加密出来的库它大概率直接提示文件损坏或无法识别这不代表密码设置失败只是格式不同。验证加密是否生效优先用上面两个手段不要拿非同类工具下结论。5. 常见问题与避坑64位 SQLite 在 VS2012 里最容易翻车的五个地方5.1 运行时报错找不到 SQLite.Interop.dll现象是程序编译通过一运行就抛异常Unable to load DLL SQLite.Interop.dll或者Could not load file or assembly ... SQLite.Interop.dll。原因是部署结构不对——System.Data.SQLite.dll 作为托管壳被找到了但它加载原生引擎时按进程位数去 x64 或 x86 子目录找子目录不存在直接失败。解决办法是重新建立标准目录结构把原生 DLL 放回对应子目录bin\System.Data.SQLite.dll bin\x64\SQLite.Interop.dll bin\x86\SQLite.Interop.dll处理完记得重新生成工程确认这两个子目录真的出现在输出路径里。如果用的是手动引用 DLL 的部署方式比如从别的机器上拷 dll最容易漏的就是子目录结构我做过一次这种事根目录文件全齐唯独少了 x64 子文件夹排查了半小时。血的教训先看输出目录再查代码。5.2 Mixed Mode Assembly 报错.NET 运行时版本对不上现象引用 System.Data.SQLite 后程序一启动就抛Mixed mode assembly is built against version v2.0.50727 of the runtime。原因是 System.Data.SQLite.dll 本身是混合模式程序集内部同时包含托管 IL 和原生代码部分版本是按 .NET 2.0 目标编译的。当宿主工程跑在 .NET 4.x 上而没有显式声明兼容策略时就会出现这个错误。解决方式是在 app.config 里加一段配置?xml version1.0 encodingutf-8? configuration startup useLegacyV2RuntimeActivationPolicytrue / /configurationuseLegacyV2RuntimeActivationPolicy 的意义是允许 CLR 2.0 的混合模式程序集加载进 CLR 4.x 运行时。加了它之后绝大多数旧版 System.Data.SQLite 都能正常跑。如果加了还报错就优先升级 NuGet 包新版包已经没有这个问题不要从网上下载来路不明的旧版 dll 硬顶。5.3 密码设了却还能被明文工具直接打开现象代码里写了 Password库文件也能正常打开但用户用别的工具一看数据全是明文。这个坑分两种情况。第一种情况你只是改了连接串没有对已存在的库执行 ChangePassword。连接串里的 Password 只负责“新建加密库”和“连接加密库”它不具备把明文库就地加密的能力。处理方式是先判断库是不是明文是明文就主动调 ChangePassword。第二种情况隐蔽得多库开了 WAL 模式加密前未执行 checkpoint导致明文页残留在 -wal 文件中。SQLite 的 WAL 模式会把新写入数据先追加到同名 .wal 文件ChangePassword 加密的是主库文件遗漏的 -wal 文件里还留着加密前的原始页。之后正常打开库WAL 重放部分页面绕过了加密读取。解决思路是加密操作前强制切回 DELETE 日志模式并做一次 checkpoint// 加密前先把 WAL 写回主库并清理 wal/shm 文件 using (SQLiteCommand cmd new SQLiteCommand(PRAGMA journal_modeDELETE;, conn)) { cmd.ExecuteNonQuery(); } // 然后执行 ChangePassword conn.ChangePassword(MyPass123);执行完这句 PRAGMA旧事务日志被合并回收再加密才真正做到全文件加密。没有这条经验的人很容易把“数据库文件加密了”误当成“整个库都安全了”实际上是主库密文、日志明文的状态。5.4 高并发写入时报 database is locked现象多线程或多进程同时写入时第二个写连接等待超时后抛出database is locked。原因不是密码功能的问题而是 SQLite 本身的锁模型同一时间只允许一个写事务其它写请求必须等待默认 busy timeout 又很短等不到锁就被判定超时。常规处理有两手连接串加长时间同时显式设置 busy_timeoutData Sourceapp.db;Version3;Default Timeout30;using (SQLiteCommand cmd new SQLiteCommand(PRAGMA busy_timeout5000;, conn)) { cmd.ExecuteNonQuery(); }这两行组合的语义是遇到锁冲突时最多等 5 秒而不是立即放弃。此外检查一下代码里有没有把 SQLiteDataReader 还开着就去执行另一个写操作这种嵌套持有连接的情况也会触发锁。锁定问题排查时先看代码结构再看超时参数绝大多数是前者。5.5 确认进程真的以 64 位在跑现象代码逻辑完全一致但 SQLite 行为异常或者加载的 DLL 版本混乱。原因可能是平台目标设置没生效或者工程被另一个 x86 工程引用最终进程还是 32 位。判断方法不要靠猜直接打一行Console.WriteLine(Is64BitProcess Environment.Is64BitProcess); Console.WriteLine(IntPtr.Size IntPtr.Size);64 位进程输出 True、832 位进程输出 False、4。如果输出是 False 但项目属性显示 x64检查一下是不是最终启动入口工程被引用链降级了。这套源码的后续所有功能包括加密 API 的参数传递都要求进程位数和 SQLite.Interop.dll 位数严格一致。进程位数是 x64、用的却是 x86 子目录里的 Interop系统加载时一样会报错。先确认位数再查其它问题能省掉一半的排查时间。6. 进阶加密库进入生产环境前要处理的三个细节6.1 加密库慎用 WAL 模式上面第 5.3 节提到过 WAL 模式会留下 -wal 文件这个隐患在生产环境里不仅是加密时机的问题。即使加密库已经稳定运行只要 journal_mode 是 WAL每次写入都会产生一个 -wal 文件而这个文件里的页在部分场景下是明文。对于“含设置密码”的库我把默认日志模式固定为 DELETE数据写入时时落回主库不从文件结构上留尾巴。如果实在需要 WAL 的并发读性能至少对 -wal 文件做同等权限管控并定期执行 checkpoint 强制合并。6.2 备份加密库用 BackupDatabase不要直接拷文件数据库文件在打开状态下直接复制得到的一致性和完整性都没有保证。System.Data.SQLite 提供了封装好的备份方法using (SQLiteConnection dest new SQLiteConnection(Data Sourcebackup.db)) { dest.Open(); // 把源数据库的 main schema 备份到目标库 conn.BackupDatabase(dest, main, main); }这个方法走的是 SQLite 官方备份 API能保证页级一致性。备份出来的库同样继承源库的加密状态需要用密码连接才能读取。养成用 API 备份的习惯后我就不再干“先关程序再复制文件”这种原始操作了少了很多莫名奇妙的文件损坏。6.3 改密码后清掉连接池里的旧连接System.Data.SQLite 默认启用连接池。改密码后池里可能还残留着旧密钥打开的连接。一个新的业务连接如果被池里复用了旧连接用的还是旧密钥一旦数据库侧密钥已经轮换就会出现“密码改了老连接还能访问”的诡异现象。轮换密码后加一行清理SQLiteConnection.ClearPool(conn);这个动作会把该连接串对应的池清空后续连接全部按新密码重新建立。密码轮换是一件低频但关键的操作舍得花这一行代码线上就能少一次莫名其妙的数据访问异常。我现在接手 SQLite 相关的工程习惯是先把目录结构、进程位数、加密状态一次确认完再写业务代码这套源码如果从一开始就按这个顺序做后面几乎所有坑都能绕开。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →