Open source recompilation System.Data.SQLite.dll adaptive x86 x64 AnyCPU recompilation

Keywords: C# SQLite C++

Background:

> System. Data. SQLite. DLL assembly does not support AngCPU format well

 

System.Data.SQLite.dll has three schemes to adapt to x86 and x64:

Use 32 or 64 hybrid compiled assemblies (programs run in 64 bits, but references to 32-bit assemblies cause errors, and vice versa) -- so this scheme is very disgusting.

Assemblies using AnyCPU -- but you have to refer indirectly to the C++ core assembly: SQLite.Interop.dll -- that is, you have to refer to two assemblies at the same time: System.Data.SQLite.dll and SQLLite. Interop. dll.

The third one is based on the second one: run the official installation file of SQLite and install the SQLite.Interop.dll assembly into the system directory automatically - when you call the AnyCPU version of System.Data.SQLite.dll, the program will automatically go to the system directory to find the corresponding C++ assembly.

(But this solution does not support install-free running - your local compilation works properly and copies to other computers crash (the target computer also has to run the official SQLite installation file)

 

The entanglement arises:

I just want to refer to an assembly.

> This assembly is AnyCPU (automatic adaptation x64 x86).

> I compiled the program and sent it to the other party, the other party can directly double-click to run.

The above three solutions: basically, they can't solve this problem perfectly.

 

Existing solutions can be found online:

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

After downloading:

But there are problems:

This buddy downloaded code modification from the official website of SQLite - and finally used code obfuscation (of course, adding hundreds of lines of code to protect his own code - is beyond reproach).

I really don't like 10940_x64 and 10940_86 folders - it seems a bit difficult to modify the source code based on this author, let's just implement one by ourselves.

 

 

Feasibility assessment attempt:

> I downloaded the latest 1.0.105.2 source code from the official website of SQLite.

After compiling, the x64 x86 directory is created manually.

 

> The program is working properly -- in other words, the SQLite authorities have provided the identification of x64 x86 folders

Then, adjust the target:

Increase self-release function.

Release to the current program directory: No x86 and x64 directories are used

Release to the corresponding platform directory: using x86 and x64 directories

Each run-time checks whether the C++ assembly adapts to the current program platform and whether it needs to be re-released.

 

Thus, System.Data.SQLite.dll adaptive AnyCPU begins with self-releasing assembly rewriting:

> GZip compression of C++ assemblies x86 and x64 to save the byte size of the assemblies and to be embedded in the project:

> In reading the official source code of SQLite 1.0.105.2, I found a function in the source code Unsafe NativeMethods. cs: PreLoad SQLiteDll (*).  

> An auxiliary class u RecoverHelper.cs, which releases C++ assemblies, is added and called directly by the PreLoadSQLiteDll(*) function.

> The modified code is as follows:

 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           //Added code
 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       }    

> u RecoverHelper.cs source code is as follows:

  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>repair System.Data.SQLite Required operating environment.</para>
 12     /// <para>release System.Data.SQLite Needed C++Assembly SQLite.Interop.dll</para>
 13     /// </summary>
 14     internal static class __RecoverHelper
 15     {
 16         #region  Attempt to load embedded assemblies
 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         /// Whether will SQLite.Interop.dll Release to x64 x86 Folder, If this parameter is not released to the current directory.
 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         /// When the running environment is not found SQLite.Interop.dll Assembly time, Attempt to write inline bytes back to disk and restore them to the original file SQLite.Interop.dll
 39         /// </summary>
 40         internal static void InitResourceSQLiteInteropAssembly()
 41         {
 42             try
 43             {
 44                 //When the running environment is not found SQLite.Interop.dll Assembly time, Attempt to write inline bytes back to disk and restore them to the original file 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                 //If the disk SQLite.Interop.dll existence, Check correlation MD5
 54                 if (File.Exists(SQLiteInteropDllPath))
 55                 {
 56                     string existFileMD5 = GetFileMD5(SQLiteInteropDllPath);
 57                     string rightFileMD5 = is64Proc ? SQLite_Interop_x64MD5 : SQLite_Interop_x86MD5;
 58 
 59                     //If MD5 Atypism, Remove existing disk SQLite.Interop.dll 
 60                     if (!string.Equals(existFileMD5, rightFileMD5, StringComparison.CurrentCultureIgnoreCase))
 61                         File.Delete(SQLiteInteropDllPath);
 62                 }
 63 
 64 
 65                 //If the disk SQLite.Interop.dll Non-existent, Then release 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                                     //Read all data from compressed streams
 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  auxiliary function
104 
105         /// <summary>
106         /// Computational file MD5, Computation error returns an empty string
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         /// Calculates the specified length of a file from the specified byte MD5 (Insufficient remaining bytes, Only the remaining byte stream is computed), Computation error returns an empty string
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; //1 per time M
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)     //Not the last piece
164                                 md5.TransformBlock(buffer, 0, bufferLength, buffer, 0);
165                             else                                                //The last piece
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 }

 

Finally, use the official test tool of SQLite to test:

> The test passed as follows:

 

Relevant source code and assembly downloads (using official key signatures):

> Click Download Source Code and Signed Assemblies If this article is helpful to you, please click on the "Recommendation" in the lower right corner. Thank you.

Go directly to the Bin directory and run the official test tools test.exe and test32.exe

> By default, release C++ assemblies to the x86 x64 folder. If you want to set it to: release C++ assemblies to the current directory, you can add the following configuration in App.config:

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

Posted by Gho on Tue, 01 Jan 2019 00:03:08 -0800