zoukankan      html  css  js  c++  java
  • 『开源重编译』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>

  • 相关阅读:
    Ubuntu adb devices :???????????? no permissions (verify udev rules) 解决方法
    ubuntu 关闭显示器的命令
    ubuntu android studio kvm
    ubuntu 14.04版本更改文件夹背景色为草绿色
    ubuntu 创建桌面快捷方式
    Ubuntu 如何更改用户密码
    ubuntu 14.04 返回到经典桌面方法
    ubuntu 信使(iptux) 创建桌面快捷方式
    Eclipse failed to get the required ADT version number from the sdk
    Eclipse '<>' operator is not allowed for source level below 1.7
  • 原文地址:https://www.cnblogs.com/shuxiaolong/p/System_Data_SQLite_AnyCPU.html
Copyright © 2011-2022 走看看