System.Data.SQLite Redistribution Issues

If you need to use sqlite in .Net application the best binding is, of course, System.Data.SQLite.dll. It is a managed assembly, but unfortunately it requires two versions (32-bit and 64-bit) of unmanaged SQLite.Interop.dll to work properly.

It would be great to embed all 3 DLLs into your application to simplify distribution. This tutorial shows how to do it with help of BoxedApp SDK, a library to virtualize files and registry.

You can download the sample from GitHub.

The Virtualization Helps

BoxedApp SDK emulates files, so a developer creates a virtual file (pseudofile) - a "file" that doesn't exist on a disk, but the process works as if this file really exists. BoxedApp intercepts file calls to achieve it.

Suppose an application needs some DLLs. You just create virtual files of the DLLs and the application works.

Embed System.Data.SQLite.dll and SQLite.Interop.dll

To fill a virtual file with a data you need a place where the code takes the data from. For .Net projects, a good choice is embedded resources.

Let's add folder "EmbeddedResources", place System.Data.SQLite.dll there, then add folders "x86" and "x64" and place 32-bit version of SQLite.Interop.dll to x86, and 64-bit version to x64:

Important: Mark Files as Embedded Resources.

After adding the file to embed, go to their properties and set Build Action as "Embedded Resources".

If you don't do it, GetManifestResourceStream() fails.

Look how to do it in Visual Studio:

Return to the code. First thing you need to do when BoxedApp SDK in the game is BoxedApp SDK initialization:

/// 
/// The main entry point for the application.
/// 
[STAThread]
static void Main()
{
    // First of all initialize BoxedApp SDK
    BoxedAppSDK.NativeMethods.BoxedAppSDK_Init();
			

Then create 3 virtual files:

// Create virtual files using the data of embedded resources
CreateVirtualFile(Path.Combine(Application.StartupPath, "System.Data.SQLite.dll"), "SqliteEmbeddingSample.EmbeddedResources.System.Data.SQLite.dll");
CreateVirtualFile(Path.Combine(Path.Combine(Application.StartupPath, "x64"), "SQLite.Interop.dll"), "SqliteEmbeddingSample.EmbeddedResources.x64.SQLite.Interop.dll");
CreateVirtualFile(Path.Combine(Path.Combine(Application.StartupPath, "x86"), "SQLite.Interop.dll"), "SqliteEmbeddingSample.EmbeddedResources.x86.SQLite.Interop.dll");
			

The method CreateVirtualFile creates a virtual file using the data from embedded resources.

Firstly it creates a virtual file calling BoxedAppSDK_CreateVirtualFile that returns a virtual file handle. It's a good idea to close it immediately after you got it. You don't need it and closing the handle helps to avoid leaks. SafeFileHandle from Microsoft.Win32.SafeHandles is way to close a handle automatically:

        static void CreateVirtualFile(string virtualFilePath, string embeddedResourceName)
        {
            // Create virtual file and immediate close handle to avoid handle leaks
            using (SafeFileHandle hHandle =
                new SafeFileHandle(
                    BoxedAppSDK.NativeMethods.BoxedAppSDK_CreateVirtualFile(
                        virtualFilePath,
                        BoxedAppSDK.NativeMethods.EFileAccess.GenericWrite,
                        BoxedAppSDK.NativeMethods.EFileShare.Read,
                        IntPtr.Zero,
                        BoxedAppSDK.NativeMethods.ECreationDisposition.New,
                        BoxedAppSDK.NativeMethods.EFileAttributes.Normal,
                        IntPtr.Zero
                    ),
                    true
                )
            )
            {
            }
			

Then CreateVirtualFile opens virtual file, takes stream of the embedded resource and copy all the data from one to another:

            using (Stream embeddedResourceDataStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(embeddedResourceName))
            {
                const int bufferSize = 1024;
                byte[] buffer = new byte[bufferSize];

                int readBytes;

                using (FileStream virtualFileStream = new FileStream(virtualFilePath, FileMode.Open))
                {
                    while ((readBytes = embeddedResourceDataStream.Read(buffer, 0, bufferSize)) > 0)
                        virtualFileStream.Write(buffer, 0, readBytes);
                }
            }
			

That's all! When the process asks for System.Data.SQLite.dll and SQLite.Interop.dll, they are provided by BoxedApp SDK.

Download

Sources are available on GitHub.