背景:

> 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 == "" || 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 == );
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 ? "" : ""));
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[];
do
{
int n = zipStream.Read(buffer, , buffer.Length);
if (n <= ) break;
myFs.Write(buffer, , 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 = ; 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 = * ; //每次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 = ;
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, , bufferLength); if (readLength + bufferLength < fileByteLength) //不是最后一块
md5.TransformBlock(buffer, , bufferLength, buffer, );
else //最后一块
md5.TransformFinalBlock(buffer, , bufferLength); readLength = readLength + bufferLength;
if (myFs.Position >= myFs.Length) break;
} byte[] hash = md5.Hash;
StringBuilder sb = new StringBuilder();
for (int i = ; 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 自适应 x86 x64 AnyCPU 重编译的更多相关文章

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

    今天在部署code到测试环境的时候 出现了未能加载文件或程序集"System.Data.SQLite.DLL"或它的某一个依赖项 这个错误,其实错误的的原因有很多,1.典型的是是版 ...

  2. 提示“应用程序无法启动,因为应用程序的并行配置不正确”不能加载 System.Data.SQLite.dll

    新版本SQLITE,如果下载Precompiled Binaries版会出现提示“应用程序无法启动,因为应用程序的并行配置不正确”不能加载 System.Data.SQLite.dll. 下载Prec ...

  3. 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. ...

  4. IIS中发布后出现Could not load file or assembly'System.Data.SQLite.dll' or one of its depedencies

    [问题]在我本机的开发环境c#连接sqlite3没有问题,可是release版本移植到其他的机器就提示Could not load file or assembly'System.Data.SQLit ...

  5. system.data.sqlite.dll

    记录下最新的system.data.sqlite.dll下载地址和官网:http://system.data.sqlite.org

  6. System.Data.SQLite.dll控件常规安装方法

    原文地址:http://www.jb51.net/dll/System.Data.SQLite.dll.html 文件运行必须安装   Microsoft Visual C++ 2010 SP1 Re ...

  7. Could not load file or assembly system.data.sqlite.dll or one for it's depedencies

    最近做一个winform项目,因为数据库用的表很少,所以用的是轻量级数据库sqlite.sqlite的优点很多,但是他要分两个版本,32位或者64位,不能同时兼容. 我遇到的问题是,我在开发端用的是. ...

  8. Could not load file or assembly'System.Data.SQLite.dll' or one of its depedencies

    安装对应的 Microsoft Visual C++ 2010 Redistributable Package (x86)   If your download does not start afte ...

  9. 【原创】System.Data.SQLite内存数据库模式

    对于很多嵌入式数据库来说都有对于的内存数据库模式,SQLite也不例外.内存数据库常常用于极速.实时的场景,一个很好的应用的场景是富客户端的缓存数据,一般富客户端的缓存常常需要分为落地和非落地两种,而 ...

随机推荐

  1. 动态规划 POJ3616 Milking Time

    #include <iostream> #include <cstdio> #include <algorithm> using namespace std; st ...

  2. QOpenGLTexture 两个纹理叠加

    如何做纹理混合? 方法是,定义多个QOpenGLTexture,然后在fragment shader中添加相应的变量,然后把texture bind到对应的uniform变量上. 废话不多说 text ...

  3. Blending, Bootstrap

    听林轩田老师的<机器学习技法>,Lecture 7讲到model的blending. 理解了一个之前一直模棱两可的概念:bootstrap. 先说一下什么是blending.在机器学习中, ...

  4. Text Document Analysis CodeForces - 723B

    Modern text editors usually show some information regarding the document being edited. For example, ...

  5. linux之软件安装

    一.软件包管理简介 1)软件包分类 1.源码包 优点: 开源, 如果有足够的能力, 可以修改源代码 可以自由选择所需的功能 软件是编译安装, 所以更加适合自己的系统, 更加稳定也效率更高 卸载方便 缺 ...

  6. 由js深拷贝引起的对内存空间的一些思考

    数据类型 js常用数据类型分为基本类型和引用类型 基本类型:null.undefined.数值型.字符串型.布尔型 引用类型:数组.对象 内存空间 var a = [1, 2, 3]; var b = ...

  7. ViewGroup事件分发机制解析

    最近在看View的事件分发机制,感觉比复杂的地方就是ViewGrop的dispatchTouchEvent函数,便对照着源码研究了一下.故名思意这个函数起到的作用就是分发事件,在具体分析之前还要说明几 ...

  8. BitMap位图与海量数据的理解

    1. Bit Map算法简介 来自于<编程珠玑>.所谓的Bit-map就是用一个bit位来标记某个元素对应的Value, 而Key即是该元素.由于采用了Bit为单位来存储数据,因此在存储空 ...

  9. 【BZOJ1412】狼和羊的故事(网络流)

    [BZOJ1412]狼和羊的故事(网络流) 题面 Description "狼爱上羊啊爱的疯狂,谁让他们真爱了一场:狼爱上羊啊并不荒唐,他们说有爱就有方向......" Orez听 ...

  10. oc 与 js交互之vue.js

    - .vue.js 调用oc的方法并传值 vue.js 组件中调用方法: methods: {     gotoDetail(item){         //此方法需要在移动端实现,这里可以加入判断 ...