一、前期回顾
前两篇文章,使用SQLite数据库加密敏感信息并嵌入程序(1)主要介绍了使用工具和原生函数生成SQLite数据库,使用SQLite数据库加密敏感信息并嵌入程序(2)主要图解了将生成的数据库作为资源嵌入到程序中使用,由于该数据库保存了敏感信息,我们的目标是在程序运行时在内存中读取相关数据,而无需使用将资源保存到临时文件再获取信息的方案。
二、SQLite跨平台利器VFS简介
SQLite可移植到不同平台,在各操作系统上都能很好且高效一致的工作的主要原因是在底层与操作系统的交互上定义了一个虚拟文件系统,即VFS(Virtual File System)对象:操作系统接口对象,这是 SQLite 可跨操作系统移植的主要原因。
每当 SQLite 中的任何其他模块需要与操作系统通信时,它们都会调用 VFS 中的方法。VFS 然后调用满足请求所需的特定于操作的代码。因此,将 SQLite 移植到新的操作系统只需编写一个新的操作系统接口层或 “VFS” 即可。
可以同时注册多个 VFS,只需保证每个 VFS 都有一个唯一的名称即可。同一进程中的单独数据库连接可以同时使用不同的 VFS。Windows 版本带有多个内置 VFS。默认的 Windows VFS 称为“win32”,适用于大多数应用程序。图1显示了 SQLite 源码中定义的 sqlite3_vfs 对象结构。
![图 1 sqlite3_vfs 结构](https://img-blog.csdnimg.cn/551c8098967e418bbf9a2ed7c23f581a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZC06K-05omS6YGT,size_20,color_FFFFFF,t_70,g_se,x_16)
要实现一个标准的 VFS 可通过子类化三个对象来实现,三个对象分别是 sqlite3_vfs(操作系统接口对象)、sqlite3_io_methods(操作系统接口文件虚拟方法对象)、sqlite3_file(操作系统文件对象)。
其中子类化 sqlite3_vfs 和 sqlite3_io_methods 就是实现两个对象的方法,可选择实现部分或所有方法。
sqlite3_file 对象代表一个打开的文件 。sqlite3_vfs 的 xOpen 方法 在打开文件时构造一个 sqlite3_file 对象,sqlite3_file 跟踪文件打开时的状态 ,它包含了一个指向 sqlite3_io_methods 对象的指针,该 sqlite3_io_methods 对象指针仅适用于当前 sqlite3_file 对象,它执行诸如从文件读取和写入、截断文件、刷新对持久存储的任何更改、查找文件大小、锁定和解锁文件以及关闭文件和最终销毁 sqlite3_file 对象的相关操作。
新构造的 sqlite3_vfs 使用 sqlite3_vfs_register 函数注册,其他两个对象无需注册,新构造的 sqlite3_file 对象将从 sqlite3_vfs 的 xOpen 方法返回,并且 sqlite3_file 对象指向一个新构造的 sqlite3_io_methods 对象的一个实例。
图2显示了 sqlite3_io_methods 结构,图3显示 sqlite3_file 结构。
![](https://img-blog.csdnimg.cn/5fb4519d296c4fa3976d0900a9d95c54.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZC06K-05omS6YGT,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/38e4d27ec6e4411c894d9686c2e951c0.png)
三、TBNSQLiteFile 和TBNSQLiteVfs结构(Delphi版)
1、TBNSQLitFile 结构
TBNSQLitFile 结构为 sqlite3_file 的子类化实现接口,与原生 sqlite3_file 接口相比增加了一些额外的字段,如图4所示。
![](https://img-blog.csdnimg.cn/8f91220991004dbebb9d63330fb660a4.png)
各字段详解如下:
1、pBaseSQLiteFile : 指向原生 sqlite3_file 接口的指针,主要用于设置在子类化的 sqlite3_vfs 实现类中由方法 xOpen 打开并设置一个有效的 sqlite3_io_methods 对象,因为无论 sqlite3_vfs.xOpen方法调用成功与否,SQLite都要求此方法应返回一个有效的的 sqlite3_io_methods对象。
2、pBaseSQLiteFilePath :指向数据库实际路径,可以含扩展名。由于本案例中使用的是在内存加载的数据库文件资源,实际文件名和路径均无意义,所以可以传递任意字符,字符长度限定在Windows平台的默认值,即1040个字符即可。
3、pDataBuffer :指向在嵌入的资源中获取的数据库文件实际数据的指针。
4、dwFlag :访问标志:只读、读写等标志,SQLite还会在由 sqlite3_vfs.xOpen 方法打开的数据库文件时额外添加一些标志,如SQLITE_OPEN_MAIN_DB 等标志。
5、dwSize :数据库文件实际大小。
2、TBNSQLiteVfs 结构
TBNSQLiteVfs 为自定义的 sqlite3_vfs 结构。如图5所示。
![](https://img-blog.csdnimg.cn/f9b975bc28b84bad8d84c32511726996.png)
SQLite 中操作系统接口对象也就是 sqlite3_vfs 是一个链表结构,此结构的两个字段分别表示当前使用的 sqlite3_vfs 和父 sqlite3_vfs,pParentVfs 字段值由 sqlite3_vfs_find 函数查找返回后设置,等效于设置了 sqlite3_vfs 的 pNext 字段值,这样就构成了一个完整的链表结构。
四、TBNSQLitIoMethodUtils 实现类(Delphi版)
TBNSQLitIoMethodUtils 类作为 sqlite3_io_methods 对象的子类化实现类在本案例中主要实现最关键的方法是xRead、xFileSize方法。由于本案例中主要是读取作为资源嵌入到程序中的SQLite数据库,操作为只读性质,所以忽略 xWrite 等方法的具体实现,返回值为 SQLITE_OK 即可。
为方便起见,类的所有的方法均为静态方法,由于SQLite原生函数调用都是全局静态方法,如实现为实例方法,函数地址的转换可能为带来潜在的未知结果。下面如图6-图9所示为 TBNSQLitIoMethodUtils 类的所有实现代码。
有关各方法的功能及详细情况请查阅 sqlite 官网,在此就不一一列出。
![](https://img-blog.csdnimg.cn/e1e71d4d58324c6fb070bbc727d0ddf3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZC06K-05omS6YGT,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/9ae45e3c71144004b7c4cbe8a0b27519.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZC06K-05omS6YGT,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/96b552de25ff48028b556995984c8faa.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5ZC06K-05omS6YGT,size_20,color_FFFFFF,t_70,g_se,x_16)
![](https://img-blog.csdnimg.cn/cb55b116099047d880c85f19f7d143e3.png)
五、结语
由于篇幅较长,下一篇我们再来看看 sqlite3_vfs 的子类化实现类 TBNSQLiteVfsUtils 如何实现。