☰
64位C#2012调用SQLite设置密码完整源码与避坑指南
2026/10/1 13:27:29 网站建设 项目流程

简介:面向64位Windows平台C#开发者的SQLite集成示例工程,完整演示VS2012环境下调用System.Data.SQLite进行数据库创建、连接、建表、增改查等操作,并包含通过连接字符串设置密码的加密实践,适合需要为轻量级应用快速加入本地存储与安全特性的中高级开发人员参考。压缩包共42个文件,以C#源文件(.cs)为主,辅以项目配置(.sln/.csproj/.config)、编译产物(.exe/.dll)及调试符号等,整包约2.48MB,结构清晰便于直接阅读与复用。已有850人学习下载。资源内含完整窗体项目,示例代码覆盖SQLiteCommand参数化操作与SQLiteDataReader查询,可帮助理解嵌入式数据库在C#中的标准调用方式,同时展示密码设置在连接字符串中的具体写法,是学习SQLite与C#集成的实用源码包。

1. 64位C#2012调用SQLite数据库源码,含设置密码:先用一个反直觉结论开场

一个很反直觉的结论放在最前面:在C#2012工程里给SQLite设置密码,最难的部分不是写那句带Password的连接串,而是确认你手里的SQLite.DLL编译时确实带上了加密扩展。很多人照着下载的源码改完,一执行就抛“file is encrypted or is not a database”,于是以为是密码写错了,实际上问题出在64位DLL版本和加密支持没有对上。C#2012工程要调用SQLite并真正把密码用起来,至少要过三道关:64位DLL选型与部署、连接串与ADO.NET调用源码、以及密码加密层的启用与验证。这篇笔记把这套方案从选型到踩坑完整拆开,适合正在维护Windows 64位工控机或桌面工具、手上有C#2012旧工程、又不敢轻易升级框架的从业者。

2. C#2012安装使用SQLite之前,先把64位DLL选型和部署定死

很多人拿到“64位C#2012调用SQLite数据库源码”之后,第一件事是把代码复制进工程里编译,然后被BadImageFormatException砸懵。这个异常几乎都是DLL选型或部署问题,而不是源码问题。所以这一章先把地基打牢:用什么DLL、怎么保证64位、怎么部署才稳定。

2.1 三种调用方式对比:为什么源码首选System.Data.SQLite

C#调用SQLite常见有三条路。第一条是直接用官方sqlite3.dll,通过P/Invoke自己声明函数指针,这种方式最底层,灵活性高但代码量大,还要自己管回调、语句句柄和内存释放,源码里任何一个IntPtr泄掉都可能让进程崩溃。第二条是用SQLCipher的.NET绑定,加密强度高、跨平台,但它包的命名空间和连接串体系跟ADO.NET差异较大,老工程迁移成本偏高。第三条就是System.Data.SQLite,它本质上是SQLite官方原生库外面包了一层ADO.NET提供程序,C#里用SqlConnection那套写法就能操作,VS2012的.NET 4.5工程直接引用就能跑,也是大多数“含设置密码”源码默认配套的方案。

方案加密支持与C#工程集成度适用场景
原生sqlite3.dll + P/Invoke官方不可用,需自行编译CODEC低,需大量互操作代码嵌入式底层组件
System.Data.SQLite编译期启用SQLITE_HAS_CODEC后可用Password高,ADO.NET标准接口C#桌面与工控机首选
SQLCipher .NET绑定内置AES-256加密中,命名空间与ADO.NET差异大跨平台、高安全诉求

我的建议是:只要你的源码是给C#2012用的、业务又以增删改查为主,就锁定System.Data.SQLite。它有几个点别人替代不了:一是连接串可以直接写Password参数,二是提供了PRAGMA rekey改密语句的完整封装,三是官方发布包把托管DLL和原生DLL分开,部署时能按进程位数自动选。后面所有源码都以它为准。

2.2 VS2012工程切64位编译:要动的三个配置位置

标题里明写了64位,但工程里默认“Any CPU”其实是个陷阱。Any CPU在64位系统上会以64位进程运行,而System.Data.SQLite官方包默认把SQLite.Interop.dll分成了x86和x64两套。如果引用的是32位那套原生DLL,Any CPU跑起来就是“托管DLL是64位、原生DLL是32位”,直接报BadImageFormatException。所以第一步是把编译目标定死。

在VS2012里依次打开“项目属性 → 生成 → 平台目标”,把“Any CPU”改成“x64”,同时确认“首选32位”复选框没有被勾选。这里容易漏的不是编译选项本身,而是配置文件里可能残留的x86平台项。最稳的做法是打开工程的.csproj文件,直接看PlatformTarget节点:

<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> <PlatformTarget>x64</PlatformTarget> <DebugType>pdbonly</DebugType> <Optimize>true</Optimize> <OutputPath>bin\Release\</OutputPath> </PropertyGroup>

这段配置的含义是:只有Release加上x64组合才会被识别为有效编译目标,PlatformTarget= x64让生成的程序集PE头标记为64位,CLR加载时会强制按64位进程初始化。OutputPath指向bin\Release,避免DLL拷错目录。如果你在旧源码里只改了IDE下拉框、没动csproj,重新拉代码编译时又会退回Any CPU,这就是为什么我要强调直接检查XML节点。

改完之后还有个验证动作。在Main函数里加一行环境检查,运行时就知道自己到底是不是64位进程,省得日后排查方向跑偏:

static void Main() { Console.WriteLine("Is64BitProcess=" + Environment.Is64BitProcess); Console.WriteLine("Is64BitOperatingSystem=" + Environment.Is64BitOperatingSystem); // 输出 True/True 才说明编译目标改对了 }

逻辑说明:Environment.Is64BitProcess看的是当前进程位数,Is64BitOperatingSystem看的是操作系统位数。输出“True/True”代表64位进程跑在64位系统上;如果第一项是False,说明csproj的PlatformTarget没生效,回到上一段检查XML。这个过程不用调试器,一条Console输出就能把问题框定。

2.3 DLL部署与App.config探路:x64/x86目录自动选择

System.Data.SQLite官方发布包的典型文件布局是:根目录放System.Data.SQLite.dll托管程序集,下面分x64和x86两个子目录,各放一份SQLite.Interop.dll原生库。部署时把整个目录结构原样拷到exe旁边,然后在App.config里加probe路径,CLR就会按进程位数自动到对应子目录加载原生DLL。

<?xml version="1.0" encoding="utf-8"?> <configuration> <startup useLegacyV2RuntimeActivationPolicy="true"> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/> </startup> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <probing privatePath="x64;x86"/> </assemblyBinding> </runtime> </configuration>

逻辑说明:startup节点里的supportedRuntime声明这个程序只在.NET 4.5运行时下运行,VS2012默认工具链生成的就是这个版本,写明确能防止老机器上被强制回退到.NET 2.0导致类型加载失败。probing的privatePath告诉运行时,当exe同目录找不到原生DLL时,去x64或者x86子目录里找,具体去哪个由进程位数决定。这样发布包可以同时带上两份原生DLL,32位、64位系统通用。

这里有一个参数细节要说明:useLegacyV2RuntimeActivationPolicy="true"在纯.NET 4.5工程里不是必须的,但如果你在旧源码里混用了CLR 2.0组件,这一项能避免“混合模式程序集”的运行时异常。我一般习惯保留它,成本极低,省得发布到用户机器上突然翻车。

3. 用C#打开SQLite数据库:最小可编译源码与连接串六个参数

地基打完之后,进入正题。这一章先不碰密码,用最干净的源码把“C#打开SQLite数据库”这条链路跑通,然后把连接串参数表拆开讲清楚,最后补多线程场景下的注意事项。密码只是连接串里多个参数中的一个,先理解整个参数体系,后面调密码才不迷糊。

3.1 完整读写源码:打开、建表、插入、查询一气呵成

下面这段代码是一个最小可编译示例,功能是把SQLite数据库、建表、插入和查询全部走一遍。它不依赖任何第三方UI组件,控制台工程里粘进去就能跑。

using System; using System.Data.SQLite; class SqliteDemo { static void Main() { // 构造连接串:指定数据库文件、SQLite版本、开启连接池、共享缓存 string connStr = "Data Source=appdata.db;Version=3;Pooling=True;Cache=Shared;"; using (SQLiteConnection conn = new SQLiteConnection(connStr)) { conn.Open(); // 建表:主键自增,name为文本,value为实数 string createSql = "CREATE TABLE IF NOT EXISTS device (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "name TEXT NOT NULL, " + "value REAL)"; using (SQLiteCommand cmd = new SQLiteCommand(createSql, conn)) { cmd.ExecuteNonQuery(); } // 插入:参数化写法,避免拼接SQL注入 using (SQLiteCommand cmd = new SQLiteCommand( "INSERT INTO device(name, value) VALUES(@name, @value)", conn)) { cmd.Parameters.AddWithValue("@name", "temp_sensor"); cmd.Parameters.AddWithValue("@value", 36.5); cmd.ExecuteNonQuery(); } // 查询:DataReader流式读取,用完即关 using (SQLiteCommand cmd = new SQLiteCommand( "SELECT id, name, value FROM device", conn)) using (SQLiteDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { Console.WriteLine("{0} {1} {2}", reader["id"], reader["name"], reader["value"]); } } } } }

逻辑说明:using语句覆盖了SQLiteConnection、SQLiteCommand和SQLiteDataReader三个对象,好处是无论执行成功还是抛异常,连接和阅读器都会在作用域结束时自动关闭。System.Data.SQLite的Connection如果不及时释放,文件会被进程独占,后续备份或删除都会报“文件被占用”。参数化插入是另一个关键点,SQLite没有像SQL Server那么复杂的权限体系,但它同样会执行拼接后的SQL,直接用字符串拼变量的源码在工控环境里容易出事故。

参数说明:AddWithValue的两个参数,第一个@name对应SQL语句里的命名参数,第二个是实际值。SQLite参数名不区分大小写,但中间的@必须有。如果你在旧源码里看到的是“?”占位符,那也可以,只是可读性差一些。ExecuteNonQuery返回值是受影响行数,插入场景里可以用来做写入确认。

3.2 连接串参数明细:Version、Pooling、Cache、FailIfMissing

“Data Source=appdata.db;Version=3;Pooling=True;Cache=Shared”这句连接串表面简单,里面每个分号项都有讲究。我把最常见的六个参数整理成表,方便你对照手上的源码做调整。

参数取值示例作用与坑
Data Sourceappdata.db 或绝对路径数据库文件路径;相对路径以当前工作目录为基准,服务化部署时容易找错文件
Version3固定为3,对应SQLite文件格式版本,写2是老项目遗留,新库不要用
FailIfMissingTrue / FalseTrue时文件不存在直接抛异常;False(默认)时自动创建空库
PoolingTrue / False开启连接池可显著提升高频读写性能,但改密码后要清池
CacheShared / PrivateShared允许多个连接共享同一页缓存,多线程场景必开
DefaultTimeout30命令默认超时秒数;数据库被外部工具锁住时,这个值决定等待多久

这里重点说两个容易错的。FailIfMissing默认是False,意味着连接串指向一个不存在的文件时会立刻建库,这在首次部署时不一定是坏事,但如果你是想用“文件在不在”来判断程序初始化状态,就得改成True,否则文件被误删时程序会静默重建一个新库,数据全丢还不报错。另一个是Cache=Shared,System.Data.SQLite在不开启共享缓存时,每个连接都有自己的页缓存,隔离性更强;但多线程或多连接同时读写时,开Shared能减少磁盘IO竞争,代价是并发控制责任从SQLite引擎转移到开发者身上。

3.3 多线程并发时Cache=Shared和事务的配合

桌面程序打开SQLite数据库通常只有一个连接,但工控机上的采集线程和UI线程如果各开一个连接,就会出现“database is locked”。常见做法是连接串里写Cache=Shared,同时把写操作包进事务。SQLite同一时刻只允许一个写事务,多个线程轮流写时,没有事务会让每次INSERT都变成一次隐式事务提交,锁竞争反而更激烈。

using (SQLiteConnection conn = new SQLiteConnection(connStr)) { conn.Open(); using (SQLiteTransaction tx = conn.BeginTransaction()) { using (SQLiteCommand cmd = new SQLiteCommand( "INSERT INTO device(name, value) VALUES(@name, @value)", conn, tx)) { for (int i = 0; i < 500; i++) { cmd.Parameters.Clear(); cmd.Parameters.AddWithValue("@name", "sensor_" + i); cmd.Parameters.AddWithValue("@value", i * 0.1); cmd.ExecuteNonQuery(); } } tx.Commit(); } }

逻辑说明:BeginTransaction之后,所有命令都挂在同一个事务对象上,500次插入只在最后Commit时刷一次磁盘,写盘次数从500次降到1次。参数说明里值得注意的一点是,SQLiteCommand构造函数第三个参数接收事务对象,漏传的话命令会自动脱离事务,每条语句又变回独立提交。如果你在源码里看到循环体外面没有BeginTransaction、里面没有Commit,批量写入慢就是这个原因。

4. 给SQLite设置密码:加密原理、创建与rekey两步源码

这一章回答标题里的“含设置密码”。先讲清楚密码在SQLite里的真实位置——它不是SQLite自带功能,再给你两段能落地的源码:一段是创建带密码数据库,一段是修改或移除密码。最后补一下外部工具打开带密码库的正确姿势。

4.1 SQLite不原生支持密码:加密扩展在DLL编译期决定

原版SQLite源码里没有“密码”这个概念。所谓设置密码,本质上是使用了编译时启用了加密扩展的SQLite版本。System.Data.SQLite的做法是,在编译原生库时打开SQLITE_HAS_CODEC宏,并且把连接串里的Password参数传给加密模块。加密后整个数据库文件变成密文,没有正确密码时,连文件头都读不出来。

这里有一个被很多源码教程刻意略过的关键事实:普通预编译的System.Data.SQLite.dll不一定带加密支持。你在连接串里写Password,如果DLL没编译加密扩展,执行Open或者第一条SQL时会直接抛“file is encrypted or is not a database”或者“not compiled with encryption support”。判断标准很简单:拿到源码包后先找里面有没有SQLite.Interop.dll,再看发布说明里是否提到SQLITE_HAS_CODEC或“encryption support”。如果源码包只给了一堆.cs文件却没配套DLL,生成出来是没法真正加密的。

从另外一个角度看,这也解释了为什么同一套源码有人用得好好的,有人复制过去就报错。加密不是一段托管代码能完成的事,它依赖原生DLL的编译开关。所以你在网上找“64位C#2012调用SQLite数据库源码,含设置密码”时,必须确认两样东西:一是64位原生DLL,二是带加密宏的构建版本,缺一个都不行。

4.2 创建带密码数据库:Password连接串与建表源码

创建带密码库的源码和普通建库几乎一样,唯一区别是连接串里多了Password项。第一次Open动作会把密码初始化到加密层,之后这个文件就必须用密码才能打开。

using System; using System.Data.SQLite; class CreateEncryptedDb { static void Main() { // 第一次创建:Password参数即初始密码 string connStr = "Data Source=secure.db;Version=3;Password=mySecret123;Pooling=False;"; using (SQLiteConnection conn = new SQLiteConnection(connStr)) { conn.Open(); // 关键:Open完成时加密已经生效 string sql = "CREATE TABLE IF NOT EXISTS account (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "username TEXT NOT NULL, " + "passhash TEXT NOT NULL)"; using (SQLiteCommand cmd = new SQLiteCommand(sql, conn)) { cmd.ExecuteNonQuery(); } using (SQLiteCommand cmd = new SQLiteCommand( "INSERT INTO account(username, passhash) VALUES(@u, @p)", conn)) { cmd.Parameters.AddWithValue("@u", "admin"); cmd.Parameters.AddWithValue("@p", "not-a-real-hash"); cmd.ExecuteNonQuery(); } } Console.WriteLine("数据库已创建并加密,文件: secure.db"); } }

逻辑说明:连接串里的Password=mySecret123在第一次Open时会被System.Data.SQLite传给加密模块,作为新库的初始密钥。之后所有写入的数据在页级别加密,不是只对某一列加密。Pooling=False在这里是刻意加的,避免创建操作产生的连接被池化后,后续验证代码拿到的还是旧连接。

参数说明:密码不要用纯数字,SQLite加密模块对短密钥支持不算好,我一般要求至少8位、大小写混合。另一个容易被忽略的参数是Data Source指向的文件路径,如果secure.db已经存在且不是加密格式,再用带Password的连接串去Open也会报错,因为加密模块尝试按密文解析明文文件头,解析失败直接抛异常。首次创建时确保文件不存在,或者先物理删除旧文件。

4.3 修改和移除密码:PRAGMA rekey的源码与三条纪律

密码不是设完就一成不变的。换密码的标准做法是用旧密码连接后执行PRAGMA rekey,这个语法从SQLite加密扩展里沿用下来,System.Data.SQLite直接透传。

using System; using System.Data.SQLite; class RekeyDemo { static void Main() { // 用旧密码连接 string oldStr = "Data Source=secure.db;Version=3;Password=mySecret123;Pooling=False;"; using (SQLiteConnection conn = new SQLiteConnection(oldStr)) { conn.Open(); // 改成新密码 using (SQLiteCommand cmd = new SQLiteCommand( "PRAGMA rekey = 'newSecret456';", conn)) { cmd.ExecuteNonQuery(); } // 如果要移除密码,执行: PRAGMA rekey = NULL; } // 改完密码后旧连接池里的连接不能复用,必须清空 SQLiteConnection.ClearAllPools(); Console.WriteLine("密码已修改"); } }

逻辑说明:rekey语句执行时,SQLite会用旧密钥读出整个库内容,再用新密钥重新加密并写回,这个过程中原始文件会先被覆盖成临时文件再替换。ClearAllPools是所有连接池场景的后悔药:连接池里的连接还持有旧密钥上下文,不清池的话,下一次连接可能拿到旧加密状态的连接,导致新密码验证失败。

这里有三条纪律最好记住。第一条,rekey之前先做一次文件级备份,万一半途断电或程序崩溃,数据库文件可能处于新旧密钥交替的中间状态。第二条,rekey执行期间别开第二个连接,SQLite虽然支持多连接读,但写操作期间额外的读连接会拿到不一致的页缓存。第三条,改密后立即清池,把ClearAllPools放在事务提交和外层using结束之间,别放到程序退出前。

4.4 用DB Browser打开带密码库的正确方式

很多人设置完密码,习惯性用DB Browser for SQLite打开看一眼,结果弹了个“file is not a database”,第一反应是源码写坏了。这里要分情况。DB Browser for SQLite的普通版本不支持加密库,它不认密码,直接按明文格式去解析文件头,所以报错是必然的。但DB Browser官方有一个内建SQLCipher分支的发布版本,打开文件时会弹出密码输入框,输入正确密码后能正常浏览表结构和数据。

我一般这样区分:如果用的是DB Browser官方普通版,看到“file is not a database”不要慌,这不是文件损坏,换个支持加密的版本就能打开。如果不想换工具,也可以用回System.Data.SQLite,在C#里把需要的数据导出成明文CSV,再用DB Browser导入。注意这一步导出时连接串必须带正确密码,否则导出的内容全是乱码或直接报异常。

5. 64位调用与密码设置的5个避坑记录:现象、原因、解决

这套方案我前后维护过好几轮,最容易反复踩的坑就集中在这五个点上。按“现象 → 原因 → 解决”三段写,方便你对照排查。

5.1 x64编译后仍然报BadImageFormatException

现象:工程明明选了x64,运行到new SQLiteConnection就抛BadImageFormatException,提示“试图加载格式不正确的程序集”。

原因:引用的是托管DLL System.Data.SQLite.dll,但它没有Copy Local到输出目录;或者输出目录里的SQLite.Interop.dll是从x86子目录拷过来的。更隐蔽的一种情况是,csproj里PlatformTarget确实写成了x64,但App.config的probing里同时写了“x64;x86”,运行库按进程位数选了x64子目录,结果那个子目录里是旧的32位DLL。

解决:先把输出目录里的DLL全部删掉,从官方发布包重新拷贝,确认x64子目录里的SQLite.Interop.dll文件大小和哈希跟官方一致;再打开csproj检查PlatformTarget=x64;最后在Main开头输出Environment.Is64BitProcess确认。三板斧排查完,90%以上的BadImageFormatException都能解决。

5.2 连接串写了Password,数据库却没有加密

现象:照源码设置密码后,用文本编辑器打开db文件,还能看到表名和字段名的明文;或者连接时直接抛“not compiled with encryption support”。

原因:当前引用的System.Data.SQLite.dll是普通编译版本,没有启用SQLITE_HAS_CODEC。Password参数被代码接收了,但原生层根本没有加密模块去执行它,于是要么忽略、要么作为不支持的选项抛异常。

解决:换带加密扩展的DLL版本。判断方法很直接,用文本搜索工具打开SQLite.Interop.dll所在目录的发布说明,或者直接给技术支持发邮件询问是否支持SQLITE_HAS_CODEC。如果找不到合适版本,退一步改用SQLCipher的.NET绑定,它内置加密,不依赖额外的CODEC宏。

5.3 PRAGMA rekey执行时报“file is encrypted or is not a database”

现象:连接串带了旧密码,Open成功,但执行PRAGMA rekey时抛SQLiteException,错误信息是“file is encrypted or is not a database”。

原因:Open成功不代表密码正确。System.Data.SQLite在Open阶段不会立刻做密码校验,密码校验发生在第一条SQL语句执行时。rekey本身也是SQL语句,所以它成了第一条触发校验的命令。如果旧密码不对,rekey自然失败。另一种情况是文件本身不是加密库,rekey语法在非加密扩展下不被识别。

解决:先写一条轻量SQL做密码探活,比如“SELECT count(*) FROM sqlite_master”,能通过再执行rekey。这条命令执行成功后,才能确认当前连接处在正确的解密状态。如果探活就失败,说明密码或DLL加密扩展有问题,按5.2的方式处理。

5.4 发布目录DLL混用引发AccessViolationException

现象:程序在开发机上跑得好好的,拷到另一台64位机器上,偶发“AccessViolationException”,代码里明明是纯托管调用,错误码是c0000005。

原因:System.Data.SQLite的托管DLL和原生DLL版本不匹配。比如System.Data.SQLite.dll是较新版本,而SQLite.Interop.dll是从另一个源码包里拷来的旧版本;或者x64子目录里的原生DLL文件损坏、被杀毒软件隔离了一半。原生层是C++代码,版本不匹配不一定会抛托管异常,而是直接访问非法内存地址,表现为c0000005。

解决:发布时用文件夹级校验,把整个发布目录的DLL文件列表快照保存下来,换机器后比对文件名和大小。我一般会在发布脚本里加一段哈希校验:对System.Data.SQLite.dll和x64\SQLite.Interop.dll各算一次SHA256,输出到文本文件,部署后一条PowerShell命令diff结果,不一致就重新拷贝。

Get-FileHash System.Data.SQLite.dll, x64\SQLite.Interop.dll -Algorithm SHA256 | Format-List

这条命令的作用是把两个关键DLL的SHA256算出来,部署现场执行后和发布快照比对。逻辑其实很简单:同版本的官方包哈希一定相同,不同版本哪怕差一个字节哈希都不同,这是排查“开发机正常、现场崩溃”最快的办法。

5.5 密码正确但多线程连接读不到数据

现象:一个线程用新密码创建了连接并写入数据,另一个线程用同样密码新开连接去读,结果抛异常或者读不到刚提交的数据。更诡异的是重启程序后数据又正常了。

原因:两个连接各自有一份页缓存,Cache参数没设成Shared。写入连接提交后数据还在它自己的缓存里,读取连接的缓存里没有这些页,又因为数据库文件被写入连接占用,无法回读文件页,于是表现为数据“丢失”。连接池也会放大这个问题,池里的连接持有的是修改前的数据库状态。

解决:连接串统一加上Cache=Shared,多线程写入时用事务串行化。另外在代码里写一个统一的连接串常量,不要散落各处手写,避免一个线程加了Shared、另一个线程没加。若改密码后出现这个问题,在改密操作后面调用SQLiteConnection.ClearAllPools(),把持有旧状态的连接全部丢弃。

6. 收尾技巧:密码快速校验、备份验证与绿色发布

密码功能做完,至少要回答两个问题:数据库备份出来还能不能用?改密之后的连接串到底对不对?最后一章给一个可复制的验证方法和一套绿色发布清单。

密码快速校验用一个探活函数。故意用错误密码连接,能执行SELECT说明密码正确,抛异常则说明密码不对或文件损坏:

static bool VerifyPassword(string dbFile, string password) { // Pooling=False 保证不会复用旧连接,探测结果反映真实状态 string connStr = string.Format( "Data Source={0};Version=3;Password={1};Pooling=False;", dbFile, password); try { using (SQLiteConnection conn = new SQLiteConnection(connStr)) { conn.Open(); using (SQLiteCommand cmd = new SQLiteCommand( "SELECT count(*) FROM sqlite_master;", conn)) { cmd.ExecuteScalar(); } } return true; } catch (SQLiteException ex) { Console.WriteLine("密码错误或文件损坏: " + ex.Message); return false; } }

逻辑说明:sqlite_master是SQLite内部的系统表,任何库都存在,用count(*)探测既轻量又能触发密码校验,比自定义表名更安全。Pooling=False是这里的关键参数,验证动作不能受连接池影响,否则上一次连接残留的上下文可能让密码错误也蒙混过关。调用前把数据库文件先复制一份到临时目录,验证通过后再覆盖正式备份,这套流程保证备份里永远不会混入半加密状态的废文件。

绿色发布是我在这个项目里养成的习惯:整个程序做成一个目录,不依赖安装程序、不写注册表。目录结构这样摆:

文件/目录作用
YourApp.exe主程序,x64编译
YourApp.exe.configApp.config编译产物,含probing路径
System.Data.SQLite.dll托管ADO.NET程序集
x64\SQLite.Interop.dll64位原生SQLite库
x86\SQLite.Interop.dll32位原生库,保留备用

这套结构拷贝到任意Windows 64位机器上就能跑。发布前我还会把VerifyPassword集成到一个隐藏命令行参数里,现场维护时用“YourApp.exe --verifydb secure.db”直接验证数据库密码是否正常,不用再开调试器。

最后说一下这套源码方案值不值得做。如果你的业务是单机数据落盘、多线程并发读多写少,System.Data.SQLite加密码连接的方案已经足够,性能开销主要来自加密计算,单机数据量在百万行以内体感不明显。我最早在这套方案上翻车,就是下载源码后没检查SQLite.Interop.dll的位数,在发布现场折腾了半个下午。后来我把DLL哈希校验写进发布脚本,这个问题再没出现过。密码机制不复杂,复杂的是配套的验证习惯,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询