/// <summary> /// + Open MSysObjects /// + Enumerate columns /// /// We're only interested in attributes for ntds.dit /// </summary> /// <returns></returns> private MSysObjectsRow[] EnumColumns() { // 此处用了打开表时的只读属性,读的时候应该会快一点. wrn = Api.JetOpenTable(sesId, dbId, MSYSOBJECTS, null, 0, OpenTableGrbit.ReadOnly | OpenTableGrbit.Sequential, out _tableid); if (wrn == JET_wrn.Success) { //JET_COLUMNLIST columndef; // 检索有关表列的信息。将列名称映射到列ID的字典 //Api.JetGetTableColumnInfo(sesId, _tableid, null, out columndef); var columnDictionary = Api.GetColumnDictionary(sesId, _tableid); // 循环遍历表,向字典添加属性ID和列名 Api.MoveBeforeFirst(sesId, _tableid); while (Api.TryMoveNext(sesId, _tableid)) { var nameColumn = new Utf8StringColumnValue { Columnid = columnDictionary["Name"] }; Api.RetrieveColumns(sesId, _tableid, nameColumn); if (nameColumn.Value.StartsWith("ATT", StringComparison.Ordinal)) { mSysObjects.Add(new MSysObjectsRow { AttributeId = int.Parse(Regex.Replace(nameColumn.Value, "[A-Za-z-]", string.Empty, RegexOptions.None), CultureInfo.InvariantCulture), ColumnName = nameColumn.Value }); // AttributeId = 2128564599 // ColumnName = ATTf-2128564599 } } } Api.JetCloseTable(sesId, _tableid); return(mSysObjects.ToArray()); }
/// <summary> /// 加载 ntds.dit,并打开 /// </summary> /// <param name="fname"></param> /// <returns></returns> public Boolean NTDSLoad(String dbPath) { // 应用实例对象和会话对象进行数据库的操作 // 在打开数据库之前,得先用 JetAttachDatabase 将备用数据库附加到当前会话,不然会引发 No such database 异常. wrn = Api.JetAttachDatabase(sesId, dbPath, AttachDatabaseGrbit.ReadOnly); if (wrn == JET_wrn.Success) { // 打开数据库 wrn = Api.JetOpenDatabase(sesId, dbPath, null, out dbId, OpenDatabaseGrbit.ReadOnly); if (wrn == JET_wrn.Success) { // 针对数据库的一系列操作 _mSysObjects = EnumColumns(); _ldapDisplayNameToDatatableColumnNameDictionary = EnumerateDatatableTableLdapDisplayNames(_mSysObjects); _datatable = EnumerateDatatableTable(_ldapDisplayNameToDatatableColumnNameDictionary); } else { Console.WriteLine("[!] error at JetOpenDatabase()"); } } else { Console.WriteLine("[!] error at JetAttachDatabase()"); } return(wrn == JET_wrn.Success); }
/// <summary> /// Update the output members of the class from a NATIVE_RETRIEVECOLUMN /// structure. This should be done after the columns are retrieved. /// </summary> /// <param name="native"> /// The structure containing the updated output fields. /// </param> internal void UpdateFromNativeRetrievecolumn(NATIVE_RETRIEVECOLUMN native) { this.cbActual = checked ((int)native.cbActual); this.columnidNextTagged = new JET_COLUMNID { Value = native.columnidNextTagged }; this.err = (JET_wrn)native.err; }
internal static void Check(JET_wrn wrn, String message) { if (wrn == JET_wrn.Success) { return; } throw new ApplicationException(message + " " + wrn); }
/// <summary> /// Create the nullable return value. /// </summary> /// <typeparam name="T">The (struct) type to return.</typeparam> /// <param name="data">The data retrieved from the column.</param> /// <param name="dataSize">The size of the data.</param> /// <param name="wrn">The warning code from esent.</param> /// <param name="actualDataSize">The actual size of the data retireved fomr esent.</param> /// <returns>A nullable struct of type T.</returns> private static T?CreateReturnValue <T>(T data, int dataSize, JET_wrn wrn, int actualDataSize) where T : struct { if (JET_wrn.ColumnNull == wrn) { return(new T?()); } CheckDataSize(dataSize, actualDataSize); return(data); }
/// <summary> /// Retrieves a string column value from the current record. The record is that /// record associated with the index entry at the current position of the cursor. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="encoding">The string encoding to use when converting data.</param> /// <param name="grbit">Retrieval options.</param> /// <returns>The data retrieved from the column as a string. Null if the column is null.</returns> public static string RetrieveColumnAsString( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, Encoding encoding, RetrieveColumnGrbit grbit) { // This is an optimization for retrieving Unicode strings if (Encoding.Unicode == encoding) { return(RetrieveUnicodeString(sesid, tableid, columnid, grbit)); } // Retrieving a string happens in two stages: first the data is retrieved into a data // buffer and then the buffer is converted to a string. The buffer isn't needed for // very long so we try to use a cached buffer. byte[] cachedBuffer = Caches.ColumnCache.Allocate(); byte[] data = cachedBuffer; int dataSize; JET_wrn wrn = JetRetrieveColumn(sesid, tableid, columnid, data, data.Length, out dataSize, grbit, null); if (JET_wrn.ColumnNull == wrn) { return(null); } if (JET_wrn.BufferTruncated == wrn) { Debug.Assert(dataSize > data.Length, "Expected column to be bigger than buffer"); data = new byte[dataSize]; wrn = JetRetrieveColumn(sesid, tableid, columnid, data, data.Length, out dataSize, grbit, null); if (JET_wrn.BufferTruncated == wrn) { string error = String.Format( CultureInfo.CurrentCulture, "Column size changed from {0} to {1}. The record was probably updated by another thread.", data.Length, dataSize); Trace.TraceError(error); throw new InvalidOperationException(error); } } // If we are about to decode ASCII data then use the UTF8 decoder instead. This // is done because the UTF8 decoder is faster and will produce the same results // on ASCII data. Different results will be produced on invalid data, but that // behaviour can be considered undefined. Encoding decoder = (encoding is ASCIIEncoding) ? asciiDecoder : encoding; string s = decoder.GetString(data, 0, dataSize); // Now we have extracted the string from the buffer we can free (cache) the buffer. Caches.ColumnCache.Free(ref cachedBuffer); return(s); }
public static ulong?RetrieveColumnAsUInt64( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit) { unsafe { const int DataSize = sizeof(ulong); ulong data; var pointer = new IntPtr(&data); int actualDataSize; JET_wrn wrn = JetRetrieveColumn( sesid, tableid, columnid, pointer, DataSize, out actualDataSize, grbit, null); return(CreateReturnValue(data, DataSize, wrn, actualDataSize)); } }
/// <summary> /// Retrieves the size of a single column value from the current record. /// The record is that record associated with the index entry at the /// current position of the cursor. Alternatively, this function can /// retrieve a column from a record being created in the cursor copy /// buffer. This function can also retrieve column data from an index /// entry that references the current record. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="itagSequence"> /// The sequence number of value in a multi-valued column. /// The array of values is one-based. The first value is /// sequence 1, not 0. If the record column has only one value then /// 1 should be passed as the itagSequence. /// </param> /// <param name="grbit">Retrieve column options.</param> /// <returns>The size of the column. 0 if the column is null.</returns> public static int?RetrieveColumnSize( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, int itagSequence, RetrieveColumnGrbit grbit) { var retinfo = new JET_RETINFO { itagSequence = itagSequence }; int dataSize; JET_wrn wrn = JetRetrieveColumn( sesid, tableid, columnid, null, 0, out dataSize, grbit, retinfo); if (JET_wrn.ColumnNull == wrn) { return(null); } return(dataSize); }
/// <summary> /// Retrieve a Unicode (UTF16) string. This is optimized to take advantage of the fact /// that no conversion is needed. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The table to retrieve from.</param> /// <param name="columnid">The column to retrieve.</param> /// <param name="grbit">Retrieve options.</param> /// <returns>The string retrieved from the column.</returns> private static unsafe string RetrieveUnicodeString(JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit) { // 512 Unicode characters (1kb on stack) const int BufferSize = 512; char * buffer = stackalloc char[BufferSize]; int actualDataSize; JET_wrn wrn = JetRetrieveColumn(sesid, tableid, columnid, new IntPtr(buffer), BufferSize * sizeof(char), out actualDataSize, grbit, null); if (JET_wrn.ColumnNull == wrn) { return(null); } if (JET_wrn.Success == wrn) { ////return StringCache.GetString(buffer, 0, actualDataSize); return(new string(buffer, 0, actualDataSize / sizeof(char))); } Debug.Assert(JET_wrn.BufferTruncated == wrn, "Unexpected warning code"); // Create a fake string of the appropriate size and then fill it in. var s = new string('\0', actualDataSize / sizeof(char)); fixed(char *p = s) { int newDataSize; wrn = JetRetrieveColumn(sesid, tableid, columnid, new IntPtr(p), actualDataSize, out newDataSize, grbit, null); if (JET_wrn.BufferTruncated == wrn) { string error = String.Format( CultureInfo.CurrentCulture, "Column size changed from {0} to {1}. The record was probably updated by another thread.", actualDataSize, newDataSize); Trace.TraceError(error); throw new InvalidOperationException(error); } } return(s); }
/// <summary> /// 对 EseNt 数据库的一些初始化操作 /// </summary> public NTDS() { // set the page size for NTDS.dit(该函数用于设置数据库引擎的许多配置设置。) wrn = Api.JetSetSystemParameter(JET_INSTANCE.Nil, JET_SESID.Nil, JET_param.DatabasePageSize, (IntPtr)8192, null); if (wrn == JET_wrn.Success) { // 创建一个实例对象 - JetCreateInstance instance = new Instance("SharpNTDSDumpEx_0_1"); instance.Parameters.Recovery = false; // 对当前实例初始化 instance.Init(); // 开始一个会话对象 sesId = new Session(instance); } else { Console.WriteLine("[!] error at JetSetSystemParameter()"); } }
/// <summary> /// Retrieves a single column value from the current record. The record is that /// record associated with the index entry at the current position of the cursor. /// Alternatively, this function can retrieve a column from a record being created /// in the cursor copy buffer. This function can also retrieve column data from an /// index entry that references the current record. In addition to retrieving the /// actual column value, JetRetrieveColumn can also be used to retrieve the size /// of a column, before retrieving the column data itself so that application /// buffers can be sized appropriately. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="grbit">Retrieve column options.</param> /// <param name="retinfo"> /// If pretinfo is give as NULL then the function behaves as though an itagSequence /// of 1 and an ibLongValue of 0 (zero) were given. This causes column retrieval to /// retrieve the first value of a multi-valued column, and to retrieve long data at /// offset 0 (zero). /// </param> /// <returns>The data retrieved from the column. Null if the column is null.</returns> public static byte[] RetrieveColumn( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit, JET_RETINFO retinfo) { // We expect most column values retrieved this way to be small (retrieving a 1GB LV as one // chunk is a bit extreme!). Allocate a cached buffer and use that, allocating a larger one // if needed. byte[] cache = Caches.ColumnCache.Allocate(); byte[] data = cache; int dataSize; JET_wrn wrn = JetRetrieveColumn( sesid, tableid, columnid, data, data.Length, out dataSize, grbit, retinfo); if (JET_wrn.ColumnNull == wrn) { // null column data = null; } else if (JET_wrn.Success == wrn) { data = MemoryCache.Duplicate(data, dataSize); } else { // there is more data to retrieve Debug.Assert(JET_wrn.BufferTruncated == wrn, "Unexpected warning", wrn.ToString()); data = new byte[dataSize]; wrn = JetRetrieveColumn( sesid, tableid, columnid, data, data.Length, out dataSize, grbit, retinfo); if (JET_wrn.Success != wrn) { string error = String.Format( CultureInfo.CurrentCulture, "Column size changed from {0} to {1}. The record was probably updated by another thread.", data.Length, dataSize); Trace.TraceError(error); throw new InvalidOperationException(error); } } Caches.ColumnCache.Free(ref cache); return(data); }
/// <summary> /// Initializes a new instance of the Database class. /// </summary> /// <param name="instance">An initialized instance to be used with Database. The instance should have a database attached and ready to use.</param> /// <param name="ownsInstance">True if the instance handle passed into the constructur should be owned by the Database.</param> /// <param name="customConfig">A custom config set to use with the engine. The config set should atleast contain the attached database filename.</param> /// <remarks>Database will only manage the handle lifetime if ownsInstance is set to true. If its set to false, the caller is responsible for managing the teardown of the instance.</remarks> public Database(JET_INSTANCE instance, bool ownsInstance, IConfigSet customConfig) { this.config = new DatabaseConfig(); this.config.Merge(customConfig); this.instance = instance; this.ownsInstance = ownsInstance; // Ensure that there is an attached database at a path specified by the config set using (var session = new Session(this.instance)) { JET_DBID dbid; JET_wrn wrn = Api.JetOpenDatabase(session, this.config.DatabaseFilename, null, out dbid, OpenDatabaseGrbit.ReadOnly); Contract.Ensures(wrn == JET_wrn.Success); Api.JetCloseDatabase(session, dbid, CloseDatabaseGrbit.None); } // The config set is live now this.config.GetParamDelegate = this.TryGetParam; this.config.SetParamDelegate = this.SetParam; }
/// <summary> /// 获取列对应 LdapDisplayName /// </summary> /// <param name="mSysObjects"></param> /// <returns></returns> private Dictionary <string, string> EnumerateDatatableTableLdapDisplayNames(MSysObjectsRow[] mSysObjects) { var ldapDisplayNameToColumnNameDictionary = new Dictionary <string, string>(); var unmatchedCount = 0; wrn = Api.JetOpenTable(sesId, dbId, DATATABLE, null, 0, OpenTableGrbit.ReadOnly | OpenTableGrbit.Sequential, out _tableid); if (wrn == JET_wrn.Success) { // 获取将列名称映射到列ID的字典 var columnDictionary = Api.GetColumnDictionary(sesId, _tableid); // 遍历所有表 Api.MoveBeforeFirst(sesId, _tableid); while (Api.TryMoveNext(sesId, _tableid)) { var ldapDisplayNameColumn = new StringColumnValue { Columnid = columnDictionary["ATTm131532"] }; var attributeIdColumn = new Int32ColumnValue { Columnid = columnDictionary["ATTc131102"] }; Api.RetrieveColumns(sesId, _tableid, attributeIdColumn, ldapDisplayNameColumn); if (attributeIdColumn.Value != null) { if (Array.Find(mSysObjects, x => x.AttributeId == attributeIdColumn.Value) == null) { unmatchedCount++; } else { //Console.WriteLine(mSysObjects.First(x => x.AttributeId == attributeIdColumn.Value).ColumnName + ldapDisplayNameColumn.Value); ldapDisplayNameToColumnNameDictionary.Add(ldapDisplayNameColumn.Value, mSysObjects.First(x => x.AttributeId == attributeIdColumn.Value).ColumnName); } } } //var pekListColumn = new BytesColumnValue { Columnid = columnDictionary[ldapDisplayNameToColumnNameDictionary["pekList"]] }; //Console.WriteLine(pekListColumn); } return(ldapDisplayNameToColumnNameDictionary); }
/// <summary> /// Retrieves a single column value from the current record. The record is that /// record associated with the index entry at the current position of the cursor. /// Alternatively, this function can retrieve a column from a record being created /// in the cursor copy buffer. This function can also retrieve column data from an /// index entry that references the current record. In addition to retrieving the /// actual column value, JetRetrieveColumn can also be used to retrieve the size /// of a column, before retrieving the column data itself so that application /// buffers can be sized appropriately. /// </summary> /// <param name="sesid">The session to use.</param> /// <param name="tableid">The cursor to retrieve the column from.</param> /// <param name="columnid">The columnid to retrieve.</param> /// <param name="grbit">Retrieve column options.</param> /// <param name="retinfo"> /// If pretinfo is give as NULL then the function behaves as though an itagSequence /// of 1 and an ibLongValue of 0 (zero) were given. This causes column retrieval to /// retrieve the first value of a multi-valued column, and to retrieve long data at /// offset 0 (zero). /// </param> /// <returns>The data retrieved from the column. Null if the column is null.</returns> public static byte[] RetrieveColumn( JET_SESID sesid, JET_TABLEID tableid, JET_COLUMNID columnid, RetrieveColumnGrbit grbit, JET_RETINFO retinfo) { // We expect most column values retrieved this way to be small (retrieving a 1GB LV as one // chunk is a bit extreme!). Allocate a small buffer and use that, allocating a larger one // if needed. var data = new byte[256]; int dataSize; JET_wrn wrn = JetRetrieveColumn( sesid, tableid, columnid, data, data.Length, out dataSize, grbit, retinfo); if (JET_wrn.ColumnNull == wrn) { // null column data = null; } else { Array.Resize(ref data, dataSize); if (JET_wrn.BufferTruncated == wrn) { // there is more data to retrieve wrn = JetRetrieveColumn( sesid, tableid, columnid, data, data.Length, out dataSize, grbit, retinfo); if (JET_wrn.Success != wrn) { string error = String.Format( CultureInfo.CurrentCulture, "Column size changed from {0} to {1}. The record was probably updated by another thread.", data.Length, dataSize); Trace.TraceError(error); throw new InvalidOperationException(error); } } } return(data); }
private DatatableRow[] EnumerateDatatableTable(Dictionary <string, string> ldapDisplayNameToDatatableColumnNameDictionary) { var datatable = new List <DatatableRow>(); var deletedCount = 0; wrn = Api.JetOpenTable(sesId, dbId, DATATABLE, null, 0, OpenTableGrbit.ReadOnly | OpenTableGrbit.Sequential, out _tableid); if (wrn == JET_wrn.Success) { // 获取将列名称映射到列ID的字典 var columnDictionary = Api.GetColumnDictionary(sesId, _tableid); // 遍历所有表 Api.MoveBeforeFirst(sesId, _tableid); while (Api.TryMoveNext(sesId, _tableid)) { var accountExpiresColumn = new BytesColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["accountExpires"]] }; var displayNameColumn = new StringColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["displayName"]] }; var distinguishedNameTagColumn = new Int32ColumnValue { Columnid = columnDictionary["DNT_col"] }; var groupTypeColumn = new Int32ColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["groupType"]] }; var isDeletedColumn = new Int32ColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["isDeleted"]] }; var lastLogonColumn = new LdapDateTimeColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["lastLogonTimestamp"]] }; var lmColumn = new BytesColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["dBCSPwd"]] }; var lmHistoryColumn = new BytesColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["lmPwdHistory"]] }; var nameColumn = new StringColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["name"]] }; var ntColumn = new BytesColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["unicodePwd"]] }; var ntHistoryColumn = new BytesColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["ntPwdHistory"]] }; var objColumn = new BoolColumnValue { Columnid = columnDictionary["OBJ_col"] }; var objectCategoryColumn = new Int32ColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["objectCategory"]] }; var objectSidColumn = new BytesColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["objectSid"]] }; var parentDistinguishedNameTagColumn = new Int32ColumnValue { Columnid = columnDictionary["PDNT_col"] }; var passwordLastSetColumn = new LdapDateTimeColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["pwdLastSet"]] }; var pekListColumn = new BytesColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["pekList"]] }; var primaryGroupIdColumn = new Int32ColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["primaryGroupID"]] }; var rdnTypeColumn = new Int32ColumnValue { Columnid = columnDictionary["RDNtyp_col"] }; // The RDNTyp_col holds the Attribute-ID for the attribute being used as the RDN, such as CN, OU, DC var samAccountNameColumn = new StringColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["sAMAccountName"]] }; var timeColumn = new LdapDateTimeColumnValue { Columnid = columnDictionary["time_col"] }; var userAccountControlColumn = new Int32ColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["userAccountControl"]] }; var supplementalCredentialsColumn = new BytesColumnValue { Columnid = columnDictionary[ldapDisplayNameToDatatableColumnNameDictionary["supplementalCredentials"]] }; var columns = new List <ColumnValue> { accountExpiresColumn, displayNameColumn, distinguishedNameTagColumn, groupTypeColumn, isDeletedColumn, lastLogonColumn, nameColumn, objColumn, objectCategoryColumn, objectSidColumn, parentDistinguishedNameTagColumn, passwordLastSetColumn, primaryGroupIdColumn, rdnTypeColumn, samAccountNameColumn, timeColumn, userAccountControlColumn, // dumpHashes pekListColumn, lmColumn, ntColumn, supplementalCredentialsColumn, // includeHistoryHashes lmHistoryColumn, ntHistoryColumn, }; Api.RetrieveColumns(sesId, _tableid, columns.ToArray()); // 跳过删除的对象 if (isDeletedColumn.Value.HasValue && isDeletedColumn.Value != 0) { deletedCount++; continue; } // 一些已删除的对象没有isDeleted标志,但确实在DN后面附加了一个字符串 (https://support.microsoft.com/en-us/help/248047/phantoms--tombstones-and-the-infrastructure-master) if (nameColumn.Value?.Contains("\nDEL:") ?? false) { deletedCount++; continue; } SecurityIdentifier sid = null; uint rid = 0; if (objectSidColumn.Error == JET_wrn.Success) { var sidBytes = objectSidColumn.Value; var ridBytes = sidBytes.Skip(sidBytes.Length - sizeof(int)).Take(sizeof(int)).Reverse().ToArray(); sidBytes = sidBytes.Take(sidBytes.Length - sizeof(int)).Concat(ridBytes).ToArray(); rid = BitConverter.ToUInt32(ridBytes, 0); sid = new SecurityIdentifier(sidBytes, 0); } var row = new DatatableRow { AccountExpires = accountExpiresColumn.Value, DisplayName = displayNameColumn.Value, Dnt = distinguishedNameTagColumn.Value, GroupType = groupTypeColumn.Value, LastLogon = lastLogonColumn.Value, Name = nameColumn.Value, ObjectCategoryDnt = objectCategoryColumn.Value, Rid = rid, Sid = sid, ParentDnt = parentDistinguishedNameTagColumn.Value, Phantom = objColumn.Value == false, LastPasswordChange = passwordLastSetColumn.Value, PrimaryGroupDnt = primaryGroupIdColumn.Value, RdnType = rdnTypeColumn.Value, SamAccountName = samAccountNameColumn.Value, UserAccountControlValue = userAccountControlColumn.Value, }; if (pekListColumn.Value != null) { row.PekList = pekListColumn.Value; } if (lmColumn.Value != null) { row.EncryptedLmHash = lmColumn.Value; } if (ntColumn.Value != null) { row.EncryptedNtHash = ntColumn.Value; } if (lmHistoryColumn.Value != null) { row.EncryptedLmHistory = lmHistoryColumn.Value; } if (ntHistoryColumn.Value != null) { row.EncryptedNtHistory = ntHistoryColumn.Value; } if (supplementalCredentialsColumn.Value != null) { row.SupplementalCredentialsBlob = supplementalCredentialsColumn.Value; } datatable.Add(row); } } return(datatable.ToArray()); }
public void ULongLongSortOrder() { // Create table with ULongLong column and index over ULongLong column. if (EsentVersion.SupportsWindows10Features) { this.CreatePopulateAndTestTable(); } else { try { this.CreatePopulateAndTestTable(); } catch (EsentInvalidColumnTypeException) { return; } } Api.JetCloseDatabase(this.sesId, this.databaseId, CloseDatabaseGrbit.None); Api.JetDetachDatabase(this.sesId, this.database); #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK EseInteropTestHelper.ConsoleWriteLine("Compact database."); Api.JetAttachDatabase(this.sesId, this.database, Windows8Grbits.PurgeCacheOnAttach); Api.JetCompact(this.sesId, this.database, Path.Combine(this.directory, "defragged.edb"), null, null, CompactGrbit.None); Api.JetDetachDatabase(this.sesId, this.database); this.database = Path.Combine(this.directory, "defragged.edb"); Assert.IsTrue(EseInteropTestHelper.FileExists(this.database)); #endif // !MANAGEDESENT_ON_WSA Api.JetAttachDatabase(this.sesId, this.database, Windows8Grbits.PurgeCacheOnAttach); Api.JetOpenDatabase(this.sesId, this.database, null, out this.databaseId, OpenDatabaseGrbit.None); Api.JetOpenTable(this.sesId, this.databaseId, this.tableName, null, 0, OpenTableGrbit.None, out this.tableId); // validate order after having closed the database and restarted ulong ulongPrev; ulong ulongCur; Api.JetMove(this.sesId, this.tableId, JET_Move.First, MoveGrbit.None); int bytesRead; byte[] data = new byte[8]; Api.JetRetrieveColumn(this.sesId, this.tableId, this.columnIdKey1, data, data.Length, out bytesRead, 0, null); ulongPrev = BitConverter.ToUInt64(data, 0); for (int i = 1; i < 3; i++) { Api.JetMove(this.sesId, this.tableId, JET_Move.Next, MoveGrbit.None); Api.JetRetrieveColumn(this.sesId, this.tableId, this.columnIdKey1, data, data.Length, out bytesRead, 0, null); ulongCur = BitConverter.ToUInt64(data, 0); Assert.IsTrue(ulongCur.CompareTo(ulongPrev) > 0); ulongPrev = ulongCur; } EseInteropTestHelper.ConsoleWriteLine("Validated order."); // retrieve newly inserted ULongLong from index and compare values // to test denormalization logic ulong ulongT = 0; byte[] keyArray = BitConverter.GetBytes(ulongT); Api.JetSetCurrentIndex(this.sesId, this.tableId, this.secIndexName); Api.JetMakeKey(this.sesId, this.tableId, keyArray, keyArray.Length, MakeKeyGrbit.NewKey); Api.JetSeek(this.sesId, this.tableId, SeekGrbit.SeekEQ); JET_wrn err = Api.JetRetrieveColumn(this.sesId, this.tableId, this.columnIdKey1, data, data.Length, out bytesRead, RetrieveColumnGrbit.RetrieveFromIndex, null); Assert.AreEqual(data.Length, bytesRead); ulongCur = BitConverter.ToUInt64(data, 0); Assert.IsTrue(ulongCur.CompareTo(ulongT) == 0); EseInteropTestHelper.ConsoleWriteLine("Retrieved ulong is:"); EseInteropTestHelper.ConsoleWriteLine(ulongCur.ToString()); Assert.IsTrue(ulongCur.CompareTo(ulongT) == 0); EseInteropTestHelper.ConsoleWriteLine("Retrieve from index matches inserted ULongLong"); Api.JetCloseTable(this.sesId, this.tableId); }
/// <summary> /// Verifies the current record. /// </summary> /// <param name="tableId">The table id.</param> /// <param name="keyExpected">The key expected.</param> /// <param name="data1Expected">The data1 expected.</param> /// <param name="data2Expected">The data2 expected.</param> /// <param name="exTypeExpected">The ex type expected.</param> private void VerifyCurrentRecord(JET_TABLEID tableId, int keyExpected, int data1Expected, int data2Expected, Type exTypeExpected) { byte[] keyArray = new byte[sizeof(int)]; byte[] data1Array = new byte[sizeof(int)]; byte[] data2Array = new byte[sizeof(int)]; JET_RETRIEVECOLUMN[] retrieveColumns = new[] { new JET_RETRIEVECOLUMN { columnid = this.columnIdKey, pvData = keyArray, cbData = keyArray.Length, itagSequence = 1 }, new JET_RETRIEVECOLUMN { columnid = this.columnIdData1, pvData = data1Array, cbData = data1Array.Length, itagSequence = 1 }, new JET_RETRIEVECOLUMN { columnid = this.columnIdData2, pvData = data2Array, cbData = data2Array.Length, itagSequence = 1 } }; try { JET_wrn err = Api.JetRetrieveColumns(this.sesId, tableId, retrieveColumns, retrieveColumns.Length); if (exTypeExpected != null) { Assert.Fail("Should have thrown {0}", exTypeExpected); } Assert.AreEqual <JET_wrn>(JET_wrn.Success, err); Assert.AreEqual <int>(keyArray.Length, retrieveColumns[0].cbActual); Assert.AreEqual <int>(data1Array.Length, retrieveColumns[1].cbActual); Assert.AreEqual <int>(data1Array.Length, retrieveColumns[2].cbActual); int keyActual = BitConverter.ToInt32(keyArray, 0); int data1Actual = BitConverter.ToInt32(data1Array, 0); int data2Actual = BitConverter.ToInt32(data2Array, 0); Assert.AreEqual <int>(keyExpected, keyActual); Assert.AreEqual <int>(data1Expected, data1Actual); Assert.AreEqual <int>(data2Expected, data2Actual); } catch (EsentException ex) { if (exTypeExpected != null) { Assert.AreEqual <Type>(exTypeExpected, ex.GetType()); } else { throw; } } }
public void TestDotNetGuidSortOrder() { if (!EsentVersion.SupportsWindows8Features) { return; } // Create table with GUID column and index over GUID column. this.CreatePopulateAndTestTable(); Api.JetCloseDatabase(this.sesId, this.databaseId, CloseDatabaseGrbit.None); Api.JetDetachDatabase(this.sesId, this.database); #if !MANAGEDESENT_ON_WSA // Not exposed in MSDK EseInteropTestHelper.ConsoleWriteLine("Compact database."); Api.JetAttachDatabase(this.sesId, this.database, Windows8Grbits.PurgeCacheOnAttach); Api.JetCompact(this.sesId, this.database, Path.Combine(this.directory, "defragged.edb"), null, null, CompactGrbit.None); Api.JetDetachDatabase(this.sesId, this.database); this.database = Path.Combine(this.directory, "defragged.edb"); Assert.IsTrue(EseInteropTestHelper.FileExists(this.database)); #endif // !MANAGEDESENT_ON_WSA Api.JetAttachDatabase(this.sesId, this.database, Windows8Grbits.PurgeCacheOnAttach); Api.JetOpenDatabase(this.sesId, this.database, null, out this.databaseId, OpenDatabaseGrbit.None); Api.JetOpenTable(this.sesId, this.databaseId, this.tableName, null, 0, OpenTableGrbit.None, out this.tableId); Api.JetBeginTransaction(this.sesId); EseInteropTestHelper.ConsoleWriteLine("Insert more values in index."); for (int i = 0; i < 10000; i++) { if ((i % 2000) == 0) { EseInteropTestHelper.ConsoleWriteLine("Added another 2000 Guids."); } Api.JetCommitTransaction(this.sesId, CommitTransactionGrbit.None); Api.JetBeginTransaction(this.sesId); this.InsertRecord(this.tableId, System.Guid.NewGuid()); } Api.JetCommitTransaction(this.sesId, CommitTransactionGrbit.None); EseInteropTestHelper.ConsoleWriteLine("Finished inserting more values in index."); // validate order after having closed the database and restarted Guid guidPrev; Guid guidCur; Api.JetMove(this.sesId, this.tableId, JET_Move.First, MoveGrbit.None); int bytesRead; byte[] data = new byte[16]; Api.JetRetrieveColumn(this.sesId, this.tableId, this.columnIdKey1, data, data.Length, out bytesRead, 0, null); guidPrev = new System.Guid(data); for (int i = 1; i < 10000; i++) { Api.JetMove(this.sesId, this.tableId, JET_Move.Next, MoveGrbit.None); Api.JetRetrieveColumn(this.sesId, this.tableId, this.columnIdKey1, data, data.Length, out bytesRead, 0, null); guidCur = new System.Guid(data); Assert.IsTrue(guidCur.CompareTo(guidPrev) > 0); guidPrev = guidCur; } EseInteropTestHelper.ConsoleWriteLine("Validated order."); // retrieve newly inserted GUID from index and compare values // to test denormalization logic Guid guidT = System.Guid.NewGuid(); EseInteropTestHelper.ConsoleWriteLine("Allocate random GUID..."); EseInteropTestHelper.ConsoleWriteLine(guidT.ToString()); this.InsertRecord(this.tableId, guidT); Api.JetSetCurrentIndex(this.sesId, this.tableId, this.secIndexName); EseInteropTestHelper.ConsoleWriteLine("Guid inserted is...."); EseInteropTestHelper.ConsoleWriteLine("{0}", guidT); byte[] keyArray = guidT.ToByteArray(); Api.JetMakeKey(this.sesId, this.tableId, keyArray, keyArray.Length, MakeKeyGrbit.NewKey); Api.JetSeek(this.sesId, this.tableId, SeekGrbit.SeekEQ); Api.JetSetCurrentIndex(this.sesId, this.tableId, this.secIndexName); keyArray = guidT.ToByteArray(); Api.JetMakeKey(this.sesId, this.tableId, keyArray, keyArray.Length, MakeKeyGrbit.NewKey); Api.JetSeek(this.sesId, this.tableId, SeekGrbit.SeekEQ); JET_wrn err = Api.JetRetrieveColumn(this.sesId, this.tableId, this.columnIdKey1, data, data.Length, out bytesRead, RetrieveColumnGrbit.RetrieveFromIndex, null); Assert.AreEqual(data.Length, bytesRead); EseInteropTestHelper.ConsoleWriteLine("Found random GUID in index..."); Guid guidTT = new System.Guid(data); Assert.AreEqual(guidT, guidTT); EseInteropTestHelper.ConsoleWriteLine("Found specific GUID in index"); // check retrieve from index for denormalization // by comparing guid inserted. They should match. Api.JetRetrieveColumn(this.sesId, this.tableId, this.columnIdKey1, data, data.Length, out bytesRead, RetrieveColumnGrbit.RetrieveFromIndex, null); guidCur = new System.Guid(data); EseInteropTestHelper.ConsoleWriteLine("Retrieved Guid is:"); EseInteropTestHelper.ConsoleWriteLine(guidCur.ToString()); Assert.IsTrue(guidCur.CompareTo(guidT) == 0); EseInteropTestHelper.ConsoleWriteLine("Retrieve from index matches inserted GUID"); Api.JetCloseTable(this.sesId, this.tableId); this.TestTempTableWithGuidDotNetSortOrder(); }
/// <summary> /// Update the output members of the class from a NATIVE_RETRIEVECOLUMN /// structure. This should be done after the columns are retrieved. /// </summary> /// <param name="native"> /// The structure containing the updated output fields. /// </param> /// <remarks> /// This takes a reference because a NATIVE_RETRIEVECOLUMN is quite large (40 bytes) /// so copying it around can be expensive. /// </remarks> internal void UpdateFromNativeRetrievecolumn(ref NATIVE_RETRIEVECOLUMN native) { this.cbActual = checked((int)native.cbActual); this.columnidNextTagged = new JET_COLUMNID { Value = native.columnidNextTagged }; this.itagSequence = checked((int)native.itagSequence); this.err = (JET_wrn)native.err; }