『开源重编译』System.Data.SQLite.dll 自适应 x86 x64 AnyCPU 重编译

背景:

> System.Data.SQLite.dll 程序集 不能良好的支持 AngCPU 格式

System.Data.SQLite.dll 在 适应 x86 和 x64 有三个方案:

> 分别使用 32 或 64 的 混合编译程序集(程序如果以64位 运行,但引用32位的 程序集 就会报错,反之) —— 所以这种方案 很惹人嫌。

> 使用 AnyCPU 的程序集 —— 但是 你得间接引用 C++ 核心程序集:SQLite.Interop.dll —— 即:你得 同时引用 两个程序集:System.Data.SQLite.dll  和 SQLite.Interop.dll

> 第三种基于第二种:运行 SQLite 官方安装文件,将 SQLite.Interop.dll 程序集 自动安装到 系统目录 —— 你调用 AnyCPU版本的 System.Data.SQLite.dll 时,程序会自动去系统目录找对应的 C++程序集。

(但这种方案 不支持 免安装运行 —— 你本地编译运行正常,复制到 别的电脑上 就崩溃了(目标电脑也得运行 SQLite 官方安装文件))

纠结就产生了:

> 我只想引用 一个程序集。

> 这个程序集 是 AnyCPU  (自动适应 x64 x86)。

> 我编译好的程序,发给对方,对方就能直接双击运行。

—— 上面三种方案:基本都无法 完美解决这个问题。

网上搜索到既有的方案:

http://download.csdn.net/download/yhbcpg/8441051

下载之后:

但是 又有了问题:

> 这位仁兄 从 SQLite官网 下载代码改造 —— 最后使用了 代码混淆(当然,增加了 几百行代码,保护自己的代码 —— 无可厚非)。

> 我挺不喜欢 10940_x64 和 10940_86 这两个文件夹的 —— 想基于这个作者修改源码 似乎有点难,算了:干脆自己实现一个了。

可行性评估尝试:

> 我从 SQLite官网 下载了 最新的 1.0.105.2 的 源码。

> 编译之后,手动创建了 x64 x86 目录。

> 程序正常运行 —— 换言之:SQLite 官方 已经提供了对 x64 x86 文件夹的识别

> 于是,调整目标:

  > 增加自释放功能。

  > 释放到当前程序目录:不使用 x86 和 x64 目录

  > 释放到对应的平台目录:使用 x86 和 x64 目录

  > 每次运行时 校验C++程序集是否 适应当前程序平台,是否需要重新释放。

于是,System.Data.SQLite.dll 自适应AnyCPU 自释放 程序集改写 开始:

> 将 x86 和 x64 的 C++程序集 进行 GZip压缩,节省 程序集字节大小,并内嵌到项目中:

> 在阅读 SQLite 1.0.105.2 官方源码 过程中,我在源码 UnsafeNativeMethods.cs 中发现了 一个函数: PreLoadSQLiteDll(*)。

> 增加了一个 释放C++程序集的 辅助类 __RecoverHelper.cs,直接由 PreLoadSQLiteDll(*) 函数调用。

> 修改的代码如下:

 1       private static bool PreLoadSQLiteDll(
 2           string baseDirectory,            /* in */
 3           string processorArchitecture,    /* in */
 4           ref string nativeModuleFileName, /* out */
 5           ref IntPtr nativeModuleHandle    /* out */
 6           )
 7       {
 8           //新增的代码
 9           __RecoverHelper.InitResourceSQLiteInteropAssembly();
10
11           //
12           // NOTE: If the specified base directory is null, use the default
13           //       (i.e. attempt to automatically detect it).
14           //
15           if (baseDirectory == null)
16               baseDirectory = GetBaseDirectory();
17
18           //.....
19       }    

> __RecoverHelper.cs 源码如下:

  1 using System.Configuration;
  2 using System.IO;
  3 using System.IO.Compression;
  4 using System.Reflection;
  5 using System.Security.Cryptography;
  6 using System.Text;
  7
  8 namespace System.Data.SQLite
  9 {
 10     /// <summary>
 11     /// <para>修复 System.Data.SQLite 所需要的 运行环境.</para>
 12     /// <para>释放 System.Data.SQLite 需要的C++程序集 SQLite.Interop.dll</para>
 13     /// </summary>
 14     internal static class __RecoverHelper
 15     {
 16         #region  尝试加载内嵌程序集
 17
 18         internal const string SQLite_Interop = @"SQLite.Interop";
 19         internal const string SQLite_Interop_x64MD5 = @"7d40719ca6d7c1622fa54d2f17a97020";
 20         internal const string SQLite_Interop_x86MD5 = @"bfd7e42cd1638debe255771057699574";
 21
 22
 23         /// <summary>
 24         /// 是否将 SQLite.Interop.dll 释放到 x64 x86 文件夹, 如果本参数为否 则释放到 当前目录.
 25         /// </summary>
 26         internal static bool InteropPlatformFolder
 27         {
 28             get
 29             {
 30                 string value = (ConfigurationManager.AppSettings["System.Data.SQLite:InteropPlatformFolder"] ?? string.Empty).Trim().ToUpper();
 31                 return (string.IsNullOrEmpty(value) || value == "TRUE" || value == "1" || value == "T");
 32             }
 33         }
 34
 35
 36
 37         /// <summary>
 38         /// 当运行环境 找不到 SQLite.Interop.dll 程序集时, 尝试将 内嵌字节 写回磁盘 还原成原始文件 SQLite.Interop.dll
 39         /// </summary>
 40         internal static void InitResourceSQLiteInteropAssembly()
 41         {
 42             try
 43             {
 44                 //当运行环境 找不到 SQLite.Interop.dll 程序集时, 尝试将 内嵌字节 写回磁盘 还原成原始文件 SQLite.Interop.dll
 45
 46                 Assembly assembly = Assembly.GetExecutingAssembly();
 47                 string domainFolder = AppDomain.CurrentDomain.BaseDirectory;
 48                 bool is64Proc = (IntPtr.Size == 8);
 49                 string interopFolder = string.Format(@"{0}\{1}", domainFolder.TrimEnd(‘/‘, ‘\\‘), (InteropPlatformFolder ? (is64Proc ? @"x64\" : @"\x86\") : string.Empty));
 50                 string SQLiteInteropDllPath = string.Format(@"{0}\{1}.dll", interopFolder.TrimEnd(‘/‘, ‘\\‘), SQLite_Interop);
 51                 if (!Directory.Exists(interopFolder)) Directory.CreateDirectory(interopFolder);
 52
 53                 //如果磁盘 SQLite.Interop.dll 存在, 则校验相关 MD5
 54                 if (File.Exists(SQLiteInteropDllPath))
 55                 {
 56                     string existFileMD5 = GetFileMD5(SQLiteInteropDllPath);
 57                     string rightFileMD5 = is64Proc ? SQLite_Interop_x64MD5 : SQLite_Interop_x86MD5;
 58
 59                     //如果MD5 不一致, 则删除磁盘现有的 SQLite.Interop.dll
 60                     if (!string.Equals(existFileMD5, rightFileMD5, StringComparison.CurrentCultureIgnoreCase))
 61                         File.Delete(SQLiteInteropDllPath);
 62                 }
 63
 64
 65                 //如果磁盘 SQLite.Interop.dll 不存在, 则释放 SQLite.Interop.dll
 66                 if (!File.Exists(SQLiteInteropDllPath))
 67                 {
 68                     string libResourceName = string.Format("{0}.Lib.{1}.x{2}.GZip.dll", Assembly.GetExecutingAssembly().GetName().Name, SQLite_Interop, (is64Proc ? "64" : "86"));
 69                     Stream stream = assembly.GetManifestResourceStream(libResourceName);
 70                     if (stream == null) return;
 71
 72                     try
 73                     {
 74                         using (stream)
 75                         {
 76                             using (Stream zipStream = (Stream)new GZipStream(stream, CompressionMode.Decompress))
 77                             {
 78                                 using (FileStream myFs = new FileStream(SQLiteInteropDllPath, FileMode.Create, FileAccess.ReadWrite))
 79                                 {
 80                                     //从压缩流中读出所有数据
 81                                     byte[] buffer = new byte[1024];
 82                                     do
 83                                     {
 84                                         int n = zipStream.Read(buffer, 0, buffer.Length);
 85                                         if (n <= 0) break;
 86                                         myFs.Write(buffer, 0, n);
 87                                     } while (true);
 88
 89                                     zipStream.Close();
 90                                 }
 91                             }
 92                         }
 93                     }
 94                     catch (Exception) { }
 95                 }
 96             }
 97             catch(Exception) { }
 98         }
 99
100
101         #endregion
102
103         #region  辅助函数
104
105         /// <summary>
106         /// 计算文件的MD5, 计算错误将返回 空字符串
107         /// </summary>
108         public static string GetFileMD5(string path)
109         {
110             if (!File.Exists(path)) return string.Empty;
111
112             try
113             {
114                 using (FileStream myFs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
115                 {
116                     using (MD5 md5 = new MD5CryptoServiceProvider())
117                     {
118                         byte[] hash = md5.ComputeHash(myFs);
119                         myFs.Close();
120
121                         StringBuilder sb = new StringBuilder();
122                         for (int i = 0; i < hash.Length; i++) sb.Append(hash[i].ToString("x2"));
123                         return sb.ToString();
124                     }
125                 }
126             }
127             catch (Exception)
128             {
129                 return string.Empty;
130             }
131         }
132         /// <summary>
133         /// 计算指定文件 从指定字节开始 的 指定长度的 文件的MD5 (剩余字节不足, 则只计算剩余字节流), 计算错误将返回 空字符串
134         /// </summary>
135         public static string GetFileMD5(string path, long offset, long length)
136         {
137             if (!File.Exists(path)) return string.Empty;
138
139             try
140             {
141                 const int PACKAGE_SIZE = 1024 * 1024; //每次1M
142
143                 using (MD5 md5 = new MD5CryptoServiceProvider())
144                 {
145                     md5.Initialize();
146
147                     using (FileStream myFs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read))
148                     {
149                         myFs.Position = offset;
150                         long fileByteLength = Math.Min(length, myFs.Length - myFs.Position);
151                         byte[] buffer = new byte[PACKAGE_SIZE];
152
153                         long readLength = 0;
154                         while (readLength < fileByteLength)
155                         {
156                             long leaveLength = myFs.Length - myFs.Position;
157                             long leaveLength2 = fileByteLength - readLength;
158                             int bufferLength = (leaveLength > (long)PACKAGE_SIZE) ? PACKAGE_SIZE : Convert.ToInt32(leaveLength);
159                             bufferLength = (leaveLength2 > (long)bufferLength) ? bufferLength : Convert.ToInt32(leaveLength2);
160
161                             myFs.Read(buffer, 0, bufferLength);
162
163                             if (readLength + bufferLength < fileByteLength)     //不是最后一块
164                                 md5.TransformBlock(buffer, 0, bufferLength, buffer, 0);
165                             else                                                //最后一块
166                                 md5.TransformFinalBlock(buffer, 0, bufferLength);
167
168                             readLength = readLength + bufferLength;
169                             if (myFs.Position >= myFs.Length) break;
170                         }
171
172                         byte[] hash = md5.Hash;
173                         StringBuilder sb = new StringBuilder();
174                         for (int i = 0; i < hash.Length; i++) sb.Append(hash[i].ToString("x2"));
175                         return sb.ToString();
176                     }
177                 }
178             }
179             catch (Exception)
180             {
181                 return string.Empty;
182             }
183         }
184
185
186         #endregion
187     }
188 }

最后使用SQLite官方测试工具测试:

> 测试通过 如下图:

相关源码 和 程序集 下载 (使用的是官方密钥签名):

> 点击下载源码 和 已签名程序集  (如果本文对你有所帮助,麻烦点击一下右下角的 “推荐”,谢谢)

> 测试时 请直接去 Bin 目录, 运行官方 测试工具 test.exe 和 test32.exe

> 默认将 C++ 程序集 释放到 x86 x64 文件夹,如果想设置为:将C++程序集释放到当前目录,可以在 App.config 中 增加如下配置: 

1 <configuration>
2   <appSettings>
3     <add key="System.Data.SQLite:InteropPlatformFolder" value="0"/>
4
5   </appSettings>
6 </configuration>

时间: 2024-10-02 19:07:09

『开源重编译』System.Data.SQLite.dll 自适应 x86 x64 AnyCPU 重编译的相关文章

Could not load file or assembly&#39;System.Data.SQLite.dll&#39; or one of its depedencies

[问题]  在我本机的开发环境c#连接sqlite3没有问题,可是release版本移植到其他的机器就提示Could not load file or assembly'System.Data.SQLite.dll' or one of its depedencies.找不到指定模块. [解决] 搜来搜去没找到靠谱的答案,其实最后还是自己解决的. sqlite官方的下载页面里面说了:The Visual C++ 2010 SP1 runtime for x86 and the .NET Fr

IIS中发布后出现Could not load file or assembly&#39;System.Data.SQLite.dll&#39; or one of its depedencies

[问题]在我本机的开发环境c#连接sqlite3没有问题,可是release版本移植到其他的机器就提示Could not load file or assembly'System.Data.SQLite.dll' or one of its depedencies.找不到指定模块. [解决]搜来搜去没找到靠谱的答案,其实最后还是自己解决的. sqlite官方的下载页面里面说了:The Visual C++ 2010 SP1 runtime for x86 and the .NET Framewo

C#使用System.Data.SQLite操作SQLite

使用System.Data.SQLite 下载地址:http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.wiki 得到System.Data.SQLite.dll添加到工程引用: 建表,插入操作 static void Main(string[] args) { SQLiteConnection conn = null; string dbPath = "Data Source =" + Environmen

System.Data.SQLite数据库简介

SQLite介绍 在介绍System.Data.SQLite之前需要介绍一下SQLite,SQLite是一个类似于Access的单机版数据库管理系统,它将所有数据库的定义(包括定义.表.索引和数据本身)都保存在一个单一的文件中.并且,SQLite是一个用C实现的类库,它在内存消耗.文件体积.简单性方面都有不错的表现,如果数据在10W条以下,查询速度也是相当快的.SQLite具有以下特征:实现多数SQL92的标准,包括事务(原子性.一致性.隔离性和持久性).触发器和大多数的复杂查询.不对插入或者更

Win7 64bit系统WinFrom下未能加载文件或程序集“System.Data.SQLite”的解决办法

在Win7 64bit系统下调试程序时出现如下错误: 未能加载文件或程序集”System.Data.SQLite”或它的某一个依赖项.试图加载格式不正确的程序. 事实上System.Data.SQLite.dll控件在默认情况下是32bit的,在64bit系统下得使用另一个控件. 可以到这里http://sourceforge.net/projects/sqlite-dotnet2/files/下载一个安装程序包,安装完后在D:\Program Files (x86)\SQLite.NET\bi

System.Data.SQLite兼容32位和64位问题

SQLite版本分的很详细,本机是64位在32位会出现问题,经过搜索找到解决方案. 这是我以前写的32位在我现在的机子上的运行报错. 类似这样的. 将当前说明文档的目录下的x64.x86目录和System.Data.SQLite.dll文件复制到您的应用程序根目录中(注意更新引用,引用System.Data.SQLite.dll即可,两目录中的不需要引用,但发布时需打包).如果是WEB网站,则复制到Bin目录下即可,发布时,也注意x64和x86一起打包发布 注意,当前的System.Data.S

System.Data.SQLite

SQLite介绍 在介绍System.Data.SQLite之前需要介绍一下SQLite,SQLite是一个类似于Access的单机版数据库管理系统,它将所有数据库的定义(包括定义.表.索引和数据本身)都保存在一个单一的文件中.并且,SQLite是一个用C实现的类库,它在内存消耗.文件体积.简单性方面都有不错的表现,如果数据在10W条以下,查询速度也是相当快的. SQLite具有以下特征: 实现多数SQL92的标准,包括事务(原子性.一致性.隔离性和持久性).触发器和大多数的复杂查询. 不对插入

System.Data.SQLite未能加载文件或程序集

1.简直是作死帝呀.不需要修改dll的名字,否则就坐等悲剧吧 如果项目中有x86和x64的dll,可以建两个不同的文件夹分别存放,但是千万不要修改掉默认的dll的名字 System.Data.SQLite.dll 2.这个dll是区分x86和x64平台的,务必设置项目的目标平台对应到正确的dll

Win7 64位 IIS未能加载文件或程序集“System.Data.SQLite”或它的某一个依赖项

未能加载文件或程序集“System.Data.SQLite”或它的某一个依赖项.试图加载格式不正 解决方案: 1.需要在IIS里设置,启用32位应用程序我用的是iis7 把启用32位应用程序的false改为true 2.下载 System.Data.SQLite.dll 的64位驱动. 地址如下: http://www.sqlite.org/download.html Precompiled Binaries For .NEThttp://system.data.sqlite.org/index