public void TestStringColumnValueToString() { var instance = new StringColumnValue { Value = "foo" }; Assert.AreEqual("foo", instance.ToString()); }
public void TestStringValueAsObject() { var instance = new StringColumnValue { Value = "hello" }; Assert.AreEqual("hello", instance.ValueAsObject); }
private static IReadOnlyDictionary <string, string> EnumerateDatatableTableLdapDisplayNames(JetDb db, MSysObjectsRow[] mSysObjects) { Stopwatch stopwatch = null; if (ShowDebugOutput) { ConsoleEx.WriteDebug($"Called: {nameof(NtdsAudit)}::{nameof(EnumerateDatatableTableLdapDisplayNames)}"); stopwatch = new Stopwatch(); stopwatch.Start(); } var ldapDisplayNameToColumnNameDictionary = new Dictionary <string, string>(); var unmatchedCount = 0; using (var table = db.OpenJetDbTable(DATATABLE)) { // Get a dictionary mapping column names to column ids var columnDictionary = table.GetColumnDictionary(); // Loop over the table table.MoveBeforeFirst(); while (table.TryMoveNext()) { var ldapDisplayNameColumn = new StringColumnValue { Columnid = columnDictionary["ATTm131532"] }; var attributeIdColumn = new Int32ColumnValue { Columnid = columnDictionary["ATTc131102"] }; table.RetrieveColumns(attributeIdColumn, ldapDisplayNameColumn); if (attributeIdColumn.Value != null) { if (Array.Find(mSysObjects, x => x.AttributeId == attributeIdColumn.Value) == null) { unmatchedCount++; } else { ldapDisplayNameToColumnNameDictionary.Add(ldapDisplayNameColumn.Value, mSysObjects.First(x => x.AttributeId == attributeIdColumn.Value).ColumnName); } } } } if (ShowDebugOutput) { ConsoleEx.WriteDebug($" Failed to match {unmatchedCount} LDAP display names to datatable column names"); ConsoleEx.WriteDebug($" Matched {ldapDisplayNameToColumnNameDictionary.Count} LDAP display names to datatable column names"); stopwatch.Stop(); ConsoleEx.WriteDebug($" Completed in {stopwatch.Elapsed}"); } return(new ReadOnlyDictionary <string, string>(ldapDisplayNameToColumnNameDictionary)); }
public void TestStringLength() { var instance = new StringColumnValue { Value = "hello" }; Assert.AreEqual("hello".Length * 2, instance.Length); instance = new StringColumnValue { Value = null }; Assert.AreEqual(0, instance.Length); }
/// <summary> /// Retrieve columns using the basic RetrieveColumns API. /// </summary> private void RetrieveWithRetrieveColumns() { var boolColumn = new BoolColumnValue { Columnid = this.columnidDict["boolean"] }; var int32Column = new Int32ColumnValue { Columnid = this.columnidDict["int32"] }; var int64Column = new Int64ColumnValue { Columnid = this.columnidDict["int64"] }; var guidColumn = new GuidColumnValue { Columnid = this.columnidDict["guid"] }; var stringColumn = new StringColumnValue { Columnid = this.columnidDict["unicode"] }; var retrievecolumns = new ColumnValue[] { boolColumn, int32Column, int64Column, guidColumn, stringColumn, }; for (int i = 0; i < NumRetrieves; ++i) { Api.JetBeginTransaction(this.session); Api.RetrieveColumns(this.session, this.tableid, retrievecolumns); bool actualBool = (bool)boolColumn.Value; int actualInt32 = (int)int32Column.Value; long actualInt64 = (long)int64Column.Value; Guid actualGuid = (Guid)guidColumn.Value; string actualString = stringColumn.Value; Assert.AreEqual(this.expectedBool, actualBool); Assert.AreEqual(this.expectedInt32, actualInt32); Assert.AreEqual(this.expectedInt64, actualInt64); Assert.AreEqual(this.expectedGuid, actualGuid); Assert.AreEqual(this.expectedString, actualString); Api.JetCommitTransaction(this.session, CommitTransactionGrbit.None); } }
private void InitColumns() { // IDictionary<string, JET_COLUMNID> columnids = Api.GetColumnDictionary(_jetSession, _jetCursor); _colIdHeaderId = Api.GetTableColumnid(_jetSession, _jetCursor, _colNameHeaderId); _colIdEntityCount = Api.GetTableColumnid(_jetSession, _jetCursor, _colNameEntityCount); _colIdFileVersion = Api.GetTableColumnid(_jetSession, _jetCursor, _colNameFileVersion); _colIdHeaderData = Api.GetTableColumnid(_jetSession, _jetCursor, _colNameHeaderData); _colValHeaderId = new Int32ColumnValue { Columnid = _colIdHeaderId }; _colValEntityCount = new Int64ColumnValue { Columnid = _colIdEntityCount }; _colValFileVersion = new StringColumnValue { Columnid = _colIdFileVersion }; _colValHeaderData = new BytesColumnValue { Columnid = _colIdHeaderData }; }
/// <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); }
private static DatatableRow[] EnumerateDatatableTable(JetDb db, IReadOnlyDictionary <string, string> ldapDisplayNameToDatatableColumnNameDictionary, bool dumpHashes, bool includeHistoryHashes) { Stopwatch stopwatch = null; if (ShowDebugOutput) { ConsoleEx.WriteDebug($"Called: {nameof(NtdsAudit)}::{nameof(EnumerateDatatableTable)}"); stopwatch = new Stopwatch(); stopwatch.Start(); } var datatable = new List <DatatableRow>(); var deletedCount = 0; using (var table = db.OpenJetDbTable(DATATABLE)) { // Get a dictionary mapping column names to column ids var columnDictionary = table.GetColumnDictionary(); // Loop over the table table.MoveBeforeFirst(); while (table.TryMoveNext()) { 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, }; if (dumpHashes) { columns.Add(pekListColumn); columns.Add(lmColumn); columns.Add(ntColumn); columns.Add(supplementalCredentialsColumn); if (includeHistoryHashes) { columns.Add(lmHistoryColumn); columns.Add(ntHistoryColumn); } } table.RetrieveColumns(columns.ToArray()); // Skip deleted objects if (isDeletedColumn.Value.HasValue && isDeletedColumn.Value != 0) { deletedCount++; continue; } // Some deleted objects do not have the isDeleted flag but do have a string appended to the 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 (dumpHashes) { 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 (includeHistoryHashes) { 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); } } if (ShowDebugOutput) { ConsoleEx.WriteDebug($" Skipped {deletedCount} deleted objects"); ConsoleEx.WriteDebug($" Enumerated {datatable.Count} objects"); stopwatch.Stop(); ConsoleEx.WriteDebug($" Completed in {stopwatch.Elapsed}"); } return(datatable.ToArray()); }
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 HowDoIDealWithMultivalues() { JET_SESID sesid = this.testSession; JET_DBID dbid = this.testDbid; JET_TABLEID tableid; JET_COLUMNDEF columndef = new JET_COLUMNDEF(); JET_COLUMNID tagColumn; Api.JetCreateTable(sesid, dbid, "table", 0, 100, out tableid); // Create the column. Any column can be multivalued. Using // ColumndefGrbit controls how the column is indexed. columndef.coltyp = JET_coltyp.LongText; columndef.cp = JET_CP.Unicode; columndef.grbit = ColumndefGrbit.ColumnMultiValued; Api.JetAddColumn(sesid, tableid, "tags", columndef, null, 0, out tagColumn); // Create the index. There will be one entry in the index for each // instance of the multivalued column. const string IndexKey = "+tags\0\0"; Api.JetCreateIndex(sesid, tableid, "tagsindex", CreateIndexGrbit.None, IndexKey, IndexKey.Length, 100); Api.JetBeginTransaction(sesid); // Now insert a record. An ESENT column can have multiple instances (multivalues) // inside the same record. Each multivalue is identified by an itag, the first itag // in a sequence is 1. Api.JetPrepareUpdate(sesid, tableid, JET_prep.Insert); // With no JET_SETINFO specified, itag 1 will be set. byte[] data = Encoding.Unicode.GetBytes("foo"); Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.None, null); // Set a column with a setinfo. The itagSequence in the setinfo will be 0, which // means the value will be added to the collection of values, i.e. the column will // have two instances, "foo" and "bar". JET_SETINFO setinfo = new JET_SETINFO(); data = Encoding.Unicode.GetBytes("bar"); Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.None, setinfo); // Add a third instance, explicitly setting the itagSequence data = Encoding.Unicode.GetBytes("baz"); setinfo.itagSequence = 4; Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.None, setinfo); // Add a duplicate value, checking for uniqueness data = Encoding.Unicode.GetBytes("foo"); setinfo.itagSequence = 0; try { Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.UniqueMultiValues, setinfo); Assert.Fail("Expected an EsentErrorException"); } catch (EsentErrorException ex) { Assert.AreEqual(JET_err.MultiValuedDuplicate, ex.Error); } Api.JetUpdate(sesid, tableid); // Find the record. We can seek for any column instance. Api.JetSetCurrentIndex(sesid, tableid, "tagsindex"); Api.MakeKey(sesid, tableid, "bar", Encoding.Unicode, MakeKeyGrbit.NewKey); Assert.IsTrue(Api.TrySeek(sesid, tableid, SeekGrbit.SeekEQ)); // Retrieve the number of column instances. This can be done with JetRetrieveColumns by setting // itagSequence to 0. JET_RETRIEVECOLUMN retrievecolumn = new JET_RETRIEVECOLUMN(); retrievecolumn.columnid = tagColumn; retrievecolumn.itagSequence = 0; Api.JetRetrieveColumns(sesid, tableid, new[] { retrievecolumn }, 1); EseInteropTestHelper.ConsoleWriteLine("Api.JetRetrieveColumns retrieved {0} multi-values.", retrievecolumn.itagSequence); Assert.AreEqual(3, retrievecolumn.itagSequence); // Use Api.RetrieveColumns() to get the number of multi-values. StringColumnValue retrieveStringColumnValue = new StringColumnValue(); retrieveStringColumnValue.Columnid = tagColumn; retrieveStringColumnValue.ItagSequence = 0; Api.RetrieveColumns(sesid, tableid, retrieveStringColumnValue); EseInteropTestHelper.ConsoleWriteLine("Api.RetrieveColumns retrieved {0} multi-values.", retrieveStringColumnValue.ItagSequence); Assert.AreEqual(3, retrieveStringColumnValue.ItagSequence); // Retrieve all the columns for (int itag = 1; itag <= retrievecolumn.itagSequence; ++itag) { JET_RETINFO retinfo = new JET_RETINFO { itagSequence = itag }; byte[] rawBytes = Api.RetrieveColumn(sesid, tableid, tagColumn, RetrieveColumnGrbit.None, retinfo); string s = Encoding.Unicode.GetString(rawBytes, 0, rawBytes.Length); EseInteropTestHelper.ConsoleWriteLine("{0}: {1}", itag, s); } // Update the record Api.JetPrepareUpdate(sesid, tableid, JET_prep.Replace); // With no JET_SETINFO specified, itag 1 will be set, overwriting the existing value. data = Encoding.Unicode.GetBytes("qux"); Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.None, null); // Set an instance to null to delete it. setinfo.itagSequence = 2; Api.JetSetColumn(sesid, tableid, tagColumn, null, 0, SetColumnGrbit.None, setinfo); // Removing itag 2 moved the other itags down (i.e. itag 3 became itag 2). // Overwrite itag 2. data = Encoding.Unicode.GetBytes("xyzzy"); setinfo.itagSequence = 2; Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.None, setinfo); // Now add a new instance by setting itag 0. This instance will go at the end. data = Encoding.Unicode.GetBytes("flob"); setinfo.itagSequence = 0; Api.JetSetColumn(sesid, tableid, tagColumn, data, data.Length, SetColumnGrbit.None, setinfo); Api.JetUpdate(sesid, tableid); // Retrieve the number of column instances again. retrievecolumn.itagSequence = 0; Api.JetRetrieveColumns(sesid, tableid, new[] { retrievecolumn }, 1); // Retrieve all the columns for (int itag = 1; itag <= retrievecolumn.itagSequence; ++itag) { JET_RETINFO retinfo = new JET_RETINFO { itagSequence = itag }; byte[] rawBytes = Api.RetrieveColumn(sesid, tableid, tagColumn, RetrieveColumnGrbit.None, retinfo); string s = Encoding.Unicode.GetString(rawBytes, 0, rawBytes.Length); EseInteropTestHelper.ConsoleWriteLine("{0}: {1}", itag, s); } Api.JetCommitTransaction(sesid, CommitTransactionGrbit.LazyFlush); }