System.AccessViolationException与SQLite

Ale*_*dru 5 .net c# sql sqlite exception

现在,我正在处理此错误:

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.
Run Code Online (Sandbox Code Playgroud)

由于没有为引发它的DLL加载任何模块,因此没有调用堆栈。我有一个枚举整个注册表并尝试将所有键/值保存到SQLite数据库文件作为注册表快照的应用程序。从某种意义上来说,它不是贪婪的,如果它无法访问某些密钥,则这些密钥将被丢弃,等等:

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();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

由于应用程序是线程化的,因此AddEntry在事务周围使用并发。最初,我没有使用插入计数器,但是后来我意识到,由于我的应用程序是针对x86构建的,并且使用.NET Framework 4.5.1,因此只要该应用程序具有近2GB的RAM,它就会完全冻结,这导致我相信这是由于x86系统上SQLite的另一个问题所致,例如x86上.NET中集合的2GB RAM限制。我使用插入计数器尝试如此频繁地提交事务,以免遇到较大的事务队列。现在,即使我这样做了,我仍然留下了这个AccessViolationException。我不确定是什么原因造成的。有人有任何线索吗?完整的代码在这里,您可以将其复制并粘贴到控制台应用程序中,然后亲自查看。我只是希望您拥有一个相当强大的注册表。非常感谢您的帮助;提前致谢!

Ale*_*dru 2

CL。这值得赞扬;他提到,只要两个或多个线程中没有同时使用单个数据库连接,SQLite 就可以安全地由多个线程使用

这种方法解决了这个问题:

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();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:甚至可能是这样的(内存使用量非常少)......

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();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)