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