├── .gitattributes
├── .gitignore
├── Directory.Build.props
├── DitExplorer.Core
├── DitExplorer.Core.csproj
├── IAttributeSchema.cs
├── IAttributeSyntax.cs
├── IClassSchema.cs
├── IDirectory.cs
├── IDirectoryObject.cs
├── ISchemaObject.cs
└── MultiValue.cs
├── DitExplorer.EseInterop
├── DitExplorer.EseInterop.csproj
├── JetColumnInfo.cs
├── JetColumnType.cs
├── JetCursor.cs
├── JetDatabase.cs
├── JetIndexColumnInfo.cs
├── JetInstance.cs
├── JetSession.cs
├── JetTableInfo.cs
├── Messages.Designer.cs
├── Messages.resx
└── esent.dll
├── DitExplorer.Extensibility
├── DitExplorer.Extensibility.csproj
├── DitExtensionManifest.xsd
├── IDirectoryNode.cs
├── IItemActionContext.cs
├── IItemActionRegistry.cs
└── ItemAction.cs
├── DitExplorer.Ntds
├── ADInstanceType.cs
├── ADSearchFlags.cs
├── ArgumentHelper.cs
├── AttributeSyntax.cs
├── AttributeSystemFlags.cs
├── CollectionHelpers.cs
├── DirectoryObjectNotFoundException.cs
├── DitExplorer.Ntds.csproj
├── Messages.Designer.cs
├── Messages.resx
├── NtdsAttributeSchema.cs
├── NtdsClassSchema.cs
├── NtdsDirectory.cs
├── NtdsDirectoryEnumerator.cs
├── NtdsDirectoryLinkEnumerator.cs
├── NtdsDirectoryObject.cs
├── NtdsDirectoryObjectReference.cs
└── NtdsSchemaObject.cs
├── DitExplorer.UI.Core
├── AssemblyInfo.cs
├── Behaviors
│ ├── CheckedList.cs
│ ├── CommandFrame.cs
│ ├── ContextCommandService.cs
│ ├── FlexGrid.cs
│ ├── FocusHelper.cs
│ └── ListViewSorting.cs
├── CollectionHelpers.cs
├── DitExplorer.UI.Core.csproj
├── ICommandHandler.cs
├── IContextCommandProvider.cs
├── Messages.Designer.cs
├── Messages.resx
├── Notifier.cs
└── ViewModel.cs
├── DitExplorer.Utilities
├── DitExplorer.Utilities.csproj
├── HexHelper.cs
└── TabularTextWriter.cs
├── DitExplorer.WpfApp
├── Actions
│ └── ViewMembersAction.cs
├── App.xaml
├── App.xaml.cs
├── AppViewModel.cs
├── ColumnChooserViewModel.cs
├── ColumnChooserWindow.xaml
├── ColumnChooserWindow.xaml.cs
├── ContextCommandInfo.cs
├── Controls
│ └── MultiValueCell.cs
├── DatabaseRecord.cs
├── DatabaseSchemaViewModel.cs
├── DatabaseSchemaWindow.xaml
├── DatabaseSchemaWindow.xaml.cs
├── DirectoryListViewModel.cs
├── DirectoryNode.cs
├── DirectoryPropertyDescriptor.cs
├── DirectoryView.cs
├── DitExplorer.UI.WpfApp.csproj
├── ItemActionCommand.cs
├── ItemActivation.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Messages.Designer.cs
├── Messages.resx
├── MyCommands.cs
├── ObjectInspectorViewModel.cs
├── ObjectInspectorWindow.xaml
├── ObjectInspectorWindow.xaml.cs
├── ObjectSearchViewModel.cs
├── Properties
│ └── AssemblyInfo.cs
├── SearchWindow.xaml
├── SearchWindow.xaml.cs
├── Services
│ ├── Interface1.cs
│ └── ItemActionRegistry.cs
└── themes
│ └── generic.xaml
├── DitExplorer.sln
├── Extensions
└── DitExplorer.CredentialExtraction
│ ├── AssemblyInfo.cs
│ ├── Credential.cs
│ ├── CredentialExtractor.ditextmanifest
│ ├── CredentialExtractorViewModel.cs
│ ├── CredentialExtractorWindow.xaml
│ ├── CredentialExtractorWindow.xaml.cs
│ ├── DitExplorer.CredentialExtraction.csproj
│ ├── ExtractCredentialsAction.cs
│ ├── Messages.Designer.cs
│ ├── Messages.resx
│ ├── SidExtensions.cs
│ └── Structs.cs
├── LICENSE.txt
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | DIT Explorer
4 | TrustedSec
5 | Alex Ball
6 | DIT Explorer
7 | Copyright © 2025
8 |
9 | https://www.github.com/trustedsec/DitExplorer
10 | https://www.github.com/trustedsec/DitExplorer
11 |
12 |
--------------------------------------------------------------------------------
/DitExplorer.Core/DitExplorer.Core.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | DitExplorer
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/DitExplorer.Core/IAttributeSchema.cs:
--------------------------------------------------------------------------------
1 | namespace DitExplorer;
2 |
3 | public interface IAttributeSchema : ISchemaObject
4 | {
5 | IAttributeSyntax? Syntax { get; }
6 | bool IsSingleValued { get; }
7 | bool IsLink { get; }
8 | }
--------------------------------------------------------------------------------
/DitExplorer.Core/IAttributeSyntax.cs:
--------------------------------------------------------------------------------
1 | namespace DitExplorer;
2 |
3 | public interface IAttributeSyntax
4 | {
5 | Type? AttributeType { get; }
6 | bool CanRetrieveValue { get; }
7 | }
--------------------------------------------------------------------------------
/DitExplorer.Core/IClassSchema.cs:
--------------------------------------------------------------------------------
1 | namespace DitExplorer;
2 |
3 | public interface IClassSchema : ISchemaObject
4 | {
5 | IClassSchema? Superclass { get; }
6 | bool HasAttribute(string ldapName);
7 | IAttributeSchema[] GetAttributes(bool includeBaseClasses);
8 | }
--------------------------------------------------------------------------------
/DitExplorer.Core/IDirectory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace DitExplorer;
8 |
9 | ///
10 | /// Exposes functionality for interacting with a directory.
11 | ///
12 | public interface IDirectory : IDisposable
13 | {
14 | ///
15 | /// Gets the root domain of the directory.
16 | ///
17 | IDirectoryObject RootDomain { get; }
18 |
19 | ///
20 | /// Gets a class corresponding to an encoded governsID value.
21 | ///
22 | /// Prefix-encoded governsID
23 | /// An with
24 | /// No class found with
25 | IClassSchema GetClassByGovernsId(int governsId);
26 | ///
27 | /// Gets a list of all object classes.
28 | ///
29 | /// An array of objects, one for
30 | /// each class defined in the directory..
31 | IClassSchema[] GetClassSchemas();
32 | ///
33 | /// Gets a list of all attribute in the schemae.
34 | ///
35 | /// An array of objects.
36 | IAttributeSchema[] GetAllAttributeSchemas();
37 | ///
38 | /// Gets an attribute by its LDAP name.
39 | ///
40 | /// LDAP name of attribute
41 | /// The with the LDAP name , if found;
42 | /// otherwise, .
43 | IAttributeSchema? TryGetAttributeByLdapName(string name);
44 | }
45 |
--------------------------------------------------------------------------------
/DitExplorer.Core/IDirectoryObject.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace DitExplorer;
3 |
4 | ///
5 | /// Provides functionality for interacting with an object in a directory.
6 | ///
7 | public interface IDirectoryObject
8 | {
9 | ///
10 | /// Gets the name of the object.
11 | ///
12 | public string Name { get; }
13 | string DistinguishedName { get; }
14 | ///
15 | /// Gets the directory containing the object.
16 | ///
17 | IDirectory Directory { get; }
18 | ///
19 | /// Gets the path of the object.
20 | ///
21 | ///
22 | /// The value of this property resembles a file path with ancestor names separated by a backslash.
23 | ///
24 | ///
25 | string ObjectPath { get; }
26 |
27 | IDirectoryObject? Parent { get; }
28 | IDirectoryObject GetChild(string name);
29 | IEnumerable GetChildren();
30 | object? GetValueOf(IAttributeSchema attribute);
31 | MultiValue GetMultiValuesOf(IAttributeSchema attribute);
32 | object[] GetValueOfMultiple(IList attributes);
33 | IEnumerable SearchSubtree(string? searchName);
34 | IEnumerable SearchSubtree(string? searchName, IClassSchema? objectClass, bool includeSubclasses);
35 |
36 | ///
37 | /// Gets the object class schema.
38 | ///
39 | IClassSchema ObjectClass { get; }
40 |
41 | ///
42 | /// Gets a list of objects that are direct members of this object.
43 | ///
44 | ///
45 | IEnumerable GetMembers();
46 | ///
47 | /// Gets a list of objects that this object is a member of.
48 | ///
49 | ///
50 | IEnumerable GetMemberOfGroups();
51 | }
--------------------------------------------------------------------------------
/DitExplorer.Core/ISchemaObject.cs:
--------------------------------------------------------------------------------
1 | namespace DitExplorer;
2 |
3 | public interface ISchemaObject : IDirectoryObject
4 | {
5 | string LdapDisplayName { get; }
6 | }
--------------------------------------------------------------------------------
/DitExplorer.Core/MultiValue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace DitExplorer;
9 |
10 | ///
11 | /// Represents the value of an attribute that can hold multiple values.
12 | ///
13 | public abstract class MultiValue : System.Collections.IEnumerable
14 | {
15 | ///
16 | /// Gets the element type;
17 | ///
18 | public abstract Type ElementType { get; }
19 |
20 | ///
21 | /// Gets the array holding the individual values.
22 | ///
23 | protected abstract Array ValueArray { get; }
24 |
25 | ///
26 | /// Gets the number of elements.
27 | ///
28 | public abstract int Count { get; }
29 |
30 | ///
31 | /// Gets an element at a 0-based index.
32 | ///
33 | /// 0-based index
34 | /// Value at
35 | public abstract object? GetElementAt(int index);
36 |
37 | ///
38 | /// Instantiates a from the values in an array.
39 | ///
40 | /// Array holding values
41 | /// A holding the values of
42 | /// is
43 | public static MultiValue Create(Array array)
44 | {
45 | if (array is null) throw new ArgumentNullException(nameof(array));
46 |
47 | var elemType = array.GetType().GetElementType();
48 | var multiType = typeof(MultiValue<>).MakeGenericType(elemType);
49 | var multi = (MultiValue)Activator.CreateInstance(multiType, array, array.Length)!;
50 | return multi;
51 | }
52 |
53 | ///
54 | /// Instantiates a from the values in an array.
55 | ///
56 | /// Array holding values
57 | /// Number of elements in to use
58 | /// A holding the values of
59 | /// is
60 | public static MultiValue Create(Array array, int count)
61 | {
62 | if (array is null) throw new ArgumentNullException(nameof(array));
63 |
64 | var elemType = array.GetType().GetElementType();
65 | var multiType = typeof(MultiValue<>).MakeGenericType(elemType);
66 | var multi = (MultiValue)Activator.CreateInstance(multiType, array, count)!;
67 | return multi;
68 | }
69 |
70 | ///
71 | /// Instantiates a from the values in an array.
72 | ///
73 | /// Type of element
74 | /// Array holding values
75 | /// A holding the values of
76 | /// is
77 | public static MultiValue Create(T[] elements)
78 | {
79 | if (elements is null) throw new ArgumentNullException(nameof(elements));
80 |
81 | return new MultiValue(elements, elements.Length);
82 | }
83 |
84 | ///
85 | /// Gets the element type for a multivalue type.
86 | ///
87 | /// A closed multivalue .
88 | /// Element type of , if it is ; otherwise,
89 | /// is
90 | ///
91 | /// If is not a , this is
92 | /// indicated by a return value of and does not throw an exception.
93 | ///
94 | public static Type? GetElementType(Type multivalueType)
95 | {
96 | if (multivalueType is null) throw new ArgumentNullException(nameof(multivalueType));
97 |
98 | if (multivalueType.IsGenericType && !multivalueType.IsGenericTypeDefinition)
99 | {
100 | var def = multivalueType.GetGenericTypeDefinition();
101 | if (def == typeof(MultiValue<>))
102 | {
103 | return def.GetGenericArguments()[0];
104 | }
105 | }
106 | return null;
107 | }
108 |
109 | ///
110 | public IEnumerator GetEnumerator() => this.ValueArray.GetEnumerator();
111 | }
112 |
113 | ///
114 | /// Represents the value of an attribute that can hold multiple values.
115 | ///
116 | /// The type of the attribute
117 | public sealed partial class MultiValue : MultiValue, IEnumerable
118 | {
119 | ///
120 | /// Initializes a new .
121 | ///
122 | /// Values contained in the attribute
123 | /// is
124 | public MultiValue(T[] values, int count)
125 | {
126 | if (values is null) throw new ArgumentNullException(nameof(values));
127 | if (count > values.Length) throw new ArgumentOutOfRangeException(nameof(count));
128 |
129 | if (count < values.Length)
130 | Array.Resize(ref values, count);
131 |
132 | this.Values = values;
133 | }
134 | ///
135 | /// Gets the individual values.
136 | ///
137 | public T[] Values { get; }
138 |
139 | ///
140 | protected sealed override Array ValueArray => this.Values;
141 |
142 | ///
143 | public sealed override Type ElementType => throw new NotImplementedException();
144 |
145 | ///
146 | public sealed override int Count => this.Values.Length;
147 | ///
148 | public sealed override object? GetElementAt(int index)
149 | => this.Values[index];
150 |
151 | private string? _cachedString;
152 |
153 | ///
154 | public sealed override string ToString()
155 | => (this._cachedString ?? this.BuildString());
156 | private string BuildString()
157 | {
158 | StringBuilder sb = new StringBuilder();
159 | foreach (var item in this.Values)
160 | {
161 | if (item != null)
162 | {
163 | var str = item.ToString();
164 | if (sb.Length > 0)
165 | sb.Append("; ");
166 | sb.Append(str);
167 | }
168 | }
169 | return sb.ToString();
170 | }
171 |
172 | IEnumerator IEnumerable.GetEnumerator()
173 | {
174 | return ((IEnumerable)Values).GetEnumerator();
175 | }
176 | }
177 |
178 | partial class MultiValue : IComparable>, IComparable
179 | {
180 | ///
181 | public int CompareTo(MultiValue? other)
182 | {
183 | if (other is null)
184 | return -1;
185 |
186 | var count = Math.Min(this.Values.Length, other.Values.Length);
187 | for (int i = 0; i < count; i++)
188 | {
189 | var x = this.Values[i];
190 | var y = this.Values[i];
191 |
192 | int cmp = Comparer.Default.Compare(x, y);
193 | if (cmp != 0)
194 | return cmp;
195 | }
196 |
197 | {
198 | int cmp = this.Values.Length - other.Values.Length;
199 | return cmp;
200 | }
201 | }
202 |
203 | ///
204 | public int CompareTo(object? obj)
205 | {
206 | if (obj is MultiValue other)
207 | return this.CompareTo(other);
208 | else
209 | throw new ArgumentException("Other object is not of the same type.", nameof(other));
210 | }
211 |
212 | }
213 |
--------------------------------------------------------------------------------
/DitExplorer.EseInterop/DitExplorer.EseInterop.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0
5 | enable
6 | enable
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | True
16 | True
17 | Messages.resx
18 |
19 |
20 |
21 |
22 |
23 | ResXFileCodeGenerator
24 | Messages.Designer.cs
25 |
26 |
27 |
28 |
29 |
30 | Never
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/DitExplorer.EseInterop/JetColumnInfo.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Isam.Esent.Interop;
2 | using System.Runtime.InteropServices;
3 | using static Microsoft.Isam.Esent.Interop.EnumeratedColumn;
4 |
5 | namespace DitExplorer.EseInterop;
6 |
7 | public class JetColumnInfo
8 | {
9 | internal JetColumnInfo(string tableName, string columnName, JET_COLUMNDEF colInfo)
10 | : this(tableName, columnName,
11 | colInfo.columnid,
12 | (JetColumnType)colInfo.coltyp,
13 | colInfo.cbMax,
14 | colInfo.grbit)
15 | {
16 | }
17 | internal JetColumnInfo(string tableName, string columnName,
18 | JET_COLUMNID columnId,
19 | JetColumnType columnType,
20 | int maxSize,
21 | ColumndefGrbit flags)
22 | {
23 | this.TableName = tableName;
24 | this.ColumnName = columnName;
25 | this.ColumnId = columnId;
26 | this.ColumnType = columnType;
27 | this.MaxSize = maxSize;
28 | this.Flags = flags;
29 | }
30 |
31 | // UNDONE: Prefer MemoryMarshal
32 | //[StructLayout(LayoutKind.Explicit)]
33 | //struct ColumnIdUnion
34 | //{
35 | // [FieldOffset(0)]
36 | // internal JET_COLUMNID colid;
37 | // [FieldOffset(0)]
38 | // internal int value;
39 | //}
40 |
41 | ///
42 | /// Creates a .
43 | ///
44 | /// Value of column ID
45 | /// A with value
46 | ///
47 | /// doesn't provide a public constructor.
48 | ///
49 | public static JET_COLUMNID MakeColumnId(int value)
50 | => MemoryMarshal.Cast(MemoryMarshal.CreateReadOnlySpan(ref value, 1))[0];
51 | public static int ValueOf(JET_COLUMNID id)
52 | => MemoryMarshal.Cast(MemoryMarshal.CreateReadOnlySpan(ref id, 1))[0];
53 |
54 | public string TableName { get; }
55 | public string ColumnName { get; }
56 |
57 | public JET_COLUMNID ColumnId { get; }
58 | public int ColumnIdValue => ValueOf(this.ColumnId);
59 | public JetColumnType ColumnType { get; }
60 | public int MaxSize { get; }
61 | public ColumndefGrbit Flags { get; }
62 |
63 | public bool IsFixedSize => 0 != (this.Flags & ColumndefGrbit.ColumnFixed);
64 | public bool IsTagged => 0 != (this.Flags & ColumndefGrbit.ColumnTagged);
65 | public bool IsNotNull => 0 != (this.Flags & ColumndefGrbit.ColumnNotNULL);
66 | public bool IsVersion => 0 != (this.Flags & ColumndefGrbit.ColumnVersion);
67 | public bool IsMultiValued => 0 != (this.Flags & ColumndefGrbit.ColumnMultiValued);
68 | }
--------------------------------------------------------------------------------
/DitExplorer.EseInterop/JetColumnType.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Isam.Esent.Interop;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace DitExplorer.EseInterop;
9 |
10 | ///
11 | /// Specifies the data type of a column in an ESE database.
12 | ///
13 | ///
14 | /// Includes all values from the ESE documentation.
15 | /// doesn't include all values.
16 | ///
17 | public enum JetColumnType
18 | {
19 | Nil = 0,
20 | Bit = 1,
21 | UnsignedByte = 2,
22 | Short = 3,
23 | Long = 4,
24 | Currency = 5,
25 | Single = 6,
26 | Double = 7,
27 | DateTime = 8,
28 | Binary = 9,
29 | Text = 10,
30 | LongBinary = 11,
31 | LongText = 12,
32 | SLV = 13,
33 | UnsignedLong = 14,
34 | LongLong = 15,
35 | Guid = 16,
36 | UnsignedShort = 17,
37 | }
38 |
--------------------------------------------------------------------------------
/DitExplorer.EseInterop/JetIndexColumnInfo.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Isam.Esent.Interop;
2 | using Microsoft.Isam.Esent.Interop.Windows8;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace DitExplorer.EseInterop;
10 | public class JetIndexColumnInfo
11 | {
12 | public JetIndexColumnInfo(
13 | string indexName,
14 | JetIndexColumnGrbit flags,
15 | int numberOfKeys,
16 | int entries,
17 | int pages,
18 | int columnCount,
19 | int columnIndex,
20 | JET_COLUMNID columnId,
21 | JetColumnType columnType,
22 | short country,
23 | short languageId,
24 | short codePage,
25 | short collate,
26 | int columnFlags,
27 | string columnName,
28 | int lCMapFlags)
29 | {
30 | IndexName = indexName;
31 | Flags = flags;
32 | NumberOfKeys = numberOfKeys;
33 | Entries = entries;
34 | Pages = pages;
35 | ColumnCount = columnCount;
36 | ColumnIndex = columnIndex;
37 | ColumnId = columnId;
38 | ColumnType = columnType;
39 | Country = country;
40 | LanguageId = languageId;
41 | CodePage = codePage;
42 | Collate = collate;
43 | ColumnFlags = columnFlags;
44 | ColumnName = columnName;
45 | LCMapFlags = lCMapFlags;
46 | }
47 |
48 | public string IndexName { get; }
49 | public JetIndexColumnGrbit Flags { get; }
50 | public int NumberOfKeys { get; }
51 | public int Entries { get; }
52 | public int Pages { get; }
53 | public int ColumnCount { get; }
54 | public int ColumnIndex { get; }
55 | public JET_COLUMNID ColumnId { get; }
56 | public JetColumnType ColumnType { get; }
57 | public short Country { get; }
58 | public short LanguageId { get; }
59 | public short CodePage { get; }
60 | public short Collate { get; }
61 | public int ColumnFlags { get; }
62 | public string ColumnName { get; }
63 | public int LCMapFlags { get; }
64 | }
65 |
--------------------------------------------------------------------------------
/DitExplorer.EseInterop/JetInstance.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Isam.Esent.Interop;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace DitExplorer.EseInterop;
9 | public partial class JetInstance
10 | {
11 | private JET_INSTANCE _inst;
12 |
13 | public static JetInstance Initialize()
14 | {
15 | JetInstance instance = new JetInstance();
16 | Api.JetInit(ref instance._inst);
17 | return instance;
18 | }
19 |
20 | public JetSession BeginSession()
21 | {
22 | Api.JetBeginSession(this._inst, out var sesid, null, null);
23 | return new JetSession(sesid);
24 | }
25 | }
26 | partial class JetInstance : IDisposable
27 | {
28 | private bool disposedValue;
29 |
30 | protected virtual void Dispose(bool disposing)
31 | {
32 | if (!disposedValue)
33 | {
34 | Api.JetTerm(this._inst);
35 |
36 | disposedValue = true;
37 | }
38 | }
39 |
40 | // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
41 | // ~JetInstance()
42 | // {
43 | // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
44 | // Dispose(disposing: false);
45 | // }
46 |
47 | public void Dispose()
48 | {
49 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
50 | Dispose(disposing: true);
51 | GC.SuppressFinalize(this);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/DitExplorer.EseInterop/JetSession.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Isam.Esent.Interop;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using static System.Collections.Specialized.BitVector32;
8 |
9 | namespace DitExplorer.EseInterop;
10 |
11 | public enum OpenDatabaseOptions
12 | {
13 | None = 0,
14 | ReadOnly = 1,
15 | }
16 |
17 | public partial class JetSession
18 | {
19 | internal readonly JET_SESID sesid;
20 |
21 | internal JetSession(JET_SESID sesid)
22 | {
23 | this.sesid = sesid;
24 | }
25 |
26 | public static JetSession Begin()
27 | {
28 | Api.JetBeginSession(JET_INSTANCE.Nil, out var sesid, null, null);
29 | return new JetSession(sesid);
30 | }
31 |
32 | public JetDatabase AttachAndOpen(string fileName, OpenDatabaseOptions options)
33 | {
34 | this.VerifyNotDisposed();
35 |
36 | AttachDatabaseGrbit attachFlags = (0 != (options & OpenDatabaseOptions.ReadOnly)) ? AttachDatabaseGrbit.ReadOnly : AttachDatabaseGrbit.None;
37 | Api.JetAttachDatabase(this.sesid, fileName, attachFlags);
38 |
39 | try
40 | {
41 | OpenDatabaseGrbit openFlags = (0 != (options & OpenDatabaseOptions.ReadOnly)) ? OpenDatabaseGrbit.ReadOnly : OpenDatabaseGrbit.None;
42 | Api.JetOpenDatabase(this.sesid, fileName, null, out var dbid, openFlags);
43 | var db = new JetDatabase(this.sesid, dbid, fileName);
44 | fileName = null;
45 | return db;
46 | }
47 | finally
48 | {
49 | if (fileName != null)
50 | Api.JetDetachDatabase(this.sesid, fileName);
51 | }
52 | }
53 |
54 |
55 | public JetCursor IntersectAll(JetCursor[] cursors)
56 | {
57 | this.VerifyNotDisposed();
58 |
59 | if (cursors == null || cursors.Length == 0 || (Array.IndexOf(cursors, null) >= 0))
60 | throw new ArgumentNullException(nameof(cursors));
61 | if (Array.FindIndex(cursors, r => r.sesid != this.sesid) >= 0)
62 | throw new ArgumentException("One or more of the cursors belong to another session. This is not allowed.");
63 | if (Array.FindIndex(cursors, r => r.IsDisposed) >= 0)
64 | throw new ArgumentException("One or more of the cursors are disposed.");
65 |
66 | var ranges = Array.ConvertAll(cursors, r => new JET_INDEXRANGE() { tableid = r.tableId, grbit = IndexRangeGrbit.RecordInIndex });
67 | Api.JetIntersectIndexes(this.sesid, ranges, ranges.Length, out var reclist, IntersectIndexesGrbit.None);
68 | using (JetCursor curIntersect = new JetCursor(this.sesid, reclist.tableid))
69 | {
70 | throw new NotImplementedException();
71 | }
72 | }
73 | }
74 |
75 | partial class JetSession : IDisposable
76 | {
77 | private bool disposedValue;
78 | protected void VerifyNotDisposed()
79 | {
80 | if (this.disposedValue)
81 | throw new ObjectDisposedException(Messages.JetObjectDisposedMessage);
82 | }
83 |
84 | protected virtual void Dispose(bool disposing)
85 | {
86 | if (!disposedValue)
87 | {
88 | Api.JetEndSession(this.sesid, EndSessionGrbit.None);
89 | disposedValue = true;
90 | }
91 | }
92 |
93 | // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
94 | ~JetSession()
95 | {
96 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
97 | Dispose(disposing: false);
98 | }
99 |
100 | public void Dispose()
101 | {
102 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
103 | Dispose(disposing: true);
104 | GC.SuppressFinalize(this);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/DitExplorer.EseInterop/JetTableInfo.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Isam.Esent.Interop;
2 |
3 | namespace DitExplorer.EseInterop;
4 |
5 | ///
6 | /// Represents a table within an ESE database.
7 | ///
8 | public class JetTableInfo
9 | {
10 | private JetDatabase _db;
11 |
12 | internal JetTableInfo(string name, JetDatabase db, JET_OBJECTINFO objInfo)
13 | :this(name,db,
14 | recordCount: objInfo.cRecord,
15 | pagesUsed: objInfo.cPage,
16 | flags: objInfo.flags
17 | )
18 | {
19 | }
20 |
21 | internal JetTableInfo(string name, JetDatabase db,
22 | int recordCount,
23 | int pagesUsed,
24 | ObjectInfoFlags flags
25 | )
26 | {
27 | this.TableName = name;
28 | this._db = db;
29 | this.RecordCount = recordCount;
30 | this.PagesUsed = pagesUsed;
31 | this.Flags = flags;
32 | }
33 |
34 | ///
35 | /// Gets the name of the table.
36 | ///
37 | public string TableName { get; }
38 |
39 | ///
40 | /// Gets the number of records in the table.
41 | ///
42 | public int RecordCount { get; }
43 |
44 | ///
45 | /// Gets the number of pages used by the table.
46 | ///
47 | public int PagesUsed { get; }
48 |
49 | ///
50 | /// Gets a applied to the object.
51 | ///
52 | public ObjectInfoFlags Flags { get; }
53 |
54 | public JET_COLUMNID GetColumnId(string columnName)
55 | {
56 | return _db.GetColumnId(TableName, columnName);
57 | }
58 |
59 | public JetColumnInfo GetColumnInfo(string columnName)
60 | {
61 | return _db.GetColumnInfo(TableName, columnName);
62 | }
63 | }
--------------------------------------------------------------------------------
/DitExplorer.EseInterop/Messages.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace DitExplorer.EseInterop {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Messages {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Messages() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DitExplorer.EseInterop.Messages", typeof(Messages).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to The cursor has no current record..
65 | ///
66 | internal static string JetCursor_NoCurrentRecord {
67 | get {
68 | return ResourceManager.GetString("JetCursor_NoCurrentRecord", resourceCulture);
69 | }
70 | }
71 |
72 | ///
73 | /// Looks up a localized string similar to The value in the column was not the expected size..
74 | ///
75 | internal static string JetCursor_UnexpectedValueSize {
76 | get {
77 | return ResourceManager.GetString("JetCursor_UnexpectedValueSize", resourceCulture);
78 | }
79 | }
80 |
81 | ///
82 | /// Looks up a localized string similar to The object is disposed and can no longer be used..
83 | ///
84 | internal static string JetObjectDisposedMessage {
85 | get {
86 | return ResourceManager.GetString("JetObjectDisposedMessage", resourceCulture);
87 | }
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/DitExplorer.EseInterop/Messages.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | The cursor has no current record.
122 |
123 |
124 | The value in the column was not the expected size.
125 |
126 |
127 | The object is disposed and can no longer be used.
128 |
129 |
--------------------------------------------------------------------------------
/DitExplorer.EseInterop/esent.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/trustedsec/DitExplorer/693508e9fdd282fe6db73dd1cab571341e9cf733/DitExplorer.EseInterop/esent.dll
--------------------------------------------------------------------------------
/DitExplorer.Extensibility/DitExplorer.Extensibility.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0-windows7.0
5 | enable
6 | enable
7 | true
8 | DitExplorer
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/DitExplorer.Extensibility/DitExtensionManifest.xsd:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/DitExplorer.Extensibility/IDirectoryNode.cs:
--------------------------------------------------------------------------------
1 | using DitExplorer.Ntds;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace DitExplorer;
9 |
10 | public interface IDirectoryView
11 | {
12 | IDirectory Directory { get; }
13 | }
14 |
15 | public interface IDirectoryNode
16 | {
17 | IDirectoryObject Object { get; }
18 | IDirectoryView DirectoryView { get; }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/DitExplorer.Extensibility/IItemActionContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 |
8 | namespace DitExplorer;
9 | public interface IItemActionContext
10 | {
11 | Window? Owner { get; }
12 | }
13 |
--------------------------------------------------------------------------------
/DitExplorer.Extensibility/IItemActionRegistry.cs:
--------------------------------------------------------------------------------
1 | namespace DitExplorer;
2 |
3 | public interface IItemActionRegistry
4 | {
5 | IList GetActionsFor(
6 | object[] items,
7 | IItemActionContext actionContext
8 | );
9 | void RegisterAction(ItemAction action);
10 | }
--------------------------------------------------------------------------------
/DitExplorer.Extensibility/ItemAction.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Input;
2 |
3 | namespace DitExplorer
4 | {
5 | ///
6 | /// Represents an action that may be performed on one or more objects.
7 | ///
8 | ///
9 | /// If the action only applies to a single item at a time, derive from .
10 | ///
11 | public abstract class ItemAction
12 | {
13 | ///
14 | /// Gets the text displayed in the menu.
15 | ///
16 | ///
17 | /// The text should be localized for the current culture and may contain an underscore to denote the access key.
18 | ///
19 | public abstract string MenuText { get; }
20 | ///
21 | /// Determines whether the action can be executed on a set of items.
22 | ///
23 | /// Items to test
24 | /// if this action can execute on the provided items; otherwise, .
25 | public abstract bool CanExecute(object[] items, IItemActionContext context);
26 | public abstract void Execute(object[] items, IItemActionContext context);
27 | }
28 |
29 | public abstract class SingleItemAction : ItemAction
30 | {
31 | public sealed override bool CanExecute(object[] items, IItemActionContext context)
32 | {
33 | return (items is not null) && (items.Length == 1) && items[0] is T typed && CanExecute(typed, context);
34 | }
35 |
36 | public abstract bool CanExecute(T items, IItemActionContext context);
37 |
38 | public sealed override void Execute(object[] items, IItemActionContext context)
39 | {
40 | this.Execute((T)items[0], context);
41 | }
42 |
43 | public abstract void Execute(T item, IItemActionContext context);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/DitExplorer.Ntds/ADInstanceType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace DitExplorer.Ntds;
8 |
9 | ///
10 | /// Specifies instance type flags.
11 | ///
12 | ///
13 | /// These are documented in Active Directory schema.
14 | ///
15 | [Flags]
16 | public enum ADInstanceType
17 | {
18 | None = 0,
19 |
20 | ///
21 | /// Head of naming context
22 | ///
23 | HeadOfNamingContext = 1,
24 | ///
25 | /// Replica is not instantiated
26 | ///
27 | ReplicaNotInstantiated = 2,
28 | ///
29 | /// Object is writable on this directory
30 | ///
31 | Writable = 4,
32 | ///
33 | /// Naming context above this one on this directory is held
34 | ///
35 | ParentNCHeld = 8,
36 | ///
37 | /// Naming context is in the process of being constructed for the first time by using replication
38 | ///
39 | NCUnderConstruction = 0x10,
40 | ///
41 | /// Naming context is in the process of being removed from the local DSA
42 | ///
43 | NCDeleting = 0x20,
44 | };
45 |
--------------------------------------------------------------------------------
/DitExplorer.Ntds/ADSearchFlags.cs:
--------------------------------------------------------------------------------
1 | namespace DitExplorer.Ntds;
2 |
3 | // REF: https://learn.microsoft.com/en-us/windows/win32/adschema/a-searchflags
4 | [Flags]
5 | public enum ADSearchFlags
6 | {
7 | None = 0,
8 |
9 | Indexed = 1,
10 | IndexedPerContainer = 2,
11 | Anr = 4,
12 | TombstonePreserve = 8,
13 | CopyWithObject = 0x10,
14 | TupleIndex = 0x20,
15 | IndexedVlv = 0x40,
16 | Confidential = 0x80,
17 | }
18 |
--------------------------------------------------------------------------------
/DitExplorer.Ntds/ArgumentHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.CompilerServices;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace DitExplorer.Ntds;
9 | static class ArgumentHelper
10 | {
11 | public static T ThrowIfNot(object? value, [CallerArgumentExpression(nameof(value))] string? expression = null)
12 | where T : class
13 | => (value is T typed) ? typed : throw new ArgumentException("The argument is of the wrong type.", expression);
14 | public static T? ThrowIfNotNullAndNot(object? value, [CallerArgumentExpression(nameof(value))] string? expression = null)
15 | where T : class
16 | => (value is null) ? null
17 | : (value is T typed) ? typed : throw new ArgumentException("The argument is of the wrong type.", expression);
18 | public static IList ThrowIfNot(System.Collections.IList value, [CallerArgumentExpression(nameof(value))] string? expression = null)
19 | where T : class
20 | {
21 | if (!value.OfType