『开源重编译』System.Data.SQLite.dll 自适应 x86 x64 AnyCPU 重编译
2017-10-01 23:31
447 查看
背景:
> 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(*) 函数调用。
> 修改的代码如下:
> __RecoverHelper.cs 源码如下:
最后使用SQLite官方测试工具测试:
> 测试通过 如下图:
相关源码 和 程序集 下载 (使用的是官方密钥签名):
> 点击下载源码 和 已签名程序集 (如果本文对你有所帮助,麻烦点击一下右下角的 “推荐”,谢谢)
> 测试时 请直接去 Bin 目录, 运行官方 测试工具 test.exe 和 test32.exe
> 默认将 C++ 程序集 释放到 x86 x64 文件夹,如果想设置为:将C++程序集释放到当前目录,可以在 App.config 中 增加如下配置:
> 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(*) 函数调用。
> 修改的代码如下:
private static bool PreLoadSQLiteDll( string baseDirectory, /* in */ string processorArchitecture, /* in */ ref string nativeModuleFileName, /* out */ ref IntPtr nativeModuleHandle /* out */ ) { //新增的代码 __RecoverHelper.InitResourceSQLiteInteropAssembly(); // // NOTE: If the specified base directory is null, use the default // (i.e. attempt to automatically detect it). // if (baseDirectory == null) baseDirectory = GetBaseDirectory(); //..... }
> __RecoverHelper.cs 源码如下:
using System.Configuration; using System.IO; using System.IO.Compression; using System.Reflection; using System.Security.Cryptography; using System.Text; namespace System.Data.SQLite { /// <summary> /// <para>修复 System.Data.SQLite 所需要的 运行环境.</para> /// <para>释放 System.Data.SQLite 需要的C++程序集 SQLite.Interop.dll</para> /// </summary> internal static class __RecoverHelper { #region 尝试加载内嵌程序集 internal const string SQLite_Interop = @"SQLite.Interop"; internal const string SQLite_Interop_x64MD5 = @"7d40719ca6d7c1622fa54d2f17a97020"; internal const string SQLite_Interop_x86MD5 = @"bfd7e42cd1638debe255771057699574"; /// <summary> /// 是否将 SQLite.Interop.dll 释放到 x64 x86 文件夹, 如果本参数为否 则释放到 当前目录. /// </summary> internal static bool InteropPlatformFolder { get { string value = (ConfigurationManager.AppSettings["System.Data.SQLite:InteropPlatformFolder"] ?? string.Empty).Trim().ToUpper(); return (string.IsNullOrEmpty(value) || value == "TRUE" || value == "1" || value == "T"); } } /// <summary> /// 当运行环境 找不到 SQLite.Interop.dll 程序集时, 尝试将 内嵌字节 写回磁盘 还原成原始文件 SQLite.Interop.dll /// </summary> internal static void InitResourceSQLiteInteropAssembly() { try { //当运行环境 找不到 SQLite.Interop.dll 程序集时, 尝试将 内嵌字节 写回磁盘 还原成原始文件 SQLite.Interop.dll Assembly assembly = Assembly.GetExecutingAssembly(); string domainFolder = AppDomain.CurrentDomain.BaseDirectory; bool is64Proc = (IntPtr.Size == 8); string interopFolder = string.Format(@"{0}\{1}", domainFolder.TrimEnd('/', '\\'), (InteropPlatformFolder ? (is64Proc ? @"x64\" : @"\x86\") : string.Empty)); string SQLiteInteropDllPath = string.Format(@"{0}\{1}.dll", interopFolder.TrimEnd('/', '\\'), SQLite_Interop); if (!Directory.Exists(interopFolder)) Directory.CreateDirectory(interopFolder); //如果磁盘 SQLite.Interop.dll 存在, 则校验相关 MD5 if (File.Exists(SQLiteInteropDllPath)) { string existFileMD5 = GetFileMD5(SQLiteInteropDllPath); string rightFileMD5 = is64Proc ? SQLite_Interop_x64MD5 : SQLite_Interop_x86MD5; //如果MD5 不一致, 则删除磁盘现有的 SQLite.Interop.dll if (!string.Equals(existFileMD5, rightFileMD5, StringComparison.CurrentCultureIgnoreCase)) File.Delete(SQLiteInteropDllPath); } //如果磁盘 SQLite.Interop.dll 不存在, 则释放 SQLite.Interop.dll if (!File.Exists(SQLiteInteropDllPath)) { string libResourceName = string.Format("{0}.Lib.{1}.x{2}.GZip.dll", Assembly.GetExecutingAssembly().GetName().Name, SQLite_Interop, (is64Proc ? "64" : "86")); Stream stream = assembly.GetManifestResourceStream(libResourceName); if (stream == null) return; try { using (stream) { using (Stream zipStream = (Stream)new GZipStream(stream, CompressionMode.Decompress)) { using (FileStream myFs = new FileStream(SQLiteInteropDllPath, FileMode.Create, FileAccess.ReadWrite)) { //从压缩流中读出所有数据 byte[] buffer = new byte[1024]; do { int n = zipStream.Read(buffer, 0, buffer.Length); if (n <= 0) break; myFs.Write(buffer, 0, n); } while (true); zipStream.Close(); } } } } catch (Exception) { } } } catch(Exception) { } } #endregion #region 辅助函数 /// <summary> /// 计算文件的MD5, 计算错误将返回 空字符串 /// </summary> public static string GetFileMD5(string path) { if (!File.Exists(path)) return string.Empty; try { using (FileStream myFs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { using (MD5 md5 = new MD5CryptoServiceProvider()) { byte[] hash = md5.ComputeHash(myFs); myFs.Close(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < hash.Length; i++) sb.Append(hash[i].ToString("x2")); return sb.ToString(); } } } catch (Exception) { return string.Empty; } } /// <summary> /// 计算指定文件 从指定字节开始 的 指定长度的 文件的MD5 (剩余字节不足, 则只计算剩余字节流), 计算错误将返回 空字符串 /// </summary> public static string GetFileMD5(string path, long offset, long length) { if (!File.Exists(path)) return string.Empty; try { const int PACKAGE_SIZE = 1024 * 1024; //每次1M using (MD5 md5 = new MD5CryptoServiceProvider()) { md5.Initialize(); using (FileStream myFs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { myFs.Position = offset; long fileByteLength = Math.Min(length, myFs.Length - myFs.Position); byte[] buffer = new byte[PACKAGE_SIZE]; long readLength = 0; while (readLength < fileByteLength) { long leaveLength = myFs.Length - myFs.Position; long leaveLength2 = fileByteLength - readLength; int bufferLength = (leaveLength > (long)PACKAGE_SIZE) ? PACKAGE_SIZE : Convert.ToInt32(leaveLength); bufferLength = (leaveLength2 > (long)bufferLength) ? bufferLength : Convert.ToInt32(leaveLength2); myFs.Read(buffer, 0, bufferLength); if (readLength + bufferLength < fileByteLength) //不是最后一块 md5.TransformBlock(buffer, 0, bufferLength, buffer, 0); else //最后一块 md5.TransformFinalBlock(buffer, 0, bufferLength); readLength = readLength + bufferLength; if (myFs.Position >= myFs.Length) break; } byte[] hash = md5.Hash; StringBuilder sb = new StringBuilder(); for (int i = 0; i < hash.Length; i++) sb.Append(hash[i].ToString("x2")); return sb.ToString(); } } } catch (Exception) { return string.Empty; } } #endregion } }
最后使用SQLite官方测试工具测试:
> 测试通过 如下图:
相关源码 和 程序集 下载 (使用的是官方密钥签名):
> 点击下载源码 和 已签名程序集 (如果本文对你有所帮助,麻烦点击一下右下角的 “推荐”,谢谢)
> 测试时 请直接去 Bin 目录, 运行官方 测试工具 test.exe 和 test32.exe
> 默认将 C++ 程序集 释放到 x86 x64 文件夹,如果想设置为:将C++程序集释放到当前目录,可以在 App.config 中 增加如下配置:
<configuration> <appSettings> <add key="System.Data.SQLite:InteropPlatformFolder" value="0"/> </appSettings> </configuration>
相关文章推荐
- System.Data.SQLite.dll不能编译成AnyCPU问题的解决方案,以及它跨x86和x64的使用方法。
- Could not load file or assembly'System.Data.SQLite.dll' or one of its depedencies
- .net 编译时选择anycpu x86 x64的区别: 在 64 位 Windows 操作系统上: 用 x86 编译的程序集将在 WOW64 下运行的 32 位 CLR 上执行。 用 x64 编译
- Could not load file or assembly system.data.sqlite.dll or one for it's depedencies
- Android Studio 通过JNA调用 Clang编译的so库,以及Java JNA 调用x64dll,C# dllimport调用x86dll
- C# System.Data.Sqlite.dll 的注意事项
- [解决]System.DllNotFoundException: 无法加载 DLL“SQLite.Interop.DLL” -- 绿化System.Data.SQLite.dll
- [解决]System.DllNotFoundException: 无法加载 DLL“SQLite.Interop.DLL” -- 绿化System.Data.SQLite.dll
- VS x86 x64 anycpu 编译运行对照表
- C# System.Data.Sqlite.dll 的注意事项
- Unity Mono.Data.Sqlite.dll System.Data.dll路径问题
- Could not load file or assembly'System.Data.SQLite.dll' or one of its depedencies
- SQLite.Interop.DLL与System.Data.SQLite.dll比较
- “System.BadImageFormatException”类型的未经处理的异常在 PurchaseDevices.Access.dll 中发生 其他信息: 未能加载文件或程序集“System.Data.SQLite, Version=1.0.66.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139”或它的某一个依赖项。试图加载格式不正确
- winform调用sqlite,打包发布后,在客户机上安装运行却报错:找不到System.Data.SQLite.DLL
- C#2010 在使用 System.Data.SQLite.dll 时出现异常
- system.data.sqlite.dll
- 未能加载文件或程序集“System.Data.SQLite.DLL”或它的某一个依赖项
- 未能加载文件或程序集“System.Data.SQLite.DLL”或它的某一个依赖项
- windows xp 不能load "System.Data.SQLite.dll"