使用SQLite的System.AccessViolationException

时间:2014-11-15 21:59:14

标签: c# sql .net 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.

没有调用堆栈,因为没有为抛出它的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();
                }
            }
        }
    }
}

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

1 个答案:

答案 0 :(得分:2)

CL.值得赞扬;他提到SQLite can be safely used by multiple threads provided that no single database connection is used simultaneously in two or more threads

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

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();
                }
            }
        }
    }
}

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

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();
                }
            }
        }
    }
}