Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

System.AccessViolationException with SQLite

Right now, I'm dealing with this error:

An unhandled exception of type 'System.AccessViolationException' occurred in Unknown Module.

Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

There is no call stack for it since no module was loaded for the DLL that threw it. I have an application that enumerates the entire registry and tries to save all keys/values to an SQLite database file, as a registry snapshot. Its not greedy in the sense that if it can't get access to some keys, those keys are discarded, etc.:

using Microsoft.Win32;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace RegistryMonitor
{
    class Program
    {

        static void Main(string[] args)
        {
            GenerateRegistrySnapshot("SnapshotOne.sqlite");
            Console.ReadLine();   
        }

        static void GenerateRegistrySnapshot(string filename)
        {
            File.Delete(filename);
            SQLiteConnection.CreateFile(filename);
            using (SQLiteConnection connection = new SQLiteConnection("Data Source=" + filename + ";Version=3;"))
            {
                connection.Open();
                CreateTable(connection);
                Stopwatch watch = new Stopwatch();
                Console.WriteLine("Started walking the registry into file {0}.", filename);
                watch.Start();
                transaction = connection.BeginTransaction();
                WalkTheRegistryAndPopulateTheSnapshot(connection);
                try
                {
                    transaction.Commit();
                    transaction.Dispose();
                }
                catch { }
                Console.WriteLine("Finished walking the registry and populating the snapshot.");
                watch.Stop();
                Console.WriteLine("Finished walking the registry in {0} seconds.", watch.Elapsed.TotalSeconds);
                connection.Close();
            }
        }

        static void CreateTable(SQLiteConnection connection)
        {
            SQLiteCommand command = new SQLiteCommand("CREATE TABLE Snapshot (ID INTEGER PRIMARY KEY AUTOINCREMENT, RegistryView INTEGER NULL, Path TEXT NULL, IsKey BOOLEAN NULL, RegistryValueKind INTEGER NULL, ValueName TEXT NULL, Value BLOB NULL, HashValue INTEGER NULL)", connection);
            command.ExecuteNonQuery();
        }

        static SQLiteTransaction transaction = null;
        static int insertions = 0;
        static object transactionLock = new object();

        static void AddEntry(SQLiteConnection connection, RegistryPath path)
        {
            SQLiteCommand command = new SQLiteCommand("INSERT INTO Snapshot (RegistryView, Path, IsKey, RegistryValueKind, ValueName, Value, HashValue) VALUES (@RegistryView, @Path, @IsKey, @RegistryValueKind, @ValueName, @Value, @HashValue)", connection);
            command.Parameters.Add("@RegistryView", DbType.Int32).Value = path.View;
            command.Parameters.Add("@Path", DbType.String).Value = path.Path;
            command.Parameters.Add("@IsKey", DbType.Boolean).Value = path.IsKey;
            command.Parameters.Add("@RegistryValueKind", DbType.Int32).Value = path.ValueKind;
            command.Parameters.Add("@ValueName", DbType.String).Value = path.ValueName;
            command.Parameters.Add("@Value", DbType.Object).Value = path.Value;
            command.Parameters.Add("@HashValue", DbType.Int32).Value = path.HashValue;
            command.ExecuteNonQuery();
            lock (transactionLock)
            {
                insertions++;
                if (insertions > 100000)
                {
                    insertions = 0;
                    transaction.Commit();
                    transaction.Dispose();
                    transaction = connection.BeginTransaction();
                }
            }
        }

        private static void WalkTheRegistryAndPopulateTheSnapshot(SQLiteConnection connection)
        {
            List<ManualResetEvent> handles = new List<ManualResetEvent>();
            foreach (RegistryHive hive in Enum.GetValues(typeof(RegistryHive)))
            {
                foreach (RegistryView view in Enum.GetValues(typeof(RegistryView)).Cast<RegistryView>().ToList().Where(x => x != RegistryView.Default))
                {
                    ManualResetEvent manualResetEvent = new ManualResetEvent(false);
                    handles.Add(manualResetEvent);
                    new Thread(() =>
                    {
                        Console.WriteLine("Walking hive {0} in registry view {1}.", hive.ToString(), view.ToString());
                        WalkKey(connection, view, RegistryKey.OpenBaseKey(hive, view));
                        Console.WriteLine("Finished walking hive {0} in registry view {1}.", hive.ToString(), view.ToString());
                        manualResetEvent.Set();
                        Console.WriteLine("Finished setting event for hive {0} in registry view {1}.", hive.ToString(), view.ToString());
                    }).Start();
                }
            }
            ManualResetEvent.WaitAll(handles.ToArray());
        }

        private static void WalkKey(SQLiteConnection connection, RegistryView view, RegistryKey key)
        {
            RegistryPath path = new RegistryPath(view, key.Name);
            AddEntry(connection, path);
            string[] valueNames = null;
            try
            {
                valueNames = key.GetValueNames();
            }
            catch { }
            if (valueNames != null)
            {
                foreach (string valueName in valueNames)
                {
                    RegistryValueKind valueKind = RegistryValueKind.Unknown;
                    try
                    {
                        valueKind = key.GetValueKind(valueName);
                    }
                    catch { }
                    object value = key.GetValue(valueName);
                    RegistryPath pathForValue = new RegistryPath(view, key.Name, valueKind, valueName, value);
                    AddEntry(connection, pathForValue);
                }
            }
            string[] subKeyNames = null;
            try
            {
                subKeyNames = key.GetSubKeyNames();
            }
            catch { }
            if (subKeyNames != null)
            {
                foreach (string subKeyName in subKeyNames)
                {
                    try
                    {
                        WalkKey(connection, view, key.OpenSubKey(subKeyName));
                    }
                    catch { }
                }
            }
        }

        class RegistryPath
        {
            public RegistryView View;
            public string Path;
            public bool IsKey;
            public RegistryValueKind ValueKind;
            public string ValueName;
            public object Value;
            public int HashValue;

            public RegistryPath(RegistryView view, string path)
            {
                View = view;
                Path = path;
                IsKey = true;
                HashValue = (view.GetHashCode() ^ path.GetHashCode()).GetHashCode();
            }

            public RegistryPath(RegistryView view, string path, RegistryValueKind valueKind, string valueName, object value)
            {
                View = view;
                Path = path;
                IsKey = false;
                ValueKind = valueKind;
                ValueName = valueName;
                Value = value;
                if (value != null)
                {
                    HashValue = (view.GetHashCode() ^ path.GetHashCode() ^ valueKind.GetHashCode() ^ valueName.GetHashCode() ^ value.GetHashCode()).GetHashCode();
                }
                else
                {
                    HashValue = (view.GetHashCode() ^ path.GetHashCode() ^ valueKind.GetHashCode() ^ valueName.GetHashCode()).GetHashCode();
                }
            }
        }
    }
}

Since the application is threaded, AddEntry uses concurrency around transactions. Originally, I did not use an insertions counter, but then I soon realized that, since my application is built for x86 and using .NET Framework 4.5.1, whenever the application had almost 2GB of RAM, it would totally freeze up, leading me to believe that its due to another issue in SQLite on x86 systems, like for example the 2GB RAM limit of collections in .NET on x86. I used an insertions counter to try to commit transactions ever so often in order to not hit a large transaction queue. Now, even though I did that, I'm still left with this AccessViolationException. I'm not sure what causes it. Does anyone have any clues? The entire code is here, you can copy and paste it into a console application and see for yourself. I just hope you have a fairly beefy registry. Help is greatly appreciated; thanks in advance!

like image 855
Alexandru Avatar asked Oct 31 '22 14:10

Alexandru


1 Answers

CL. deserves credit for this; he mentioned that SQLite can be safely used by multiple threads provided that no single database connection is used simultaneously in two or more threads.

This approach fixed the problem:

using Microsoft.Win32;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace RegistryMonitor
{
    class Program
    {

        static void Main(string[] args)
        {
            GenerateRegistrySnapshot("SnapshotOne.sqlite");
            Console.ReadLine();   
        }

        static void GenerateRegistrySnapshot(string filename)
        {
            File.Delete(filename);
            SQLiteConnection.CreateFile(filename);
            bool finished = false;
            ConcurrentQueue<RegistryPath> queue = new ConcurrentQueue<RegistryPath>();
            Thread worker = new Thread(() =>
            {
                using (SQLiteConnection connection = new SQLiteConnection("Data Source=" + filename + ";Version=3;"))
                {
                    connection.Open();
                    CreateTable(connection);
                    SQLiteTransaction transaction = connection.BeginTransaction();
                    RegistryPath path;
                    while (!finished)
                    {
                        while (queue.TryDequeue(out path))
                        {
                            AddEntry(connection, path);
                        }
                        Thread.Sleep(100);
                    }
                    transaction.Commit();
                    transaction.Dispose();
                    connection.Close();
                }
            });
            worker.Start();
            Stopwatch watch = new Stopwatch();
            Console.WriteLine("Started walking the registry into file {0}.", filename);
            watch.Start();
            WalkTheRegistryAndPopulateTheSnapshot(queue);
            finished = true;
            worker.Join();
            watch.Stop();
            Console.WriteLine("Finished walking the registry in {0} seconds.", watch.Elapsed.TotalSeconds);
        }

        static void CreateTable(SQLiteConnection connection)
        {
            SQLiteCommand command = new SQLiteCommand("CREATE TABLE Snapshot (ID INTEGER PRIMARY KEY AUTOINCREMENT, RegistryView INTEGER NULL, Path TEXT NULL, IsKey BOOLEAN NULL, RegistryValueKind INTEGER NULL, ValueName TEXT NULL, Value BLOB NULL, HashValue INTEGER NULL)", connection);
            command.ExecuteNonQuery();
        }

        static void AddEntry(SQLiteConnection connection, RegistryPath path)
        {
            SQLiteCommand command = new SQLiteCommand("INSERT INTO Snapshot (RegistryView, Path, IsKey, RegistryValueKind, ValueName, Value, HashValue) VALUES (@RegistryView, @Path, @IsKey, @RegistryValueKind, @ValueName, @Value, @HashValue)", connection);
            command.Parameters.Add("@RegistryView", DbType.Int32).Value = path.View;
            command.Parameters.Add("@Path", DbType.String).Value = path.Path;
            command.Parameters.Add("@IsKey", DbType.Boolean).Value = path.IsKey;
            command.Parameters.Add("@RegistryValueKind", DbType.Int32).Value = path.ValueKind;
            command.Parameters.Add("@ValueName", DbType.String).Value = path.ValueName;
            command.Parameters.Add("@Value", DbType.Object).Value = path.Value;
            command.Parameters.Add("@HashValue", DbType.Int32).Value = path.HashValue;
            command.ExecuteNonQuery();
        }

        private static void WalkTheRegistryAndPopulateTheSnapshot(ConcurrentQueue<RegistryPath> queue)
        {
            List<ManualResetEvent> handles = new List<ManualResetEvent>();
            foreach (RegistryHive hive in Enum.GetValues(typeof(RegistryHive)))
            {
                foreach (RegistryView view in Enum.GetValues(typeof(RegistryView)).Cast<RegistryView>().ToList().Where(x => x != RegistryView.Default))
                {
                    ManualResetEvent manualResetEvent = new ManualResetEvent(false);
                    handles.Add(manualResetEvent);
                    new Thread(() =>
                    {
                        WalkKey(queue, view, RegistryKey.OpenBaseKey(hive, view));
                        manualResetEvent.Set();
                    }).Start();
                }
            }
            ManualResetEvent.WaitAll(handles.ToArray());
        }

        private static void WalkKey(ConcurrentQueue<RegistryPath> queue, RegistryView view, RegistryKey key)
        {
            RegistryPath path = new RegistryPath(view, key.Name);
            queue.Enqueue(path);
            string[] valueNames = null;
            try
            {
                valueNames = key.GetValueNames();
            }
            catch { }
            if (valueNames != null)
            {
                foreach (string valueName in valueNames)
                {
                    RegistryValueKind valueKind = RegistryValueKind.Unknown;
                    try
                    {
                        valueKind = key.GetValueKind(valueName);
                    }
                    catch { }
                    object value = key.GetValue(valueName);
                    RegistryPath pathForValue = new RegistryPath(view, key.Name, valueKind, valueName, value);
                    queue.Enqueue(pathForValue);
                }
            }
            string[] subKeyNames = null;
            try
            {
                subKeyNames = key.GetSubKeyNames();
            }
            catch { }
            if (subKeyNames != null)
            {
                foreach (string subKeyName in subKeyNames)
                {
                    try
                    {
                        WalkKey(queue, view, key.OpenSubKey(subKeyName));
                    }
                    catch { }
                }
            }
        }

        class RegistryPath
        {
            public RegistryView View;
            public string Path;
            public bool IsKey;
            public RegistryValueKind ValueKind;
            public string ValueName;
            public object Value;
            public int HashValue;

            public RegistryPath(RegistryView view, string path)
            {
                View = view;
                Path = path;
                IsKey = true;
                HashValue = (view.GetHashCode() ^ path.GetHashCode()).GetHashCode();
            }

            public RegistryPath(RegistryView view, string path, RegistryValueKind valueKind, string valueName, object value)
            {
                View = view;
                Path = path;
                IsKey = false;
                ValueKind = valueKind;
                ValueName = valueName;
                Value = value;
                if (value != null)
                {
                    HashValue = (view.GetHashCode() ^ path.GetHashCode() ^ valueKind.GetHashCode() ^ valueName.GetHashCode() ^ value.GetHashCode()).GetHashCode();
                }
                else
                {
                    HashValue = (view.GetHashCode() ^ path.GetHashCode() ^ valueKind.GetHashCode() ^ valueName.GetHashCode()).GetHashCode();
                }
            }
        }
    }
}

Edit: Possibly even something like this (very minimal memory usage)...

using Microsoft.Win32;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace RegistryMonitor
{
    class Program
    {

        static void Main(string[] args)
        {
            GenerateRegistrySnapshot("Snapshot.sqlite");
            Console.ReadLine();   
        }

        static object writeLock = new object();
        static AutoResetEvent writeReady = new AutoResetEvent(false);
        static AutoResetEvent writeCompleted = new AutoResetEvent(false);
        static RegistryPath pathToWrite;

        static void GenerateRegistrySnapshot(string filename)
        {
            File.Delete(filename);
            SQLiteConnection.CreateFile(filename);
            bool finished = false;
            Thread worker = new Thread(() =>
            {
                using (SQLiteConnection connection = new SQLiteConnection("Data Source=" + filename + ";Version=3;"))
                {
                    connection.Open();
                    CreateTable(connection);
                    SQLiteTransaction transaction = connection.BeginTransaction();
                    while (!finished)
                    {
                        writeReady.WaitOne();
                        if (finished)
                        {
                            break;
                        }
                        AddEntry(connection, pathToWrite);
                        writeCompleted.Set();
                    }
                    transaction.Commit();
                    transaction.Dispose();
                    connection.Close();
                }
            });
            worker.Start();
            Stopwatch watch = new Stopwatch();
            Console.WriteLine("Started walking the registry into file {0}.", filename);
            watch.Start();
            WalkTheRegistryAndPopulateTheSnapshot();
            finished = true;
            writeReady.Set();
            worker.Join();
            watch.Stop();
            Console.WriteLine("Finished walking the registry in {0} seconds.", watch.Elapsed.TotalSeconds);
        }

        static void CreateTable(SQLiteConnection connection)
        {
            SQLiteCommand command = new SQLiteCommand("CREATE TABLE Snapshot (ID INTEGER PRIMARY KEY AUTOINCREMENT, RegistryView INTEGER NULL, Path TEXT NULL, IsKey BOOLEAN NULL, RegistryValueKind INTEGER NULL, ValueName TEXT NULL, Value BLOB NULL, HashValue INTEGER NULL)", connection);
            command.ExecuteNonQuery();
        }

        static void AddEntry(SQLiteConnection connection, RegistryPath path)
        {
            SQLiteCommand command = new SQLiteCommand("INSERT INTO Snapshot (RegistryView, Path, IsKey, RegistryValueKind, ValueName, Value, HashValue) VALUES (@RegistryView, @Path, @IsKey, @RegistryValueKind, @ValueName, @Value, @HashValue)", connection);
            command.Parameters.Add("@RegistryView", DbType.Int32).Value = path.View;
            command.Parameters.Add("@Path", DbType.String).Value = path.Path;
            command.Parameters.Add("@IsKey", DbType.Boolean).Value = path.IsKey;
            command.Parameters.Add("@RegistryValueKind", DbType.Int32).Value = path.ValueKind;
            command.Parameters.Add("@ValueName", DbType.String).Value = path.ValueName;
            command.Parameters.Add("@Value", DbType.Object).Value = path.Value;
            command.Parameters.Add("@HashValue", DbType.Int32).Value = path.HashValue;
            command.ExecuteNonQuery();
        }

        private static void WalkTheRegistryAndPopulateTheSnapshot()
        {
            List<ManualResetEvent> handles = new List<ManualResetEvent>();
            foreach (RegistryHive hive in Enum.GetValues(typeof(RegistryHive)))
            {
                foreach (RegistryView view in Enum.GetValues(typeof(RegistryView)).Cast<RegistryView>().ToList().Where(x => x != RegistryView.Default))
                {
                    ManualResetEvent manualResetEvent = new ManualResetEvent(false);
                    handles.Add(manualResetEvent);
                    new Thread(() =>
                    {
                        WalkKey(view, RegistryKey.OpenBaseKey(hive, view));
                        manualResetEvent.Set();
                    }).Start();
                }
            }
            ManualResetEvent.WaitAll(handles.ToArray());
        }

        private static void WalkKey(RegistryView view, RegistryKey key)
        {
            RegistryPath path = new RegistryPath(view, key.Name);
            Write(path);
            string[] valueNames = null;
            try
            {
                valueNames = key.GetValueNames();
            }
            catch { }
            if (valueNames != null)
            {
                foreach (string valueName in valueNames)
                {
                    RegistryValueKind valueKind = RegistryValueKind.Unknown;
                    try
                    {
                        valueKind = key.GetValueKind(valueName);
                    }
                    catch { }
                    object value = key.GetValue(valueName);
                    RegistryPath pathForValue = new RegistryPath(view, key.Name, valueKind, valueName, value);
                    Write(pathForValue);
                }
            }
            string[] subKeyNames = null;
            try
            {
                subKeyNames = key.GetSubKeyNames();
            }
            catch { }
            if (subKeyNames != null)
            {
                foreach (string subKeyName in subKeyNames)
                {
                    try
                    {
                        WalkKey(view, key.OpenSubKey(subKeyName));
                    }
                    catch { }
                }
            }
        }

        private static void Write(RegistryPath path)
        {
            lock (writeLock)
            {
                pathToWrite = path;
                writeReady.Set();
                writeCompleted.WaitOne();
            }
        }

        class RegistryPath
        {
            public RegistryView View;
            public string Path;
            public bool IsKey;
            public RegistryValueKind ValueKind;
            public string ValueName;
            public object Value;
            public int HashValue;

            public RegistryPath(RegistryView view, string path)
            {
                View = view;
                Path = path;
                IsKey = true;
                HashValue = (view.GetHashCode() ^ path.GetHashCode()).GetHashCode();
            }

            public RegistryPath(RegistryView view, string path, RegistryValueKind valueKind, string valueName, object value)
            {
                View = view;
                Path = path;
                IsKey = false;
                ValueKind = valueKind;
                ValueName = valueName;
                Value = value;
                if (value != null)
                {
                    HashValue = (view.GetHashCode() ^ path.GetHashCode() ^ valueKind.GetHashCode() ^ valueName.GetHashCode() ^ value.GetHashCode()).GetHashCode();
                }
                else
                {
                    HashValue = (view.GetHashCode() ^ path.GetHashCode() ^ valueKind.GetHashCode() ^ valueName.GetHashCode()).GetHashCode();
                }
            }
        }
    }
}
like image 138
Alexandru Avatar answered Nov 15 '22 04:11

Alexandru