// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Data; using Microsoft.Isam.Esent.Interop; using Microsoft.Isam.Esent.Interop.Vista; using Microsoft.Isam.Esent.Interop.Windows10; namespace Spludlow.Data { /// /// ExtensibleStorageEngine ESE Read only methods (not a DAL Implimentaion) /// /// Wrapper for ManagedEsent /// /// ListTables() uses ESENTUTL.EXE /// /// Only used it so far for reading MS Edge Browser favorites, but there are a few places in Windows where this may come in handy /// /// Edge needs to be closed for these methods to work. /// dataSet = Spludlow.Data.ExtensibleStorageEngine.ReadDatabase(@"C:\Users\Fred\AppData\Local\Packages\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\AC\MicrosoftEdge\User\Default\DataStore\Data\nouser1\120712-0049\DBStore\spartan.edb", "Favorites"); /// good one Microsoft /// /// ManagedEsent/Esent.Interop (ONLY), Esent.Isam (NOT USED) /// public class ExtensibleStorageEngine { private static Dictionary dataTypeLookup; static ExtensibleStorageEngine() { dataTypeLookup = new Dictionary(); dataTypeLookup.Add(JET_coltyp.Binary, typeof(Byte[])); dataTypeLookup.Add(JET_coltyp.Bit, typeof(Boolean)); dataTypeLookup.Add(JET_coltyp.Currency, typeof(Int64)); dataTypeLookup.Add(JET_coltyp.DateTime, typeof(DateTime)); dataTypeLookup.Add(JET_coltyp.IEEEDouble, typeof(Double)); dataTypeLookup.Add(JET_coltyp.IEEESingle, typeof(Single)); dataTypeLookup.Add(JET_coltyp.Long, typeof(Int32)); dataTypeLookup.Add(JET_coltyp.LongBinary, typeof(Byte[])); dataTypeLookup.Add(JET_coltyp.LongText, typeof(String)); dataTypeLookup.Add(JET_coltyp.Short, typeof(Int16)); dataTypeLookup.Add(JET_coltyp.Text, typeof(String)); dataTypeLookup.Add(JET_coltyp.UnsignedByte, typeof(Byte)); dataTypeLookup.Add(VistaColtyp.GUID, typeof(Guid)); dataTypeLookup.Add(VistaColtyp.LongLong, typeof(Int64)); dataTypeLookup.Add(VistaColtyp.UnsignedLong, typeof(UInt32)); dataTypeLookup.Add(VistaColtyp.UnsignedShort, typeof(UInt16)); dataTypeLookup.Add(Windows10Coltyp.UnsignedLongLong, typeof(UInt64)); } public static string[] ListTables(string edbFilename) { string programPath = Spludlow.SpawnProcess.FindPath(@"ESENTUTL.EXE"); Spludlow.SpawnProcess.ProcessExitInfo info = Spludlow.SpawnProcess.Run(programPath, "/mm " + edbFilename, 0); if (info.ExitCode != 0) throw new ApplicationException("ESE ListTables: ESENTUTL.EXE Exit Code: " + info.ExitCode + ", stderr:" + info.StandardError); List tableNames = new List(); bool inBody = false; foreach (string line in Spludlow.Text.SplitLines(info.StandardOutput)) { if (line.StartsWith("====") == true) { inBody = true; continue; } if (inBody == false) continue; if (line.StartsWith("****") == true) break; string[] words = Spludlow.Text.Split(line, ' ', true, false); if (words.Length != 4) continue; if (words[1] == "Tbl") tableNames.Add(words[0]); } return tableNames.ToArray(); } /// /// Clean shutdown state /// public static void Recover(string edbFilename) { string programPath = Spludlow.SpawnProcess.FindPath(@"ESENTUTL.EXE"); string logDirectory = Path.GetDirectoryName(edbFilename) + @"\LogFiles"; Spludlow.SpawnProcess.ProcessExitInfo info = Spludlow.SpawnProcess.Run(programPath, "/r edb", logDirectory, null, 0, false); if (info.ExitCode != 0) throw new ApplicationException("ESE Repair: ESENTUTL.EXE Exit Code: " + info.ExitCode + ", stderr:" + info.StandardError); } public static void Repair(string edbFilename) { string programPath = Spludlow.SpawnProcess.FindPath(@"ESENTUTL.EXE"); Spludlow.SpawnProcess.ProcessExitInfo info = Spludlow.SpawnProcess.Run(programPath, "/p /o " + edbFilename, 0); if (info.ExitCode != 0) throw new ApplicationException("ESE Repair: ESENTUTL.EXE Exit Code: " + info.ExitCode + ", stderr:" + info.StandardError); } public static DataSet ReadDatabase(string edbFilename) { string[] tableNames = ListTables(edbFilename); return ReadDatabase(edbFilename, tableNames); } public static DataSet ReadDatabase(string edbFilename, string tableName) { return ReadDatabase(edbFilename, new string[] { tableName }); } public static DataSet ReadDatabase(string edbFilename, string[] tableNames) { DataSet dataSet = new DataSet(); int pageSize = 0; Api.JetGetDatabaseFileInfo(edbFilename, out pageSize, JET_DbInfo.PageSize); Api.JetSetSystemParameter(JET_INSTANCE.Nil, JET_SESID.Nil, JET_param.DatabasePageSize, pageSize, null); string instanceName = Guid.NewGuid().ToString(); JET_INSTANCE jetInstance = JET_INSTANCE.Nil; Api.JetCreateInstance(out jetInstance, instanceName); Api.JetInit(ref jetInstance); try { JET_SESID jetSession = JET_SESID.Nil; Api.JetBeginSession(jetInstance, out jetSession, null, null); try { JET_DBID jetDatabaseId = JET_DBID.Nil; Api.JetAttachDatabase(jetSession, edbFilename, AttachDatabaseGrbit.ReadOnly); Api.JetOpenDatabase(jetSession, edbFilename, null, out jetDatabaseId, OpenDatabaseGrbit.ReadOnly); try { foreach (string tableName in tableNames) ReadTable(dataSet, tableName, jetSession, jetDatabaseId); } finally { Api.JetCloseDatabase(jetSession, jetDatabaseId, CloseDatabaseGrbit.None); Api.JetDetachDatabase(jetSession, edbFilename); } } finally { Api.JetEndSession(jetSession, EndSessionGrbit.None); } } finally { Api.JetTerm(jetInstance); } return dataSet; } private static void ReadTable(DataSet dataSet, string tableName, JET_SESID jetSession, JET_DBID jetDatabaseId) { DataTable table = new DataTable(tableName); JET_TABLEID tableId = JET_TABLEID.Nil; Api.JetOpenTable(jetSession, jetDatabaseId, tableName, null, 0, OpenTableGrbit.ReadOnly, out tableId); try { List tableColumns = new List(Api.GetTableColumns(jetSession, tableId)); foreach (ColumnInfo columnInfo in tableColumns) { if (dataTypeLookup.ContainsKey(columnInfo.Coltyp) == false) throw new ApplicationException("ExtensibleStorageEngine ReadTable; Type not found: " + columnInfo.Coltyp); table.Columns.Add(columnInfo.Name, dataTypeLookup[columnInfo.Coltyp]); } for (JET_Move move = JET_Move.First; Api.TryMove(jetSession, tableId, move, MoveGrbit.None); move = JET_Move.Next) { DataRow row = table.NewRow(); foreach (ColumnInfo columnInfo in tableColumns) { object data = null; switch (columnInfo.Coltyp) { case JET_coltyp.Binary: data = Api.RetrieveColumn(jetSession, tableId, columnInfo.Columnid); break; case JET_coltyp.Bit: data = Api.RetrieveColumnAsBoolean(jetSession, tableId, columnInfo.Columnid); break; case JET_coltyp.Currency: data = Api.RetrieveColumnAsInt64(jetSession, tableId, columnInfo.Columnid); break; case JET_coltyp.DateTime: data = Api.RetrieveColumnAsDateTime(jetSession, tableId, columnInfo.Columnid); break; case JET_coltyp.IEEEDouble: data = Api.RetrieveColumnAsDouble(jetSession, tableId, columnInfo.Columnid); break; case JET_coltyp.IEEESingle: data = Api.RetrieveColumnAsFloat(jetSession, tableId, columnInfo.Columnid); break; case JET_coltyp.Long: data = Api.RetrieveColumnAsInt32(jetSession, tableId, columnInfo.Columnid); break; case JET_coltyp.LongBinary: data = Api.RetrieveColumn(jetSession, tableId, columnInfo.Columnid); break; case JET_coltyp.LongText: data = Api.RetrieveColumnAsString(jetSession, tableId, columnInfo.Columnid, (columnInfo.Cp == JET_CP.Unicode ? Encoding.Unicode : Encoding.ASCII)); break; case JET_coltyp.Short: data = Api.RetrieveColumnAsInt16(jetSession, tableId, columnInfo.Columnid); break; case JET_coltyp.Text: data = Api.RetrieveColumnAsString(jetSession, tableId, columnInfo.Columnid, (columnInfo.Cp == JET_CP.Unicode ? Encoding.Unicode : Encoding.ASCII)); break; case JET_coltyp.UnsignedByte: data = Api.RetrieveColumnAsByte(jetSession, tableId, columnInfo.Columnid); break; case VistaColtyp.GUID: data = Api.RetrieveColumnAsGuid(jetSession, tableId, columnInfo.Columnid); break; case VistaColtyp.LongLong: data = Api.RetrieveColumnAsInt64(jetSession, tableId, columnInfo.Columnid); break; case VistaColtyp.UnsignedLong: data = Api.RetrieveColumnAsUInt32(jetSession, tableId, columnInfo.Columnid); break; case VistaColtyp.UnsignedShort: data = Api.RetrieveColumnAsUInt16(jetSession, tableId, columnInfo.Columnid); break; case Windows10Coltyp.UnsignedLongLong: data = Api.RetrieveColumnAsUInt64(jetSession, tableId, columnInfo.Columnid); break; } if (data == null) data = DBNull.Value; row[columnInfo.Name] = data; } table.Rows.Add(row); } dataSet.Tables.Add(table); } finally { Api.JetCloseTable(jetSession, tableId); } } } }