├── .gitignore
├── ADPickerTester
├── ADPickerTester.csproj
├── App.ico
├── MainForm.Designer.cs
├── MainForm.cs
├── MainForm.resx
└── app.config
├── Active Directory Object Picker.sln
├── LICENSE
├── README.md
├── Screenshots
├── ADPickerTester.gif
├── img01.png
├── img02.png
└── img03.png
├── Tulpep.ActiveDirectoryObjectPicker
├── ADsPathsProviders.cs
├── ComInterop.cs
├── DirectoryObject.cs
├── DirectoryObjectPickerDialog.cs
├── EnumExtensions.cs
├── ExtensionAttributeFor.NET_2.0.cs
├── IntPtrExtensions.cs
├── Locations.cs
├── NameTranslator.cs
├── ObjectTypes.cs
├── PInvoke.cs
├── StructsFlags.cs
├── Tulpep.ActiveDirectoryObjectPicker.csproj
├── Tulpep.ActiveDirectoryObjectPicker.snk
└── UnmanagedArrayOfStrings.cs
└── appveyor.yml
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # Visual Studio cache
5 | .vs/
6 |
7 | # User-specific files
8 | *.suo
9 | *.user
10 | *.sln.docstates
11 |
12 | # Build results
13 |
14 | [Dd]ebug/
15 | [Rr]elease/
16 | x64/
17 | build/
18 | [Bb]in/
19 | [Oo]bj/
20 |
21 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
22 | !packages/*/build/
23 |
24 | # MSTest test Results
25 | [Tt]est[Rr]esult*/
26 | [Bb]uild[Ll]og.*
27 |
28 | *_i.c
29 | *_p.c
30 | *.ilk
31 | *.meta
32 | *.obj
33 | *.pch
34 | *.pdb
35 | *.pgc
36 | *.pgd
37 | *.rsp
38 | *.sbr
39 | *.tlb
40 | *.tli
41 | *.tlh
42 | *.tmp
43 | *.tmp_proj
44 | *.log
45 | *.vspscc
46 | *.vssscc
47 | .builds
48 | *.pidb
49 | *.log
50 | *.scc
51 |
52 | # Visual C++ cache files
53 | ipch/
54 | *.aps
55 | *.ncb
56 | *.opensdf
57 | *.sdf
58 | *.cachefile
59 |
60 | # Visual Studio profiler
61 | *.psess
62 | *.vsp
63 | *.vspx
64 |
65 | # Guidance Automation Toolkit
66 | *.gpState
67 |
68 | # ReSharper is a .NET coding add-in
69 | _ReSharper*/
70 | *.[Rr]e[Ss]harper
71 |
72 | # TeamCity is a build add-in
73 | _TeamCity*
74 |
75 | # DotCover is a Code Coverage Tool
76 | *.dotCover
77 |
78 | # NCrunch
79 | *.ncrunch*
80 | .*crunch*.local.xml
81 |
82 | # Installshield output folder
83 | [Ee]xpress/
84 |
85 | # DocProject is a documentation generator add-in
86 | DocProject/buildhelp/
87 | DocProject/Help/*.HxT
88 | DocProject/Help/*.HxC
89 | DocProject/Help/*.hhc
90 | DocProject/Help/*.hhk
91 | DocProject/Help/*.hhp
92 | DocProject/Help/Html2
93 | DocProject/Help/html
94 |
95 | # Click-Once directory
96 | publish/
97 |
98 | # Publish Web Output
99 | *.Publish.xml
100 |
101 | # NuGet Packages Directory
102 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
103 | packages/
104 |
105 | # Visual Studio 2017 project directory
106 | .vs/
107 |
108 | # Windows Azure Build Output
109 | csx
110 | *.build.csdef
111 |
112 | # Windows Store app package directory
113 | AppPackages/
114 |
115 | # Others
116 | sql/
117 | *.Cache
118 | ClientBin/
119 | [Ss]tyle[Cc]op.*
120 | ~$*
121 | *~
122 | *.dbmdl
123 | *.[Pp]ublish.xml
124 | *.publishsettings
125 |
126 | # RIA/Silverlight projects
127 | Generated_Code/
128 |
129 | # Backup & report files from converting an old project file to a newer
130 | # Visual Studio version. Backup files are not needed, because we have git ;-)
131 | _UpgradeReport_Files/
132 | Backup*/
133 | UpgradeLog*.XML
134 | UpgradeLog*.htm
135 |
136 | # SQL Server files
137 | *.mdf
138 | *.ldf
139 |
140 |
141 | #LightSwitch generated files
142 | GeneratedArtifacts/
143 | _Pvt_Extensions/
144 | ModelManifest.xml
145 |
146 | # =========================
147 | # Windows detritus
148 | # =========================
149 |
150 | # Windows image file caches
151 | Thumbs.db
152 | ehthumbs.db
153 |
154 | # Folder config file
155 | Desktop.ini
156 |
157 | # Recycle Bin used on file shares
158 | $RECYCLE.BIN/
159 |
160 | # Mac desktop service store files
161 | .DS_Store
162 |
163 | #Nuget builder process
164 | nuget.exe
165 | *.nupkg
166 |
--------------------------------------------------------------------------------
/ADPickerTester/ADPickerTester.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | WinExe
4 | net472
5 | true
6 | true
7 | ADPickerTester
8 |
9 | Directory Object Picker
10 | © 2004 Armand du Plessis <armand@dotnet.org.za>
11 | 2.0.0.0
12 | 2.0.0
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/ADPickerTester/App.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tulpep/Active-Directory-Object-Picker/7888679b16e929b750b6b56b3f931162fa674dc9/ADPickerTester/App.ico
--------------------------------------------------------------------------------
/ADPickerTester/MainForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace ADPickerTester
2 | {
3 | partial class MainForm
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 |
24 | #region Windows Form Designer generated code
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.btnInvoke = new System.Windows.Forms.Button();
32 | this.chklistDefaultTypes = new System.Windows.Forms.CheckedListBox();
33 | this.label1 = new System.Windows.Forms.Label();
34 | this.chkMultiSelect = new System.Windows.Forms.CheckBox();
35 | this.label2 = new System.Windows.Forms.Label();
36 | this.chklistAllowedTypes = new System.Windows.Forms.CheckedListBox();
37 | this.label3 = new System.Windows.Forms.Label();
38 | this.chklistAllowedLocations = new System.Windows.Forms.CheckedListBox();
39 | this.label4 = new System.Windows.Forms.Label();
40 | this.chklistDefaultLocations = new System.Windows.Forms.CheckedListBox();
41 | this.chkShowAdvanced = new System.Windows.Forms.CheckBox();
42 | this.txtTargetComputer = new System.Windows.Forms.TextBox();
43 | this.label5 = new System.Windows.Forms.Label();
44 | this.chklistAttributes = new System.Windows.Forms.CheckedListBox();
45 | this.label6 = new System.Windows.Forms.Label();
46 | this.chkSkipDcCheck = new System.Windows.Forms.CheckBox();
47 | this.comboPathProvider = new System.Windows.Forms.ComboBox();
48 | this.label7 = new System.Windows.Forms.Label();
49 | this.txtFeedback = new System.Windows.Forms.TextBox();
50 | this.label8 = new System.Windows.Forms.Label();
51 | this.txUserName = new System.Windows.Forms.TextBox();
52 | this.label9 = new System.Windows.Forms.Label();
53 | this.txPassword = new System.Windows.Forms.TextBox();
54 | this.SuspendLayout();
55 | //
56 | // btnInvoke
57 | //
58 | this.btnInvoke.Location = new System.Drawing.Point(530, 172);
59 | this.btnInvoke.Name = "btnInvoke";
60 | this.btnInvoke.Size = new System.Drawing.Size(121, 39);
61 | this.btnInvoke.TabIndex = 0;
62 | this.btnInvoke.Text = "Directory Object Picker";
63 | this.btnInvoke.Click += new System.EventHandler(this.btnInvoke_Click);
64 | //
65 | // chklistDefaultTypes
66 | //
67 | this.chklistDefaultTypes.FormattingEnabled = true;
68 | this.chklistDefaultTypes.Location = new System.Drawing.Point(279, 25);
69 | this.chklistDefaultTypes.Name = "chklistDefaultTypes";
70 | this.chklistDefaultTypes.Size = new System.Drawing.Size(120, 124);
71 | this.chklistDefaultTypes.TabIndex = 6;
72 | //
73 | // label1
74 | //
75 | this.label1.AutoSize = true;
76 | this.label1.Location = new System.Drawing.Point(279, 9);
77 | this.label1.Name = "label1";
78 | this.label1.Size = new System.Drawing.Size(73, 13);
79 | this.label1.TabIndex = 5;
80 | this.label1.Text = "Default Types";
81 | //
82 | // chkMultiSelect
83 | //
84 | this.chkMultiSelect.AutoSize = true;
85 | this.chkMultiSelect.Location = new System.Drawing.Point(279, 199);
86 | this.chkMultiSelect.Name = "chkMultiSelect";
87 | this.chkMultiSelect.Size = new System.Drawing.Size(81, 17);
88 | this.chkMultiSelect.TabIndex = 19;
89 | this.chkMultiSelect.Text = "Multi-Select";
90 | this.chkMultiSelect.UseVisualStyleBackColor = true;
91 | //
92 | // label2
93 | //
94 | this.label2.AutoSize = true;
95 | this.label2.Location = new System.Drawing.Point(12, 9);
96 | this.label2.Name = "label2";
97 | this.label2.Size = new System.Drawing.Size(76, 13);
98 | this.label2.TabIndex = 1;
99 | this.label2.Text = "Allowed Types";
100 | //
101 | // chklistAllowedTypes
102 | //
103 | this.chklistAllowedTypes.FormattingEnabled = true;
104 | this.chklistAllowedTypes.Location = new System.Drawing.Point(12, 25);
105 | this.chklistAllowedTypes.Name = "chklistAllowedTypes";
106 | this.chklistAllowedTypes.Size = new System.Drawing.Size(120, 124);
107 | this.chklistAllowedTypes.TabIndex = 2;
108 | //
109 | // label3
110 | //
111 | this.label3.AutoSize = true;
112 | this.label3.Location = new System.Drawing.Point(138, 9);
113 | this.label3.Name = "label3";
114 | this.label3.Size = new System.Drawing.Size(93, 13);
115 | this.label3.TabIndex = 3;
116 | this.label3.Text = "Allowed Locations";
117 | //
118 | // chklistAllowedLocations
119 | //
120 | this.chklistAllowedLocations.FormattingEnabled = true;
121 | this.chklistAllowedLocations.Location = new System.Drawing.Point(138, 25);
122 | this.chklistAllowedLocations.Name = "chklistAllowedLocations";
123 | this.chklistAllowedLocations.Size = new System.Drawing.Size(120, 124);
124 | this.chklistAllowedLocations.TabIndex = 4;
125 | //
126 | // label4
127 | //
128 | this.label4.AutoSize = true;
129 | this.label4.Location = new System.Drawing.Point(405, 9);
130 | this.label4.Name = "label4";
131 | this.label4.Size = new System.Drawing.Size(90, 13);
132 | this.label4.TabIndex = 7;
133 | this.label4.Text = "Default Locations";
134 | //
135 | // chklistDefaultLocations
136 | //
137 | this.chklistDefaultLocations.FormattingEnabled = true;
138 | this.chklistDefaultLocations.Location = new System.Drawing.Point(405, 25);
139 | this.chklistDefaultLocations.Name = "chklistDefaultLocations";
140 | this.chklistDefaultLocations.Size = new System.Drawing.Size(120, 124);
141 | this.chklistDefaultLocations.TabIndex = 8;
142 | //
143 | // chkShowAdvanced
144 | //
145 | this.chkShowAdvanced.AutoSize = true;
146 | this.chkShowAdvanced.Location = new System.Drawing.Point(279, 181);
147 | this.chkShowAdvanced.Name = "chkShowAdvanced";
148 | this.chkShowAdvanced.Size = new System.Drawing.Size(105, 17);
149 | this.chkShowAdvanced.TabIndex = 18;
150 | this.chkShowAdvanced.Text = "Show Advanced";
151 | this.chkShowAdvanced.UseVisualStyleBackColor = true;
152 | //
153 | // txtTargetComputer
154 | //
155 | this.txtTargetComputer.Location = new System.Drawing.Point(104, 163);
156 | this.txtTargetComputer.Name = "txtTargetComputer";
157 | this.txtTargetComputer.Size = new System.Drawing.Size(154, 20);
158 | this.txtTargetComputer.TabIndex = 12;
159 | //
160 | // label5
161 | //
162 | this.label5.AutoSize = true;
163 | this.label5.Location = new System.Drawing.Point(12, 167);
164 | this.label5.Name = "label5";
165 | this.label5.Size = new System.Drawing.Size(86, 13);
166 | this.label5.TabIndex = 11;
167 | this.label5.Text = "Target Computer";
168 | //
169 | // chklistAttributes
170 | //
171 | this.chklistAttributes.FormattingEnabled = true;
172 | this.chklistAttributes.Location = new System.Drawing.Point(531, 25);
173 | this.chklistAttributes.Name = "chklistAttributes";
174 | this.chklistAttributes.Size = new System.Drawing.Size(120, 124);
175 | this.chklistAttributes.TabIndex = 10;
176 | //
177 | // label6
178 | //
179 | this.label6.AutoSize = true;
180 | this.label6.Location = new System.Drawing.Point(531, 9);
181 | this.label6.Name = "label6";
182 | this.label6.Size = new System.Drawing.Size(93, 13);
183 | this.label6.TabIndex = 9;
184 | this.label6.Text = "Attributes to Fetch";
185 | //
186 | // chkSkipDcCheck
187 | //
188 | this.chkSkipDcCheck.AutoSize = true;
189 | this.chkSkipDcCheck.Checked = true;
190 | this.chkSkipDcCheck.CheckState = System.Windows.Forms.CheckState.Checked;
191 | this.chkSkipDcCheck.Location = new System.Drawing.Point(279, 163);
192 | this.chkSkipDcCheck.Name = "chkSkipDcCheck";
193 | this.chkSkipDcCheck.Size = new System.Drawing.Size(98, 17);
194 | this.chkSkipDcCheck.TabIndex = 17;
195 | this.chkSkipDcCheck.Text = "Skip DC check";
196 | this.chkSkipDcCheck.UseVisualStyleBackColor = true;
197 | //
198 | // comboPathProvider
199 | //
200 | this.comboPathProvider.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
201 | this.comboPathProvider.FormattingEnabled = true;
202 | this.comboPathProvider.Location = new System.Drawing.Point(403, 182);
203 | this.comboPathProvider.Name = "comboPathProvider";
204 | this.comboPathProvider.Size = new System.Drawing.Size(121, 21);
205 | this.comboPathProvider.TabIndex = 21;
206 | //
207 | // label7
208 | //
209 | this.label7.AutoSize = true;
210 | this.label7.Location = new System.Drawing.Point(405, 163);
211 | this.label7.Name = "label7";
212 | this.label7.Size = new System.Drawing.Size(71, 13);
213 | this.label7.TabIndex = 20;
214 | this.label7.Text = "Path Provider";
215 | //
216 | // txtFeedback
217 | //
218 | this.txtFeedback.Dock = System.Windows.Forms.DockStyle.Bottom;
219 | this.txtFeedback.Location = new System.Drawing.Point(0, 222);
220 | this.txtFeedback.Multiline = true;
221 | this.txtFeedback.Name = "txtFeedback";
222 | this.txtFeedback.ReadOnly = true;
223 | this.txtFeedback.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
224 | this.txtFeedback.Size = new System.Drawing.Size(666, 237);
225 | this.txtFeedback.TabIndex = 22;
226 | //
227 | // label8
228 | //
229 | this.label8.AutoSize = true;
230 | this.label8.Location = new System.Drawing.Point(12, 193);
231 | this.label8.Name = "label8";
232 | this.label8.Size = new System.Drawing.Size(29, 13);
233 | this.label8.TabIndex = 13;
234 | this.label8.Text = "User";
235 | //
236 | // txUserName
237 | //
238 | this.txUserName.Location = new System.Drawing.Point(47, 190);
239 | this.txUserName.Name = "txUserName";
240 | this.txUserName.Size = new System.Drawing.Size(85, 20);
241 | this.txUserName.TabIndex = 14;
242 | //
243 | // label9
244 | //
245 | this.label9.AutoSize = true;
246 | this.label9.Location = new System.Drawing.Point(138, 193);
247 | this.label9.Name = "label9";
248 | this.label9.Size = new System.Drawing.Size(28, 13);
249 | this.label9.TabIndex = 15;
250 | this.label9.Text = "Pwd";
251 | //
252 | // txPassword
253 | //
254 | this.txPassword.Location = new System.Drawing.Point(173, 190);
255 | this.txPassword.Name = "txPassword";
256 | this.txPassword.Size = new System.Drawing.Size(85, 20);
257 | this.txPassword.TabIndex = 16;
258 | //
259 | // MainForm
260 | //
261 | this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
262 | this.ClientSize = new System.Drawing.Size(666, 459);
263 | this.Controls.Add(this.txPassword);
264 | this.Controls.Add(this.label9);
265 | this.Controls.Add(this.txUserName);
266 | this.Controls.Add(this.label8);
267 | this.Controls.Add(this.txtFeedback);
268 | this.Controls.Add(this.comboPathProvider);
269 | this.Controls.Add(this.label5);
270 | this.Controls.Add(this.txtTargetComputer);
271 | this.Controls.Add(this.chkShowAdvanced);
272 | this.Controls.Add(this.label6);
273 | this.Controls.Add(this.label7);
274 | this.Controls.Add(this.label4);
275 | this.Controls.Add(this.chklistAttributes);
276 | this.Controls.Add(this.chklistDefaultLocations);
277 | this.Controls.Add(this.label3);
278 | this.Controls.Add(this.chklistAllowedLocations);
279 | this.Controls.Add(this.label2);
280 | this.Controls.Add(this.chklistAllowedTypes);
281 | this.Controls.Add(this.chkSkipDcCheck);
282 | this.Controls.Add(this.chkMultiSelect);
283 | this.Controls.Add(this.label1);
284 | this.Controls.Add(this.chklistDefaultTypes);
285 | this.Controls.Add(this.btnInvoke);
286 | this.Name = "MainForm";
287 | this.Text = "Directory Object Picker Tester";
288 | this.Load += new System.EventHandler(this.Form1_Load);
289 | this.ResumeLayout(false);
290 | this.PerformLayout();
291 |
292 | }
293 | #endregion
294 |
295 | private System.Windows.Forms.Button btnInvoke;
296 | private System.Windows.Forms.CheckedListBox chklistDefaultTypes;
297 | private System.Windows.Forms.Label label1;
298 | private System.Windows.Forms.CheckBox chkMultiSelect;
299 | private System.Windows.Forms.Label label2;
300 | private System.Windows.Forms.CheckedListBox chklistAllowedTypes;
301 | private System.Windows.Forms.Label label3;
302 | private System.Windows.Forms.CheckedListBox chklistAllowedLocations;
303 | private System.Windows.Forms.Label label4;
304 | private System.Windows.Forms.CheckedListBox chklistDefaultLocations;
305 | private System.Windows.Forms.CheckBox chkShowAdvanced;
306 | private System.Windows.Forms.TextBox txtTargetComputer;
307 | private System.Windows.Forms.Label label5;
308 | private System.Windows.Forms.CheckedListBox chklistAttributes;
309 | private System.Windows.Forms.Label label6;
310 | private System.Windows.Forms.CheckBox chkSkipDcCheck;
311 | private System.Windows.Forms.ComboBox comboPathProvider;
312 | private System.Windows.Forms.Label label7;
313 | private System.Windows.Forms.TextBox txtFeedback;
314 | private System.Windows.Forms.Label label8;
315 | private System.Windows.Forms.TextBox txUserName;
316 | private System.Windows.Forms.Label label9;
317 | private System.Windows.Forms.TextBox txPassword;
318 | }
319 | }
320 |
--------------------------------------------------------------------------------
/ADPickerTester/MainForm.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Security.Principal;
4 | using System.Text;
5 | using System.Windows.Forms;
6 |
7 | using Tulpep.ActiveDirectoryObjectPicker;
8 |
9 | namespace ADPickerTester
10 | {
11 | /// Summary description for Form1.
12 | public partial class MainForm : Form
13 | {
14 | public MainForm() =>
15 | // Required for Windows Form Designer support
16 | InitializeComponent();
17 |
18 | /// The main entry point for the application.
19 | [STAThread]
20 | private static void Main() => Application.Run(new MainForm());
21 |
22 | private void btnInvoke_Click(object sender, EventArgs e)
23 | {
24 | try
25 | {
26 | var allowedTypes = ObjectTypes.None;
27 | foreach (ObjectTypes value in chklistAllowedTypes.CheckedItems)
28 | {
29 | allowedTypes |= value;
30 | }
31 | var defaultTypes = ObjectTypes.None;
32 | foreach (ObjectTypes value in chklistDefaultTypes.CheckedItems)
33 | {
34 | defaultTypes |= value;
35 | }
36 | var allowedLocations = Locations.None;
37 | foreach (Locations value in chklistAllowedLocations.CheckedItems)
38 | {
39 | allowedLocations |= value;
40 | }
41 | var defaultLocations = Locations.None;
42 | foreach (Locations value in chklistDefaultLocations.CheckedItems)
43 | {
44 | defaultLocations |= value;
45 | }
46 | // Show dialog
47 | var picker = new DirectoryObjectPickerDialog
48 | {
49 | AllowedObjectTypes = allowedTypes,
50 | DefaultObjectTypes = defaultTypes,
51 | AllowedLocations = allowedLocations,
52 | DefaultLocations = defaultLocations,
53 | MultiSelect = chkMultiSelect.Checked,
54 | TargetComputer = txtTargetComputer.Text,
55 | SkipDomainControllerCheck = chkSkipDcCheck.Checked,
56 | ShowAdvancedView = chkShowAdvanced.Checked
57 | };
58 |
59 | if (comboPathProvider.SelectedItem is ADsPathsProviders)
60 | picker.Providers = (ADsPathsProviders)comboPathProvider.SelectedItem;
61 |
62 | if (txUserName.TextLength > 0)
63 | picker.SetCredentials(txUserName.Text, txPassword.Text);
64 |
65 | foreach (string attribute in chklistAttributes.CheckedItems)
66 | {
67 | var trimmed = attribute.Trim();
68 | if (trimmed.Length > 0)
69 | picker.AttributesToFetch.Add(trimmed);
70 | }
71 | var dialogResult = picker.ShowDialog(this);
72 | if (dialogResult == DialogResult.OK)
73 | {
74 | var results = picker.SelectedObjects;
75 | if (results == null)
76 | {
77 | txtFeedback.Text = "Results null.";
78 | return;
79 | }
80 |
81 | var sb = new StringBuilder();
82 |
83 | for (var i = 0; i <= results.Length - 1; i++)
84 | {
85 | sb.Append(string.Format("Name: \t\t {0}", results[i].Name));
86 | sb.Append(Environment.NewLine);
87 | sb.Append(string.Format("UPN: \t\t {0}", results[i].Upn));
88 | sb.Append(Environment.NewLine);
89 | sb.Append(string.Format("Path: \t\t {0}", results[i].Path));
90 | sb.Append(Environment.NewLine);
91 | sb.Append(string.Format("Schema Class: \t\t {0} ", results[i].SchemaClassName));
92 | sb.Append(Environment.NewLine);
93 | var downLevelName = "";
94 | try
95 | {
96 | if (!string.IsNullOrEmpty(results[i].Upn))
97 | downLevelName = NameTranslator.TranslateUpnToDownLevel(results[i].Upn);
98 | }
99 | catch (Exception ex)
100 | {
101 | downLevelName = string.Format("{0}: {1}", ex.GetType().Name, ex.Message);
102 | }
103 | sb.Append(string.Format("Down-level Name: \t\t {0} ", downLevelName));
104 | sb.Append(Environment.NewLine);
105 | sb.AppendFormat("Fetched {0} attributes", results[i].FetchedAttributes.Length);
106 | sb.Append(Environment.NewLine);
107 | for (var j = 0; j < results[i].FetchedAttributes.Length; j++)
108 | {
109 | var attributeName = picker.AttributesToFetch[j];
110 | sb.AppendFormat("\t{0}. {1}", j, attributeName);
111 | sb.Append(Environment.NewLine);
112 |
113 | var multivaluedAttribute = results[i].FetchedAttributes[j];
114 | if (!(multivaluedAttribute is IEnumerable) || multivaluedAttribute is byte[] || multivaluedAttribute is string)
115 | multivaluedAttribute = new[] { multivaluedAttribute };
116 |
117 | foreach (var attribute in (IEnumerable)multivaluedAttribute)
118 | {
119 | sb.Append("\t");
120 | if (attribute == null)
121 | {
122 | sb.Append("(not present)");
123 | }
124 | else if (attribute is byte[])
125 | {
126 | var bytes = (byte[])attribute;
127 | if (attributeName.Equals("objectSid", StringComparison.OrdinalIgnoreCase))
128 | {
129 | sb.Append(SIDBytesToString(bytes));
130 | }
131 | else
132 | {
133 | sb.Append(GuidBytesToString(bytes));
134 | }
135 | }
136 | else
137 | {
138 | sb.AppendFormat("{0}", attribute);
139 | }
140 | sb.Append(Environment.NewLine);
141 | }
142 |
143 | sb.Append(Environment.NewLine);
144 | }
145 |
146 | sb.Append(Environment.NewLine);
147 | sb.Append(Environment.NewLine);
148 | }
149 | txtFeedback.Text = sb.ToString();
150 | }
151 | else
152 | {
153 | txtFeedback.Text = "Dialog result: " + dialogResult;
154 | }
155 | }
156 | catch (Exception e1)
157 | {
158 | MessageBox.Show(e1.ToString());
159 | }
160 | }
161 |
162 | private string BytesToString(byte[] bytes)
163 | {
164 | return "0x" + BitConverter.ToString(bytes).Replace('-', ' ');
165 | }
166 |
167 | private string GuidBytesToString(byte[] bytes)
168 | {
169 | try
170 | {
171 | var guid = new Guid(bytes);
172 | return guid.ToString("D");
173 | }
174 | // ReSharper disable once EmptyGeneralCatchClause
175 | catch (Exception)
176 | {
177 | }
178 |
179 | return BytesToString(bytes);
180 | }
181 |
182 | private string SIDBytesToString(byte[] bytes)
183 | {
184 | try
185 | {
186 | var sid = new SecurityIdentifier(bytes, 0);
187 | return sid.ToString();
188 | }
189 | // ReSharper disable once EmptyGeneralCatchClause
190 | catch (Exception)
191 | {
192 | }
193 |
194 | return BytesToString(bytes);
195 | }
196 |
197 | private void Form1_Load(object sender, EventArgs e)
198 | {
199 | chklistAllowedTypes.Items.Clear();
200 | chklistDefaultTypes.Items.Clear();
201 | foreach (ObjectTypes objectType in Enum.GetValues(typeof(ObjectTypes)))
202 | {
203 | if (objectType != ObjectTypes.None && objectType != ObjectTypes.All)
204 | {
205 | chklistAllowedTypes.Items.Add(objectType, CheckState.Checked);
206 | if (objectType == ObjectTypes.Users || objectType == ObjectTypes.Groups
207 | || objectType == ObjectTypes.Computers || objectType == ObjectTypes.Contacts)
208 | {
209 | chklistDefaultTypes.Items.Add(objectType, CheckState.Checked);
210 | }
211 | else
212 | {
213 | chklistDefaultTypes.Items.Add(objectType, CheckState.Unchecked);
214 | }
215 | }
216 | }
217 |
218 | chklistAllowedLocations.Items.Clear();
219 | chklistDefaultLocations.Items.Clear();
220 | foreach (Locations location in Enum.GetValues(typeof(Locations)))
221 | {
222 | if (location != Locations.None && location != Locations.All)
223 | {
224 | chklistAllowedLocations.Items.Add(location, CheckState.Checked);
225 | if (location == Locations.JoinedDomain)
226 | {
227 | chklistDefaultLocations.Items.Add(location, CheckState.Checked);
228 | }
229 | else
230 | {
231 | chklistDefaultLocations.Items.Add(location, CheckState.Unchecked);
232 | }
233 | }
234 | }
235 |
236 | comboPathProvider.Items.Clear();
237 | foreach (ADsPathsProviders provider in Enum.GetValues(typeof(ADsPathsProviders)))
238 | {
239 | comboPathProvider.Items.Add(provider);
240 | }
241 | comboPathProvider.SelectedIndex = 0;
242 |
243 | chklistAttributes.Items.Clear();
244 | //to find more, go to http://msdn.microsoft.com/en-us/library/cc219752.aspx, http://msdn.microsoft.com/en-us/library/cc220155.aspx and http://msdn.microsoft.com/en-us/library/cc220700.aspx
245 | chklistAttributes.Items.AddRange(new object[]
246 | {
247 | "objectSid",
248 | });
249 | }
250 | }
251 | }
--------------------------------------------------------------------------------
/ADPickerTester/MainForm.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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/ADPickerTester/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/Active Directory Object Picker.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29709.97
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ADPickerTester", "ADPickerTester\ADPickerTester.csproj", "{46550771-182F-4A29-9879-AD837D0A9D10}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tulpep.ActiveDirectoryObjectPicker", "Tulpep.ActiveDirectoryObjectPicker\Tulpep.ActiveDirectoryObjectPicker.csproj", "{AD25BE24-8A42-44E0-9588-3767F572C338}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution files", "Solution files", "{3CFB5347-F5F1-4097-B9F7-0880E1B1CA57}"
11 | ProjectSection(SolutionItems) = preProject
12 | appveyor.yml = appveyor.yml
13 | LICENSE = LICENSE
14 | README.md = README.md
15 | EndProjectSection
16 | EndProject
17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Screenshots", "Screenshots", "{81FE7AEB-CEBA-425A-8CFA-B1AE18185813}"
18 | ProjectSection(SolutionItems) = preProject
19 | Screenshots\ADPickerTester.gif = Screenshots\ADPickerTester.gif
20 | Screenshots\img01.png = Screenshots\img01.png
21 | Screenshots\img02.png = Screenshots\img02.png
22 | Screenshots\img03.png = Screenshots\img03.png
23 | EndProjectSection
24 | EndProject
25 | Global
26 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
27 | Debug|Any CPU = Debug|Any CPU
28 | Release|Any CPU = Release|Any CPU
29 | EndGlobalSection
30 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
31 | {46550771-182F-4A29-9879-AD837D0A9D10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32 | {46550771-182F-4A29-9879-AD837D0A9D10}.Debug|Any CPU.Build.0 = Debug|Any CPU
33 | {46550771-182F-4A29-9879-AD837D0A9D10}.Release|Any CPU.ActiveCfg = Release|Any CPU
34 | {46550771-182F-4A29-9879-AD837D0A9D10}.Release|Any CPU.Build.0 = Release|Any CPU
35 | {AD25BE24-8A42-44E0-9588-3767F572C338}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
36 | {AD25BE24-8A42-44E0-9588-3767F572C338}.Debug|Any CPU.Build.0 = Debug|Any CPU
37 | {AD25BE24-8A42-44E0-9588-3767F572C338}.Release|Any CPU.ActiveCfg = Release|Any CPU
38 | {AD25BE24-8A42-44E0-9588-3767F572C338}.Release|Any CPU.Build.0 = Release|Any CPU
39 | EndGlobalSection
40 | GlobalSection(SolutionProperties) = preSolution
41 | HideSolutionNode = FALSE
42 | EndGlobalSection
43 | GlobalSection(NestedProjects) = preSolution
44 | {81FE7AEB-CEBA-425A-8CFA-B1AE18185813} = {3CFB5347-F5F1-4097-B9F7-0880E1B1CA57}
45 | EndGlobalSection
46 | GlobalSection(ExtensibilityGlobals) = postSolution
47 | SolutionGuid = {EAC3C911-4E73-492D-B434-3F7FDDC11C8F}
48 | EndGlobalSection
49 | EndGlobal
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Microsoft Public License (MS-PL)
2 |
3 | The initial project was originally created by Armand du Plessis in 2004 and now is extended and maintained by Tulpep.
4 |
5 | This license governs use of the accompanying software. If you use the software, you
6 | accept this license. If you do not accept the license, do not use the software.
7 |
8 | 1. Definitions
9 | The terms "reproduce," "reproduction," "derivative works," and "distribution" have the
10 | same meaning here as under U.S. copyright law.
11 | A "contribution" is the original software, or any additions or changes to the software.
12 | A "contributor" is any person that distributes its contribution under this license.
13 | "Licensed patents" are a contributor's patent claims that read directly on its contribution.
14 |
15 | 2. Grant of Rights
16 | (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
17 | (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
18 |
19 | 3. Conditions and Limitations
20 | (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
21 | (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
22 | (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
23 | (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
24 | (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Active Directory Object Picker [](https://ci.appveyor.com/project/Boardgent/active-directory-object-picker)
2 |
3 | ========================
4 | ### The standard Active Directory object picker dialog for .NET
5 |
6 | 
7 |
8 |
9 | This project is based on a [Active Directory Common Dialogs .NET (ADUI)](https://adui.codeplex.com/) created in 2004 by Armand du Plessis. It has been updated and make it working with 64 bit Windows Editions.
10 | Now it works in all .Net framework versions, including version 2.0, 3.5, 4.0, 4.5 and .NET Core.
11 |
12 | ### How to use it
13 | You can install the latest version using [NuGet](https://www.nuget.org/packages/Tulpep.ActiveDirectoryObjectPicker/)
14 | ```powershell
15 | Install-Package Tulpep.ActiveDirectoryObjectPicker
16 | ```
17 |
18 | And use it this way:
19 | ```cs
20 | DirectoryObjectPickerDialog picker = new DirectoryObjectPickerDialog()
21 | {
22 | AllowedObjectTypes = ObjectTypes.Users | ObjectTypes.Groups | ObjectTypes.Computers,
23 | DefaultObjectTypes = ObjectTypes.Computers,
24 | AllowedLocations = Locations.All,
25 | DefaultLocations = Locations.JoinedDomain,
26 | MultiSelect = true,
27 | ShowAdvancedView = true
28 | };
29 | using (picker)
30 | {
31 | if (picker.ShowDialog() == DialogResult.OK)
32 | {
33 | foreach (var sel in picker.SelectedObjects)
34 | {
35 | Console.WriteLine(sel.Name);
36 | }
37 | }
38 | }
39 | ```
40 | This repository contains a Visual Studio Test Project if you want a working example.
41 |
--------------------------------------------------------------------------------
/Screenshots/ADPickerTester.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tulpep/Active-Directory-Object-Picker/7888679b16e929b750b6b56b3f931162fa674dc9/Screenshots/ADPickerTester.gif
--------------------------------------------------------------------------------
/Screenshots/img01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tulpep/Active-Directory-Object-Picker/7888679b16e929b750b6b56b3f931162fa674dc9/Screenshots/img01.png
--------------------------------------------------------------------------------
/Screenshots/img02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tulpep/Active-Directory-Object-Picker/7888679b16e929b750b6b56b3f931162fa674dc9/Screenshots/img02.png
--------------------------------------------------------------------------------
/Screenshots/img03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tulpep/Active-Directory-Object-Picker/7888679b16e929b750b6b56b3f931162fa674dc9/Screenshots/img03.png
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/ADsPathsProviders.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Tulpep.ActiveDirectoryObjectPicker
4 | {
5 | ///
6 | /// Indicates the ADsPaths provider type of the DirectoryObjectPickerDialog. This provider affects the contents of the ADPath returned
7 | ///
8 | [Flags]
9 | public enum ADsPathsProviders
10 | {
11 | /// Default provider.
12 | Default = 0,
13 |
14 | ///
15 | /// The ADsPaths are converted to use the WinNT provider.
16 | ///
17 | /// The ADsPath string for the ADSI WinNT provider can be one of the following forms:
18 | ///
19 | ///WinNT:
20 | ///WinNT://<domain name>
21 | ///WinNT://<domain name>/<server>
22 | ///WinNT://<domain name>/<path>
23 | ///WinNT://<domain name>/<object name>
24 | ///WinNT://<domain name>/<object name>,<object class>
25 | ///WinNT://<server>
26 | ///WinNT://<server>/<object name>
27 | ///WinNT://<server>/<object name>,<object class>
28 | ///
29 | /// The domain name can be either a NETBIOS name or a DNS name. The server is the name of a specific server within the domain. The
30 | /// path is the path of on object, such as "printserver1/printer2". The object name is the name of a specific object. The object
31 | /// class is the class name of the named object. One example of this usage would be "WinNT://MyServer/JeffSmith,user". Specifying a
32 | /// class name can improve the performance of the bind operation.
33 | ///
34 | WinNT = 0x00000002,
35 |
36 | ///
37 | /// The ADsPaths are converted to use the LDAP provider.
38 | /// The Microsoft LDAP provider ADsPath requires the following format.
39 | /// LDAP://HostName[:PortNumber][/DistinguishedName]
40 | /// Further info, see http://msdn.microsoft.com/en-us/library/aa746384(v=VS.85).aspx.
41 | ///
42 | LDAP = 0x00000004,
43 |
44 | /// The ADsPaths for objects selected from this scope are converted to use the GC provider.
45 | GC = 0x00000008,
46 |
47 | ///
48 | /// The ADsPaths having an objectSid attribute are converted to the form
49 | ///
50 | ///LDAP://<SID=x>
51 | ///
52 | /// where x represents the hexadecimal digits of the objectSid attribute value.
53 | ///
54 | SIDPath = 0x00000010,
55 |
56 | ///
57 | /// The ADsPaths for down-level, well-known SID objects are an empty string unless this flag is specified (For example;
58 | /// DSOP_DOWNLEVEL_FILTER_INTERACTIVE). If this flag is specified, the paths have the form:
59 | /// WinNT://NT AUTHORITY/Interactive or WinNT://Creator owner.
60 | ///
61 | DownlevelBuiltinPath = 0x00000020,
62 |
63 | /// Use DownlevelBuiltinPath instead.
64 | [Obsolete("Use DownlevelBuiltinPath instead.")]
65 | DownlevelBuildinPath = 0x00000020
66 | }
67 | }
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/ComInterop.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | // ReSharper disable InconsistentNaming -- those names are from Windows/COM
5 | namespace Tulpep.ActiveDirectoryObjectPicker
6 | {
7 | /// The object picker dialog box.
8 | [ComImport, Guid("17D6CCD8-3B7B-11D2-B9E0-00C04FD8DBF7")]
9 | internal class DSObjectPicker
10 | {
11 | }
12 |
13 | /// The IDsObjectPicker interface is used by an application to initialize and display an object picker dialog box.
14 | [ComImport, Guid("0C87E64E-3B7A-11D2-B9E0-00C04FD8DBF7"),
15 | InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
16 | internal interface IDsObjectPicker
17 | {
18 | [PreserveSig]
19 | int Initialize(ref DSOP_INIT_INFO pInitInfo);
20 |
21 | [PreserveSig]
22 | int InvokeDialog(IntPtr HWND, out IDataObject lpDataObject);
23 | }
24 |
25 | [ComImport, Guid("e2d3ec9b-d041-445a-8f16-4748de8fb1cf"),
26 | InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
27 | internal interface IDsObjectPickerCredentials
28 | {
29 | [PreserveSig]
30 | int Initialize(ref DSOP_INIT_INFO pInitInfo);
31 |
32 | [PreserveSig]
33 | int InvokeDialog(IntPtr HWND, out IDataObject lpDataObject);
34 |
35 | [PreserveSig]
36 | int SetCredentials(string userName, string password);
37 | }
38 |
39 | /// Interface to enable data transfers
40 | [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
41 | Guid("0000010e-0000-0000-C000-000000000046")]
42 | internal interface IDataObject
43 | {
44 | [PreserveSig]
45 | int GetData(ref FORMATETC pFormatEtc, ref STGMEDIUM b);
46 |
47 | void GetDataHere(ref FORMATETC pFormatEtc, ref STGMEDIUM b);
48 |
49 | [PreserveSig]
50 | int QueryGetData(IntPtr a);
51 |
52 | [PreserveSig]
53 | int GetCanonicalFormatEtc(IntPtr a, IntPtr b);
54 |
55 | [PreserveSig]
56 | int SetData(IntPtr a, IntPtr b, int c);
57 |
58 | [PreserveSig]
59 | int EnumFormatEtc(uint a, IntPtr b);
60 |
61 | [PreserveSig]
62 | int DAdvise(IntPtr a, uint b, IntPtr c, ref uint d);
63 |
64 | [PreserveSig]
65 | int DUnadvise(uint a);
66 |
67 | [PreserveSig]
68 | int EnumDAdvise(IntPtr a);
69 | }
70 |
71 | [ComImport, Guid("9068270b-0939-11d1-8be1-00c04fd8d503")]
72 | internal interface IAdsLargeInteger
73 | {
74 | long HighPart { get; set; }
75 | long LowPart { get; set; }
76 | }
77 |
78 | [ComImport, Guid("274fae1f-3626-11d1-a3a4-00c04fb950dc")]
79 | internal class NameTranslate
80 | {
81 | }
82 |
83 | [Guid("B1B272A3-3625-11D1-A3A4-00C04FB950DC"),
84 | #pragma warning disable CS0618 // Type or member is obsolete
85 | InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
86 | #pragma warning restore CS0618 // Type or member is obsolete
87 | internal interface IADsNameTranslate
88 | {
89 | [DispId(1)]
90 | int ChaseReferral { set; }
91 |
92 | [DispId(5)]
93 | string Get(int lnFormatType);
94 |
95 | [DispId(7)]
96 | object GetEx(int lnFormatType);
97 |
98 | [DispId(2)]
99 | void Init(int lnSetType, string bstrADsPath);
100 |
101 | [DispId(3)]
102 | void InitEx(int lnSetType, string bstrADsPath, string bstrUserID, string bstrDomain, string bstrPassword);
103 |
104 | [DispId(4)]
105 | void Set(int lnSetType, string bstrADsPath);
106 |
107 | [DispId(6)]
108 | void SetEx(int lnFormatType, object pVar);
109 | }
110 | }
111 |
112 | // ReSharper restore InconsistentNaming
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/DirectoryObject.cs:
--------------------------------------------------------------------------------
1 | namespace Tulpep.ActiveDirectoryObjectPicker
2 | {
3 | /// Details of a directory object selected in the DirectoryObjectPickerDialog.
4 | public class DirectoryObject
5 | {
6 | /// Initializes a new instance of the class.
7 | /// The directory object's relative distinguished name (RDN).
8 | /// The Active Directory path for this directory object.
9 | /// The name of the schema class for this directory object (objectClass attribute).
10 | /// The objects user principal name (userPrincipalName attribute).
11 | /// The attributes retrieved by the object picker as it makes the selection.
12 | public DirectoryObject(string name, string path, string schemaClass, string upn, object[] fetchedAttributes)
13 | {
14 | this.Name = name;
15 | this.Path = path;
16 | this.SchemaClassName = schemaClass;
17 | this.Upn = upn;
18 | this.FetchedAttributes = fetchedAttributes;
19 | }
20 |
21 | /// Gets attributes retrieved by the object picker as it makes the selection.
22 | public object[] FetchedAttributes { get; private set; }
23 |
24 | /// Gets the directory object's relative distinguished name (RDN).
25 | public string Name { get; private set; }
26 |
27 | /// Gets the Active Directory path for this directory object.
28 | ///
29 | ///
30 | /// The format of this string depends on the options specified in the DirectoryObjectPickerDialog from which this object was selected.
31 | ///
32 | ///
33 | public string Path { get; private set; }
34 |
35 | /// Gets the name of the schema class for this directory object (objectClass attribute).
36 | public string SchemaClassName { get; private set; }
37 |
38 | /// Gets the objects user principal name (userPrincipalName attribute).
39 | ///
40 | /// If the object does not have a userPrincipalName value, this property is an empty string.
41 | ///
42 | public string Upn { get; private set; }
43 | }
44 | }
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/DirectoryObjectPickerDialog.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2004 Armand du Plessis http://dotnet.org.za/armand/articles/2453.aspx Thanks to Joe Cataford and
2 | // Friedrich Brunzema. Also see MSDN: http://msdn2.microsoft.com/en-us/library/ms675899.aspx Enhancements by sgryphon@computer.org (PACOM):
3 | // - Integrated with the CommonDialog API, e.g. returns a DialogResult, changed namespace, etc.
4 | // - Marked COM interop code as internal; only the main dialog (and related classes) are public.
5 | // - Added basic scope (location) and filter (object type) control, plus multi-select flag.
6 |
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Collections.ObjectModel;
10 | using System.ComponentModel;
11 | using System.Runtime.InteropServices;
12 | using System.Windows.Forms;
13 |
14 | namespace Tulpep.ActiveDirectoryObjectPicker
15 | {
16 | /// Represents a common dialog that allows a user to select directory objects.
17 | ///
18 | ///
19 | /// The directory object picker dialog box enables a user to select one or more objects from either the global catalog, a Microsoft
20 | /// Windows 2000 domain or computer, a Microsoft Windows NT 4.0 domain or computer, or a workgroup. The object types from which a user
21 | /// can select include user, contact, group, and computer objects.
22 | ///
23 | /// This managed class wraps the Directory Object Picker common dialog from the Active Directory UI.
24 | ///
25 | /// It simplifies the scope (Locations) and filter (ObjectTypes) selection by allowing a single filter to be specified which applies to
26 | /// all scopes (translating to both up-level and down-level filter flags as necessary).
27 | ///
28 | ///
29 | /// The object type filter is also simplified by combining different types of groups (local, global, etc) and not using individual well
30 | /// known types in down-level scopes (only all well known types can be specified).
31 | ///
32 | ///
33 | /// The scope location is also simplified by combining down-level and up-level variations into a single locations flag, e.g. external domains.
34 | ///
35 | ///
36 | [DefaultProperty(nameof(DefaultObjectTypes))]
37 | public class DirectoryObjectPickerDialog : CommonDialog
38 | {
39 | private DirectoryObject[] selectedObjects;
40 | private string userName, password;
41 |
42 | /// Constructor. Sets all properties to their default values.
43 | ///
44 | /// The default values for the DirectoryObjectPickerDialog properties are:
45 | ///
46 | ///
47 | ///
48 | /// Property
49 | /// Default value
50 | ///
51 | /// -
52 | /// AllowedLocations
53 | /// All locations.
54 | ///
55 | /// -
56 | /// AllowedObjectTypes
57 | /// All object types.
58 | ///
59 | /// -
60 | /// DefaultLocations
61 | /// None. (Will default to first location.)
62 | ///
63 | /// -
64 | /// DefaultObjectTypes
65 | /// All object types.
66 | ///
67 | /// -
68 | /// Providers
69 | /// .
70 | ///
71 | /// -
72 | /// MultiSelect
73 | /// false.
74 | ///
75 | /// -
76 | /// SkipDomainControllerCheck
77 | /// false.
78 | ///
79 | /// -
80 | /// AttributesToFetch
81 | /// Empty list.
82 | ///
83 | /// -
84 | /// SelectedObject
85 | /// null.
86 | ///
87 | /// -
88 | /// SelectedObjects
89 | /// Empty array.
90 | ///
91 | /// -
92 | /// ShowAdvancedView
93 | /// false.
94 | ///
95 | /// -
96 | /// TargetComputer
97 | /// null.
98 | ///
99 | ///
100 | ///
101 | ///
102 | public DirectoryObjectPickerDialog() => ResetInner();
103 |
104 | /// Gets or sets the scopes the DirectoryObjectPickerDialog is allowed to search.
105 | [Category("Behavior"), DefaultValue(typeof(Locations), "All"), Description("The scopes the dialog is allowed to search.")]
106 | public Locations AllowedLocations { get; set; }
107 |
108 | /// Gets or sets the types of objects the DirectoryObjectPickerDialog is allowed to search for.
109 | [Category("Behavior"), DefaultValue(typeof(ObjectTypes), "All"), Description("The types of objects the dialog is allowed to search for.")]
110 | public ObjectTypes AllowedObjectTypes { get; set; }
111 |
112 | /// A list of LDAP attribute names that will be retrieved for picked objects.
113 | [Category("Behavior"), Description("A list of LDAP attribute names that will be retrieved for picked objects.")]
114 | [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
115 | public Collection AttributesToFetch { get; set; }
116 |
117 | /// Gets or sets the initially selected scope in the DirectoryObjectPickerDialog.
118 | [Category("Behavior"), DefaultValue(typeof(Locations), "None"), Description("The initially selected scope in the dialog.")]
119 | public Locations DefaultLocations { get; set; }
120 |
121 | /// Gets or sets the initially seleted types of objects in the DirectoryObjectPickerDialog.
122 | [Category("Behavior"), DefaultValue(typeof(ObjectTypes), "All"), Description("The initially selected types of objects in the dialog.")]
123 | public ObjectTypes DefaultObjectTypes { get; set; }
124 |
125 | /// Gets or sets whether the user can select multiple objects.
126 | ///
127 | /// If this flag is false, the user can select only one object.
128 | ///
129 | [Category("Behavior"), DefaultValue(false), Description("Whether the user can select multiple objects.")]
130 | public bool MultiSelect { get; set; }
131 |
132 | /// Gets or sets the providers affecting the ADPath returned in objects.
133 | [Category("Behavior"), DefaultValue(typeof(ADsPathsProviders), "Default"), Description("The providers affecting the ADPath returned in objects.")]
134 | public ADsPathsProviders Providers { get; set; }
135 |
136 | /// Gets the directory object selected in the dialog, or null if no object was selected.
137 | ///
138 | ///
139 | /// If MultiSelect is enabled, then this property returns only the first selected object. Use SelectedObjects to get an array of all
140 | /// objects selected.
141 | ///
142 | ///
143 | [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
144 | public DirectoryObject SelectedObject => selectedObjects == null || selectedObjects.Length == 0 ? null : selectedObjects[0];
145 |
146 | /// Gets an array of the directory objects selected in the dialog.
147 | [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
148 | public DirectoryObject[] SelectedObjects => selectedObjects == null ? (new DirectoryObject[0]) : (DirectoryObject[])selectedObjects.Clone();
149 |
150 | /// Gets or sets whether objects flagged as show in advanced view only are displayed (up-level).
151 | [Category("Appearance"), DefaultValue(false), Description("Whether objects flagged as show in advanced view only are displayed (up-level).")]
152 | public bool ShowAdvancedView { get; set; }
153 |
154 | /// Gets or sets the whether to check whether the target is a Domain Controller and hide the "Local Computer" scope
155 | ///
156 | ///
157 | /// From MSDN:
158 | ///
159 | /// If this flag is NOT set, then the DSOP_SCOPE_TYPE_TARGET_COMPUTER flag will be ignored if the target computer is a DC. This flag
160 | /// has no effect unless DSOP_SCOPE_TYPE_TARGET_COMPUTER is specified.
161 | ///
162 | ///
163 | [Category("Behavior"), DefaultValue(false), Description("Whether to check whether the target is a Domain Controller and hide the 'Local Computer' scope.")]
164 | public bool SkipDomainControllerCheck { get; set; }
165 |
166 | /// Gets or sets the name of the target computer.
167 | ///
168 | ///
169 | /// The dialog box operates as if it is running on the target computer, using the target computer to determine the joined domain and
170 | /// enterprise. If this value is null or empty, the target computer is the local computer.
171 | ///
172 | ///
173 | [Category("Behavior"), DefaultValue(null), Description("The name of the target computer.")]
174 | public string TargetComputer { get; set; }
175 |
176 | /// Resets all properties to their default values.
177 | public override void Reset() => ResetInner();
178 |
179 | /// Use this method to override the user credentials, passing new credentials for the account profile to be used.
180 | /// Name of the user.
181 | /// The password for the user.
182 | public void SetCredentials(string userName, string password)
183 | {
184 | this.userName = userName;
185 | this.password = password;
186 | }
187 |
188 | /// Displays the Directory Object Picker (Active Directory) common dialog, when called by ShowDialog.
189 | /// Handle to the window that owns the dialog.
190 | ///
191 | /// If the user clicks the OK button of the Directory Object Picker dialog that is displayed, true is returned; otherwise, false.
192 | ///
193 | protected override bool RunDialog(IntPtr hwndOwner)
194 | {
195 | var ipicker = Initialize();
196 | if (ipicker == null)
197 | {
198 | selectedObjects = null;
199 | return false;
200 | }
201 |
202 | try
203 | {
204 | IDataObject dataObj = null;
205 | var hresult = ipicker.InvokeDialog(hwndOwner, out dataObj);
206 | if (hresult == HRESULT.S_OK)
207 | {
208 | selectedObjects = ProcessSelections(dataObj);
209 | Marshal.ReleaseComObject(dataObj);
210 | return true;
211 | }
212 | if (hresult == HRESULT.S_FALSE)
213 | {
214 | selectedObjects = null;
215 | return false;
216 | }
217 | throw new COMException("IDsObjectPicker.InvokeDialog failed", hresult);
218 | }
219 | finally
220 | {
221 | Marshal.ReleaseComObject(ipicker);
222 | }
223 | }
224 |
225 | // Convert Locations to DSOP_SCOPE_TYPE_FLAGS
226 | private static uint GetScope(Locations locations)
227 | {
228 | uint scope = 0;
229 | if (locations.IsFlagSet(Locations.LocalComputer))
230 | {
231 | scope |= DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_TARGET_COMPUTER;
232 | }
233 | if (locations.IsFlagSet(Locations.JoinedDomain))
234 | {
235 | scope |= DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN |
236 | DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN;
237 | }
238 | if (locations.IsFlagSet(Locations.EnterpriseDomain))
239 | {
240 | scope |= DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN;
241 | }
242 | if (locations.IsFlagSet(Locations.GlobalCatalog))
243 | {
244 | scope |= DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_GLOBAL_CATALOG;
245 | }
246 | if (locations.IsFlagSet(Locations.ExternalDomain))
247 | {
248 | scope |= DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN |
249 | DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN;
250 | }
251 | if (locations.IsFlagSet(Locations.Workgroup))
252 | {
253 | scope |= DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_WORKGROUP;
254 | }
255 | if (locations.IsFlagSet(Locations.UserEntered))
256 | {
257 | scope |= DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_USER_ENTERED_DOWNLEVEL_SCOPE |
258 | DSOP_SCOPE_TYPE_FLAGS.DSOP_SCOPE_TYPE_USER_ENTERED_UPLEVEL_SCOPE;
259 | }
260 | return scope;
261 | }
262 |
263 | // Convert ObjectTypes to DSCOPE_SCOPE_INIT_INFO_FLAGS
264 | private uint GetDefaultFilter()
265 | {
266 | uint defaultFilter = 0;
267 | if (DefaultObjectTypes.IsFlagSet(ObjectTypes.Users) || DefaultObjectTypes.IsFlagSet(ObjectTypes.WellKnownPrincipals))
268 | {
269 | defaultFilter |= DSOP_SCOPE_INIT_INFO_FLAGS.DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS;
270 | }
271 | if (DefaultObjectTypes.IsFlagSet(ObjectTypes.Groups) || DefaultObjectTypes.IsFlagSet(ObjectTypes.BuiltInGroups))
272 | {
273 | defaultFilter |= DSOP_SCOPE_INIT_INFO_FLAGS.DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS;
274 | }
275 | if (DefaultObjectTypes.IsFlagSet(ObjectTypes.Computers))
276 | {
277 | defaultFilter |= DSOP_SCOPE_INIT_INFO_FLAGS.DSOP_SCOPE_FLAG_DEFAULT_FILTER_COMPUTERS;
278 | }
279 | if (DefaultObjectTypes.IsFlagSet(ObjectTypes.Contacts))
280 | {
281 | defaultFilter |= DSOP_SCOPE_INIT_INFO_FLAGS.DSOP_SCOPE_FLAG_DEFAULT_FILTER_CONTACTS;
282 | }
283 | if (DefaultObjectTypes.IsFlagSet(ObjectTypes.ServiceAccounts))
284 | {
285 | defaultFilter |= DSOP_SCOPE_INIT_INFO_FLAGS.DSOP_SCOPE_FLAG_DEFAULT_FILTER_SERVICE_ACCOUNTS;
286 | }
287 | return defaultFilter;
288 | }
289 |
290 | // Convert ObjectTypes to DSOP_DOWNLEVEL_FLAGS
291 | private uint GetDownLevelFilter()
292 | {
293 | uint downlevelFilter = 0;
294 | if (AllowedObjectTypes.IsFlagSet(ObjectTypes.Users))
295 | {
296 | downlevelFilter |= DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_USERS;
297 | }
298 | if (AllowedObjectTypes.IsFlagSet(ObjectTypes.Groups))
299 | {
300 | downlevelFilter |= DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_LOCAL_GROUPS |
301 | DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS;
302 | }
303 | if (AllowedObjectTypes.IsFlagSet(ObjectTypes.Computers))
304 | {
305 | downlevelFilter |= DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_COMPUTERS;
306 | }
307 | // Contacts not available in downlevel scopes
308 | //if ((allowedTypes & ObjectTypes.Contacts) == ObjectTypes.Contacts)
309 | // Exclude build in groups if not selected
310 | if ((AllowedObjectTypes & ObjectTypes.BuiltInGroups) == 0)
311 | {
312 | downlevelFilter |= DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_EXCLUDE_BUILTIN_GROUPS;
313 | }
314 | if (AllowedObjectTypes.IsFlagSet(ObjectTypes.WellKnownPrincipals))
315 | {
316 | downlevelFilter |= DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_ALL_WELLKNOWN_SIDS;
317 | // This includes all the following:
318 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_WORLD |
319 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_AUTHENTICATED_USER |
320 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_ANONYMOUS |
321 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_BATCH |
322 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_CREATOR_OWNER |
323 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_CREATOR_GROUP |
324 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_DIALUP |
325 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_INTERACTIVE |
326 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_NETWORK |
327 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_SERVICE |
328 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_SYSTEM |
329 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_TERMINAL_SERVER |
330 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_LOCAL_SERVICE |
331 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_NETWORK_SERVICE |
332 | //DSOP_DOWNLEVEL_FLAGS.DSOP_DOWNLEVEL_FILTER_REMOTE_LOGON;
333 | }
334 | return downlevelFilter;
335 | }
336 |
337 | private object[] GetFetchedAttributes(IntPtr pvarFetchedAttributes, int cFetchedAttributes, string schemaClassName)
338 | {
339 | var fetchedAttributes = cFetchedAttributes > 0 ? Marshal.GetObjectsForNativeVariants(pvarFetchedAttributes, cFetchedAttributes) : (new object[0]);
340 | for (var i = 0; i < fetchedAttributes.Length; i++)
341 | {
342 | if (fetchedAttributes[i] is IAdsLargeInteger largeInteger)
343 | {
344 | var l = largeInteger.HighPart * 0x100000000L + (uint)largeInteger.LowPart;
345 | fetchedAttributes[i] = l;
346 | }
347 |
348 | if (AttributesToFetch[i].Equals("objectClass", StringComparison.OrdinalIgnoreCase)) // see comments in Initialize() function
349 | fetchedAttributes[i] = schemaClassName;
350 | }
351 |
352 | return fetchedAttributes;
353 | }
354 |
355 | // Convert ADsPathsProviders to DSOP_SCOPE_INIT_INFO_FLAGS
356 | private uint GetProviderFlags()
357 | {
358 | uint scope = 0;
359 | if (Providers.IsFlagSet(ADsPathsProviders.WinNT))
360 | scope |= DSOP_SCOPE_INIT_INFO_FLAGS.DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT;
361 |
362 | if (Providers.IsFlagSet(ADsPathsProviders.LDAP))
363 | scope |= DSOP_SCOPE_INIT_INFO_FLAGS.DSOP_SCOPE_FLAG_WANT_PROVIDER_LDAP;
364 |
365 | if (Providers.IsFlagSet(ADsPathsProviders.GC))
366 | scope |= DSOP_SCOPE_INIT_INFO_FLAGS.DSOP_SCOPE_FLAG_WANT_PROVIDER_GC;
367 |
368 | if (Providers.IsFlagSet(ADsPathsProviders.SIDPath))
369 | scope |= DSOP_SCOPE_INIT_INFO_FLAGS.DSOP_SCOPE_FLAG_WANT_SID_PATH;
370 |
371 | if (Providers.IsFlagSet(ADsPathsProviders.DownlevelBuiltinPath))
372 | scope |= DSOP_SCOPE_INIT_INFO_FLAGS.DSOP_SCOPE_FLAG_WANT_DOWNLEVEL_BUILTIN_PATH;
373 |
374 | return scope;
375 | }
376 |
377 | // Convert ObjectTypes to DSOP_FILTER_FLAGS_FLAGS
378 | private uint GetUpLevelFilter()
379 | {
380 | uint uplevelFilter = 0;
381 | if (AllowedObjectTypes.IsFlagSet(ObjectTypes.Users))
382 | {
383 | uplevelFilter |= DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_USERS;
384 | }
385 | if (AllowedObjectTypes.IsFlagSet(ObjectTypes.Groups))
386 | {
387 | uplevelFilter |= DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_UNIVERSAL_GROUPS_DL |
388 | DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_UNIVERSAL_GROUPS_SE |
389 | DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_GLOBAL_GROUPS_DL |
390 | DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_GLOBAL_GROUPS_SE |
391 | DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL |
392 | DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE;
393 | }
394 | if (AllowedObjectTypes.IsFlagSet(ObjectTypes.Computers))
395 | {
396 | uplevelFilter |= DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_COMPUTERS;
397 | }
398 | if (AllowedObjectTypes.IsFlagSet(ObjectTypes.Contacts))
399 | {
400 | uplevelFilter |= DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_CONTACTS;
401 | }
402 | if (AllowedObjectTypes.IsFlagSet(ObjectTypes.BuiltInGroups))
403 | {
404 | uplevelFilter |= DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_BUILTIN_GROUPS;
405 | }
406 | if (AllowedObjectTypes.IsFlagSet(ObjectTypes.WellKnownPrincipals))
407 | {
408 | uplevelFilter |= DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_WELL_KNOWN_PRINCIPALS;
409 | }
410 | if (AllowedObjectTypes.IsFlagSet(ObjectTypes.ServiceAccounts))
411 | {
412 | uplevelFilter |= DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_SERVICE_ACCOUNTS;
413 | }
414 | if (ShowAdvancedView)
415 | {
416 | uplevelFilter |= DSOP_FILTER_FLAGS_FLAGS.DSOP_FILTER_INCLUDE_ADVANCED_VIEW;
417 | }
418 | return uplevelFilter;
419 | }
420 |
421 | private IDsObjectPicker Initialize()
422 | {
423 | var picker = new DSObjectPicker();
424 | var ipicker = (IDsObjectPicker)picker;
425 |
426 | var scopeInitInfoList = new List();
427 |
428 | // Note the same default and filters are used by all scopes
429 | var defaultFilter = GetDefaultFilter();
430 | var upLevelFilter = GetUpLevelFilter();
431 | var downLevelFilter = GetDownLevelFilter();
432 | var providerFlags = GetProviderFlags();
433 |
434 | // Internall, use one scope for the default (starting) locations.
435 | var startingScope = GetScope(DefaultLocations);
436 | if (startingScope > 0)
437 | {
438 | var startingScopeInfo = new DSOP_SCOPE_INIT_INFO
439 | {
440 | cbSize = (uint)Marshal.SizeOf(typeof(DSOP_SCOPE_INIT_INFO)),
441 | flType = startingScope,
442 | flScope = DSOP_SCOPE_INIT_INFO_FLAGS.DSOP_SCOPE_FLAG_STARTING_SCOPE | defaultFilter | providerFlags,
443 | pwzADsPath = null,
444 | pwzDcName = null,
445 | hr = 0,
446 | };
447 | startingScopeInfo.FilterFlags.Uplevel.flBothModes = upLevelFilter;
448 | startingScopeInfo.FilterFlags.flDownlevel = downLevelFilter;
449 | scopeInitInfoList.Add(startingScopeInfo);
450 | }
451 |
452 | // And another scope for all other locations (AllowedLocation values not in DefaultLocation)
453 | var otherLocations = AllowedLocations & (~DefaultLocations);
454 | var otherScope = GetScope(otherLocations);
455 | if (otherScope > 0)
456 | {
457 | var otherScopeInfo = new DSOP_SCOPE_INIT_INFO
458 | {
459 | cbSize = (uint)Marshal.SizeOf(typeof(DSOP_SCOPE_INIT_INFO)),
460 | flType = otherScope,
461 | flScope = defaultFilter | providerFlags,
462 | pwzADsPath = null,
463 | pwzDcName = null,
464 | hr = 0
465 | };
466 | otherScopeInfo.FilterFlags.Uplevel.flBothModes = upLevelFilter;
467 | otherScopeInfo.FilterFlags.flDownlevel = downLevelFilter;
468 | scopeInitInfoList.Add(otherScopeInfo);
469 | }
470 |
471 | var scopeInitInfo = scopeInitInfoList.ToArray();
472 |
473 | // TODO: Scopes for alternate ADs, alternate domains, alternate computers, etc
474 |
475 | // Allocate memory from the unmananged mem of the process, this should be freed later!??
476 | var refScopeInitInfo = Marshal.AllocHGlobal
477 | (Marshal.SizeOf(typeof(DSOP_SCOPE_INIT_INFO)) * scopeInitInfo.Length);
478 |
479 | // Marshal structs to pointers
480 | for (var index = 0; index < scopeInitInfo.Length; index++)
481 | {
482 | Marshal.StructureToPtr(scopeInitInfo[index],
483 | refScopeInitInfo.OffsetWith(index * Marshal.SizeOf(typeof(DSOP_SCOPE_INIT_INFO))),
484 | false);
485 | }
486 |
487 | // Initialize structure with data to initialize an object picker dialog box.
488 | var initInfo = new DSOP_INIT_INFO
489 | {
490 | cbSize = (uint)Marshal.SizeOf(typeof(DSOP_INIT_INFO)),
491 | pwzTargetComputer = TargetComputer,
492 | cDsScopeInfos = (uint)scopeInitInfo.Length,
493 | aDsScopeInfos = refScopeInitInfo,
494 | };
495 | // Flags that determine the object picker options.
496 | uint flOptions = 0;
497 | if (MultiSelect)
498 | {
499 | flOptions |= DSOP_INIT_INFO_FLAGS.DSOP_FLAG_MULTISELECT;
500 | }
501 | // Only set DSOP_INIT_INFO_FLAGS.DSOP_FLAG_SKIP_TARGET_COMPUTER_DC_CHECK if we know target is not a DC (which then saves
502 | // initialization time).
503 | if (SkipDomainControllerCheck)
504 | {
505 | flOptions |= DSOP_INIT_INFO_FLAGS.DSOP_FLAG_SKIP_TARGET_COMPUTER_DC_CHECK;
506 | }
507 | initInfo.flOptions = flOptions;
508 |
509 | // there's a (seeming?) bug on my Windows XP when fetching the objectClass attribute - the pwzClass field is corrupted... plus,
510 | // it returns a multivalued array for this attribute. In Windows 2008 R2, however, only last value is returned, just as in
511 | // pwzClass. So won't actually be retrieving __objectClass__ - will give pwzClass instead
512 | var goingToFetch = new List(AttributesToFetch);
513 | for (var i = 0; i < goingToFetch.Count; i++)
514 | {
515 | if (goingToFetch[i].Equals("objectClass", StringComparison.OrdinalIgnoreCase))
516 | goingToFetch[i] = "__objectClass__";
517 | }
518 |
519 | initInfo.cAttributesToFetch = (uint)goingToFetch.Count;
520 | var unmanagedAttributesToFetch = new UnmanagedArrayOfStrings(goingToFetch);
521 | initInfo.apwzAttributeNames = unmanagedAttributesToFetch.ArrayPtr;
522 |
523 | // If the user has defined new credentials, set them now
524 | if (!string.IsNullOrEmpty(userName))
525 | {
526 | var cred = (IDsObjectPickerCredentials)ipicker;
527 | cred.SetCredentials(userName, password);
528 | }
529 |
530 | try
531 | {
532 | // Initialize the Object Picker Dialog Box with our options
533 | var hresult = ipicker.Initialize(ref initInfo);
534 | if (hresult != HRESULT.S_OK)
535 | {
536 | Marshal.ReleaseComObject(ipicker);
537 | throw new COMException("IDsObjectPicker.Initialize failed", hresult);
538 | }
539 | return ipicker;
540 | }
541 | finally
542 | {
543 | /*
544 | from MSDN (http://msdn.microsoft.com/en-us/library/ms675899(VS.85).aspx):
545 |
546 | Initialize can be called multiple times, but only the last call has effect.
547 | Be aware that object picker makes its own copy of InitInfo.
548 | */
549 | Marshal.FreeHGlobal(refScopeInitInfo);
550 | unmanagedAttributesToFetch.Dispose();
551 | }
552 | }
553 |
554 | private DirectoryObject[] ProcessSelections(IDataObject dataObj)
555 | {
556 | if (dataObj == null)
557 | return null;
558 |
559 | DirectoryObject[] selections = null;
560 |
561 | // The STGMEDIUM structure is a generalized global memory handle used for data transfer operations
562 | var stg = new STGMEDIUM
563 | {
564 | tymed = (uint)TYMED.TYMED_HGLOBAL,
565 | hGlobal = IntPtr.Zero,
566 | pUnkForRelease = IntPtr.Zero
567 | };
568 |
569 | // The FORMATETC structure is a generalized Clipboard format.
570 | var fe = new FORMATETC
571 | {
572 | cfFormat = DataFormats.GetFormat(CLIPBOARD_FORMAT.CFSTR_DSOP_DS_SELECTION_LIST).Id,
573 | // The CFSTR_DSOP_DS_SELECTION_LIST clipboard format is provided by the IDataObject obtained by calling IDsObjectPicker::InvokeDialog
574 | ptd = IntPtr.Zero,
575 | dwAspect = 1, //DVASPECT.DVASPECT_CONTENT = 1,
576 | lindex = -1, // all of the data
577 | tymed = (uint)TYMED.TYMED_HGLOBAL //The storage medium is a global memory handle (HGLOBAL)
578 | };
579 |
580 | var hresult = dataObj.GetData(ref fe, ref stg);
581 | if (hresult != HRESULT.S_OK) throw new COMException("IDataObject.GetData failed", hresult);
582 |
583 | var pDsSL = PInvoke.GlobalLock(stg.hGlobal);
584 | if (pDsSL == IntPtr.Zero) throw new Win32Exception("GlobalLock(stg.hGlobal) failed");
585 |
586 | try
587 | {
588 | // the start of our structure
589 | var current = pDsSL;
590 | // get the # of items selected
591 | var cnt = Marshal.ReadInt32(current);
592 | var cFetchedAttributes = Marshal.ReadInt32(current, Marshal.SizeOf(typeof(uint)));
593 |
594 | // if we selected at least 1 object
595 | if (cnt > 0)
596 | {
597 | selections = new DirectoryObject[cnt];
598 | // increment the pointer so we can read the DS_SELECTION structure
599 | current = current.OffsetWith(Marshal.SizeOf(typeof(uint)) * 2);
600 | // now loop through the structures
601 | for (var i = 0; i < cnt; i++)
602 | {
603 | // marshal the pointer to the structure
604 | var s = (DS_SELECTION)Marshal.PtrToStructure(current, typeof(DS_SELECTION));
605 |
606 | // increment the position of our pointer by the size of the structure
607 | current = current.OffsetWith(Marshal.SizeOf(typeof(DS_SELECTION)));
608 |
609 | var name = s.pwzName;
610 | var path = s.pwzADsPath;
611 | var schemaClassName = s.pwzClass;
612 | var upn = s.pwzUPN;
613 | var fetchedAttributes = GetFetchedAttributes(s.pvarFetchedAttributes, cFetchedAttributes, schemaClassName);
614 |
615 | selections[i] = new DirectoryObject(name, path, schemaClassName, upn, fetchedAttributes);
616 | }
617 | }
618 | }
619 | finally
620 | {
621 | PInvoke.GlobalUnlock(pDsSL);
622 | PInvoke.ReleaseStgMedium(ref stg);
623 | }
624 | return selections;
625 | }
626 |
627 | #pragma warning disable IDE0051 // Remove unused private members
628 | private void ResetAttributesToFetch() => AttributesToFetch = new Collection();
629 | #pragma warning restore IDE0051 // Remove unused private members
630 |
631 | private void ResetInner() // can be called from constructor without a "Virtual member call in constructor" warning
632 | {
633 | AllowedLocations = Locations.All;
634 | AllowedObjectTypes = ObjectTypes.All;
635 | DefaultLocations = Locations.None;
636 | DefaultObjectTypes = ObjectTypes.All;
637 | Providers = ADsPathsProviders.Default;
638 | MultiSelect = false;
639 | SkipDomainControllerCheck = false;
640 | ResetAttributesToFetch();
641 | selectedObjects = null;
642 | ShowAdvancedView = false;
643 | TargetComputer = null;
644 | }
645 |
646 | private bool ShouldSerializeAttributesToFetch() => AttributesToFetch != null && AttributesToFetch.Count > 0;
647 | }
648 | }
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/EnumExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Tulpep.ActiveDirectoryObjectPicker
4 | {
5 | internal static class EnumExtensions
6 | {
7 | /// Determines whether the enumerated flag value has the specified flag set.
8 | /// The enumerated type.
9 | /// The enumerated flag value.
10 | /// The flag value to check.
11 | /// true if is flag set; otherwise, false.
12 | public static bool IsFlagSet(this T flags, T flag) where T : struct, System.Enum
13 | {
14 | CheckHasFlags();
15 | var flagValue = Convert.ToInt64(flag);
16 | return (Convert.ToInt64(flags) & flagValue) == flagValue;
17 | }
18 |
19 | /// Checks if represents an enumeration and throws an exception if not.
20 | /// The to validate.
21 | ///
22 | private static void CheckHasFlags() where T : struct, System.Enum
23 | {
24 | if (!IsFlags())
25 | throw new ArgumentException($"Type '{typeof(T).FullName}' doesn't have the 'Flags' attribute");
26 | }
27 |
28 | /// Determines whether this enumerations has the set.
29 | /// The enumerated type.
30 | /// true if this instance has the set; otherwise, false.
31 | private static bool IsFlags() where T : struct, System.Enum => Attribute.IsDefined(typeof(T), typeof(FlagsAttribute));
32 | }
33 | }
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/ExtensionAttributeFor.NET_2.0.cs:
--------------------------------------------------------------------------------
1 | #if NET20
2 | namespace System.Runtime.CompilerServices
3 | {
4 | /// Attribute allowing extenders to be used with .NET Framework 2.0.
5 | internal sealed class ExtensionAttribute : Attribute
6 | {
7 | }
8 | }
9 | #endif
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/IntPtrExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Tulpep.ActiveDirectoryObjectPicker
4 | {
5 | internal static class IntPtrExtensions
6 | {
7 | /// Adds an offset to the value of a pointer.
8 | /// The pointer to add the offset to.
9 | /// The offset to add.
10 | /// A new pointer that reflects the addition of to .
11 | public static IntPtr OffsetWith(this IntPtr pointer, long offset) =>
12 | // On 64bits computer, we need ToInt64() to prevent exceptions when process is using more than int.MaxValue of memory.
13 | //
14 | // On 32bits computer, the use of ToInt64() has no effect except when the pointer moved by offset would make it past the
15 | // int.MaxValue barrier. We still need to fail in that case so let's make the IntPtr constructor fail with a meaningful error message.
16 | new IntPtr(pointer.ToInt64() + offset);
17 | }
18 | }
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/Locations.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Tulpep.ActiveDirectoryObjectPicker
4 | {
5 | /// Indicates the scope the DirectoryObjectPickerDialog searches for objects.
6 | [Flags]
7 | public enum Locations
8 | {
9 | /// No locations.
10 | None = 0x0,
11 |
12 | /// The target computer (down-level).
13 | LocalComputer = 0x0001,
14 |
15 | /// A domain to which the target computer is joined (down-level and up-level).
16 | JoinedDomain = 0x0002,
17 |
18 | /// All Windows 2000 domains in the enterprise to which the target computer belongs (up-level).
19 | EnterpriseDomain = 0x0004,
20 |
21 | /// A scope containing objects from all domains in the enterprise (up-level).
22 | GlobalCatalog = 0x0008,
23 |
24 | ///
25 | /// All domains external to the enterprise, but trusted by the domain to which the target computer is joined (down-level and up-level).
26 | ///
27 | ExternalDomain = 0x0010,
28 |
29 | /// The workgroup to which the target computer is joined (down-level).
30 | ///
31 | ///
32 | /// Applies only if the target computer is not joined to a domain. The only type of object that can be selected from a workgroup is
33 | /// a computer.
34 | ///
35 | ///
36 | Workgroup = 0x0020,
37 |
38 | /// Enables the user to enter a scope (down-level and up-level).
39 | ///
40 | /// If not specified, the dialog box restricts the user to the scopes in the locations drop-down list.
41 | ///
42 | UserEntered = 0x0040,
43 |
44 | /// All locations.
45 | All = 0x007F
46 | }
47 | }
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/NameTranslator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Tulpep.ActiveDirectoryObjectPicker
4 | {
5 | /// Active Directory name translation.
6 | ///
7 | ///
8 | /// Translates names between Active Directory formats, e.g. from down-level NT4 style names ("ACME\alice") to User Principal Name ("alice@acme.com").
9 | ///
10 | /// This utility class encapsulates the ActiveDs.dll COM library.
11 | ///
12 | public static class NameTranslator
13 | {
14 | private const int NameTypeNt4 = (int)ADS_NAME_TYPE_ENUM.ADS_NAME_TYPE_NT4;
15 | private const int NameTypeUpn = (int)ADS_NAME_TYPE_ENUM.ADS_NAME_TYPE_USER_PRINCIPAL_NAME;
16 |
17 | /// Convert from a down-level NT4 style name to an Active Directory User Principal Name (UPN).
18 | public static string TranslateDownLevelToUpn(string downLevelNt4Name)
19 | {
20 | if (downLevelNt4Name == null) throw new ArgumentNullException("downLevelNt4Name");
21 | if (downLevelNt4Name.Length == 0) throw new ArgumentOutOfRangeException("downLevelNt4Name", "downLevelNt4Name is empty");
22 |
23 | // ReSharper disable once SuspiciousTypeConversion.Global -- COM object and interfaces
24 | var nameTranslate = (IADsNameTranslate)new NameTranslate();
25 | nameTranslate.Set(NameTypeNt4, downLevelNt4Name);
26 | return nameTranslate.Get(NameTypeUpn);
27 | }
28 |
29 | /// Convert from an Active Directory User Principal Name (UPN) to a down-level NT4 style name.
30 | public static string TranslateUpnToDownLevel(string userPrincipalName)
31 | {
32 | if (userPrincipalName == null) throw new ArgumentNullException("userPrincipalName");
33 | if (userPrincipalName.Length == 0) throw new ArgumentOutOfRangeException("userPrincipalName", "userPrincipalName is empty");
34 |
35 | // ReSharper disable once SuspiciousTypeConversion.Global -- COM object and interfaces
36 | var nameTranslate = (IADsNameTranslate)new NameTranslate();
37 | nameTranslate.Set(NameTypeUpn, userPrincipalName);
38 | return nameTranslate.Get(NameTypeNt4);
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/ObjectTypes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Tulpep.ActiveDirectoryObjectPicker
4 | {
5 | /// Indicates the type of objects the DirectoryObjectPickerDialog searches for.
6 | [Flags]
7 | public enum ObjectTypes
8 | {
9 | /// No object types.
10 | None = 0x0,
11 |
12 | /// Includes user objects.
13 | Users = 0x0001,
14 |
15 | /// Includes security groups with universal scope.
16 | ///
17 | /// In an up-level scope, this includes distribution and security groups, with universal, global and domain local scope.
18 | /// In a down-level scope, this includes local and global groups.
19 | ///
20 | Groups = 0x0002,
21 |
22 | /// Includes computer objects.
23 | Computers = 0x0004,
24 |
25 | /// Includes contact objects.
26 | Contacts = 0x0008,
27 |
28 | /// Includes built-in group objects.
29 | ///
30 | /// In an up-level scope, this includes group objects with the built-in groupType flags.
31 | /// In a down-level scope, not setting this object type excludes local built-in groups.
32 | ///
33 | BuiltInGroups = 0x0010,
34 |
35 | /// Includes all well-known security principals.
36 | ///
37 | /// In an up-level scope, this includes the contents of the Well Known Security Principals container.
38 | /// In a down-level scope, this includes all well-known SIDs.
39 | ///
40 | WellKnownPrincipals = 0x0020,
41 |
42 | /// Includes all service accounts and group managed service accounts.
43 | ServiceAccounts = 0x0040,
44 |
45 | /// All object types.
46 | All = 0x007F
47 | }
48 | }
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/PInvoke.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace Tulpep.ActiveDirectoryObjectPicker
5 | {
6 | internal class PInvoke
7 | {
8 | ///
9 | /// The GlobalLock function locks a global memory object and returns a pointer to the first byte of the object's memory block.
10 | /// GlobalLock function increments the lock count by one. Needed for the clipboard functions when getting the data from IDataObject
11 | ///
12 | ///
13 | ///
14 | [DllImport("Kernel32.dll", SetLastError = true)]
15 | public static extern IntPtr GlobalLock(IntPtr hMem);
16 |
17 | /// The GlobalUnlock function decrements the lock count associated with a memory object.
18 | ///
19 | ///
20 | [DllImport("Kernel32.dll", SetLastError = true)]
21 | public static extern bool GlobalUnlock(IntPtr hMem);
22 |
23 | /// Frees the specified storage medium.
24 | /// Pointer to the storage medium that is to be freed.
25 | [DllImport("ole32.dll")]
26 | internal static extern void ReleaseStgMedium([In] ref STGMEDIUM pmedium);
27 | }
28 | }
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/StructsFlags.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | // ReSharper disable InconsistentNaming -- those names are from Windows/COM
5 | namespace Tulpep.ActiveDirectoryObjectPicker
6 | {
7 | /// Directory name types for use with IADsNameTranslate
8 | internal enum ADS_NAME_TYPE_ENUM
9 | {
10 | ADS_NAME_TYPE_1779 = 1,
11 | ADS_NAME_TYPE_CANONICAL = 2,
12 | ADS_NAME_TYPE_NT4 = 3,
13 | ADS_NAME_TYPE_DISPLAY = 4,
14 | ADS_NAME_TYPE_DOMAIN_SIMPLE = 5,
15 | ADS_NAME_TYPE_ENTERPRISE_SIMPLE = 6,
16 | ADS_NAME_TYPE_GUID = 7,
17 | ADS_NAME_TYPE_UNKNOWN = 8,
18 | ADS_NAME_TYPE_USER_PRINCIPAL_NAME = 9,
19 | ADS_NAME_TYPE_CANONICAL_EX = 10,
20 | ADS_NAME_TYPE_SERVICE_PRINCIPAL_NAME = 11,
21 | ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME = 12,
22 | }
23 |
24 | /// The DVASPECT enumeration values specify the desired data or view aspect of the object when drawing or getting data.
25 | internal enum DVASPECT
26 | {
27 | DVASPECT_CONTENT = 1,
28 | DVASPECT_THUMBNAIL = 2,
29 | DVASPECT_ICON = 4,
30 | DVASPECT_DOCPRINT = 8
31 | }
32 |
33 | /// The TYMED enumeration values indicate the type of storage medium being used in a data transfer.
34 | internal enum TYMED
35 | {
36 | TYMED_HGLOBAL = 1,
37 | TYMED_FILE = 2,
38 | TYMED_ISTREAM = 4,
39 | TYMED_ISTORAGE = 8,
40 | TYMED_GDI = 16,
41 | TYMED_MFPICT = 32,
42 | TYMED_ENHMF = 64,
43 | TYMED_NULL = 0
44 | }
45 |
46 | ///
47 | /// The DS_SELECTION structure contains data about an object the user selected from an object picker dialog box. The DS_SELECTION_LIST
48 | /// structure contains an array of DS_SELECTION structures.
49 | ///
50 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
51 | internal struct DS_SELECTION
52 | {
53 | [MarshalAs(UnmanagedType.LPWStr)]
54 | public string pwzName;
55 |
56 | [MarshalAs(UnmanagedType.LPWStr)]
57 | public string pwzADsPath;
58 |
59 | [MarshalAs(UnmanagedType.LPWStr)]
60 | public string pwzClass;
61 |
62 | [MarshalAs(UnmanagedType.LPWStr)]
63 | public string pwzUPN;
64 |
65 | public IntPtr pvarFetchedAttributes;
66 | public uint flScopeType;
67 | }
68 |
69 | ///
70 | /// The DS_SELECTION_LIST structure contains data about the objects the user selected from an object picker dialog box. This structure
71 | /// is supplied by the IDataObject interface supplied by the IDsObjectPicker::InvokeDialog method in the CFSTR_DSOP_DS_SELECTION_LIST
72 | /// data format.
73 | ///
74 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
75 | internal struct DS_SELECTION_LIST
76 | {
77 | public uint cItems;
78 | public uint cFetchedAttributes;
79 | public DS_SELECTION[] aDsSelection;
80 | }
81 |
82 | ///
83 | /// The DSOP_FILTER_FLAGS structure contains flags that indicate the types of objects presented to the user for a specified scope or scopes.
84 | ///
85 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
86 | internal struct DSOP_FILTER_FLAGS
87 | {
88 | public DSOP_UPLEVEL_FILTER_FLAGS Uplevel;
89 | public uint flDownlevel;
90 | }
91 |
92 | /// The DSOP_INIT_INFO structure contains data required to initialize an object picker dialog box.
93 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
94 | internal struct DSOP_INIT_INFO
95 | {
96 | public uint cbSize;
97 |
98 | [MarshalAs(UnmanagedType.LPWStr)]
99 | public string pwzTargetComputer;
100 |
101 | public uint cDsScopeInfos;
102 | public IntPtr aDsScopeInfos;
103 | public uint flOptions;
104 | public uint cAttributesToFetch;
105 | public IntPtr apwzAttributeNames;
106 | }
107 |
108 | ///
109 | /// The DSOP_SCOPE_INIT_INFO structure describes one or more scope types that have the same attributes. A scope type is a type of
110 | /// location, for example a domain, computer, or Global Catalog, from which the user can select objects.
111 | ///
112 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto), Serializable]
113 | internal struct DSOP_SCOPE_INIT_INFO
114 | {
115 | public uint cbSize;
116 | public uint flType;
117 | public uint flScope;
118 | public DSOP_FILTER_FLAGS FilterFlags;
119 |
120 | [MarshalAs(UnmanagedType.LPWStr)]
121 | public string pwzDcName;
122 |
123 | [MarshalAs(UnmanagedType.LPWStr)]
124 | public string pwzADsPath;
125 |
126 | public uint hr;
127 | }
128 |
129 | ///
130 | /// The DSOP_UPLEVEL_FILTER_FLAGS structure contains flags that indicate the filters to use for an up-level scope. An up-level scope is
131 | /// a scope that supports the ADSI LDAP provider.
132 | ///
133 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
134 | internal struct DSOP_UPLEVEL_FILTER_FLAGS
135 | {
136 | public uint flBothModes;
137 | public uint flMixedModeOnly;
138 | public uint flNativeModeOnly;
139 | }
140 |
141 | /// This structure is used as a parameter in OLE functions and methods that require data format information.
142 | [StructLayout(LayoutKind.Sequential)]
143 | internal struct FORMATETC
144 | {
145 | public int cfFormat;
146 | public IntPtr ptd;
147 | public uint dwAspect;
148 | public int lindex;
149 | public uint tymed;
150 | }
151 |
152 | /// The STGMEDIUM structure is a generalized global memory handle used for data transfer operations by the IDataObject
153 | [StructLayout(LayoutKind.Sequential)]
154 | internal struct STGMEDIUM
155 | {
156 | public uint tymed;
157 | public IntPtr hGlobal;
158 | /* Presumably this is supposed to be an Object but according to a comment by xC0000005 on the
159 | * DSOP_INIT_INFO MSDN page, there is a bug in Windows whereby the returned object doesn't
160 | * support IUnknown, causing a E_NOT_IMPL error from .NET.
161 | *
162 | * Changing it to IntPtr makes it opaque to .NET and prevents the error
163 | */
164 | public IntPtr pUnkForRelease;
165 | }
166 |
167 | /// The CFSTR_DSOP_DS_SELECTION_LIST clipboard format is provided by the IDataObject obtained by calling IDsObjectPicker.InvokeDialog
168 | internal class CLIPBOARD_FORMAT
169 | {
170 | public const string CFSTR_DSOP_DS_SELECTION_LIST =
171 | "CFSTR_DSOP_DS_SELECTION_LIST";
172 | }
173 |
174 | /// Contains the filter flags to use for down-level scopes
175 | internal class DSOP_DOWNLEVEL_FLAGS
176 | {
177 | public const uint DSOP_DOWNLEVEL_FILTER_ALL_WELLKNOWN_SIDS = 0x80020000;
178 | public const uint DSOP_DOWNLEVEL_FILTER_ANONYMOUS = 0x80000040;
179 | public const uint DSOP_DOWNLEVEL_FILTER_AUTHENTICATED_USER = 0x80000020;
180 | public const uint DSOP_DOWNLEVEL_FILTER_BATCH = 0x80000080;
181 | public const uint DSOP_DOWNLEVEL_FILTER_COMPUTERS = 0x80000008;
182 | public const uint DSOP_DOWNLEVEL_FILTER_CREATOR_GROUP = 0x80000200;
183 | public const uint DSOP_DOWNLEVEL_FILTER_CREATOR_OWNER = 0x80000100;
184 | public const uint DSOP_DOWNLEVEL_FILTER_DIALUP = 0x80000400;
185 | public const uint DSOP_DOWNLEVEL_FILTER_EXCLUDE_BUILTIN_GROUPS = 0x80008000;
186 | public const uint DSOP_DOWNLEVEL_FILTER_GLOBAL_GROUPS = 0x80000004;
187 | public const uint DSOP_DOWNLEVEL_FILTER_IIS_APP_POOL = 0x84000000;
188 | public const uint DSOP_DOWNLEVEL_FILTER_INTERACTIVE = 0x80000800;
189 | public const uint DSOP_DOWNLEVEL_FILTER_INTERNET_USER = 0x80200000;
190 | public const uint DSOP_DOWNLEVEL_FILTER_LOCAL_GROUPS = 0x80000002;
191 | public const uint DSOP_DOWNLEVEL_FILTER_LOCAL_LOGON = 0x81000000;
192 | public const uint DSOP_DOWNLEVEL_FILTER_LOCAL_SERVICE = 0x80040000;
193 | public const uint DSOP_DOWNLEVEL_FILTER_NETWORK = 0x80001000;
194 | public const uint DSOP_DOWNLEVEL_FILTER_NETWORK_SERVICE = 0x80080000;
195 | public const uint DSOP_DOWNLEVEL_FILTER_OWNER_RIGHTS = 0x80400000;
196 | public const uint DSOP_DOWNLEVEL_FILTER_REMOTE_LOGON = 0x80100000;
197 | public const uint DSOP_DOWNLEVEL_FILTER_SERVICE = 0x80002000;
198 | public const uint DSOP_DOWNLEVEL_FILTER_SERVICES = 0x80800000;
199 | public const uint DSOP_DOWNLEVEL_FILTER_SYSTEM = 0x80004000;
200 | public const uint DSOP_DOWNLEVEL_FILTER_TERMINAL_SERVER = 0x80010000;
201 | public const uint DSOP_DOWNLEVEL_FILTER_THIS_ORG_CERT = 0x82000000;
202 | public const uint DSOP_DOWNLEVEL_FILTER_USERS = 0x80000001;
203 | public const uint DSOP_DOWNLEVEL_FILTER_WORLD = 0x80000010;
204 | }
205 |
206 | /// Filter flags to use for an up-level scope, regardless of whether it is a mixed or native mode domain.
207 | internal class DSOP_FILTER_FLAGS_FLAGS
208 | {
209 | public const uint DSOP_FILTER_BUILTIN_GROUPS = 0x00000004;
210 | public const uint DSOP_FILTER_COMPUTERS = 0x00000800;
211 | public const uint DSOP_FILTER_CONTACTS = 0x00000400;
212 | public const uint DSOP_FILTER_DOMAIN_LOCAL_GROUPS_DL = 0x00000100;
213 | public const uint DSOP_FILTER_DOMAIN_LOCAL_GROUPS_SE = 0x00000200;
214 | public const uint DSOP_FILTER_GLOBAL_GROUPS_DL = 0x00000040;
215 | public const uint DSOP_FILTER_GLOBAL_GROUPS_SE = 0x00000080;
216 | public const uint DSOP_FILTER_INCLUDE_ADVANCED_VIEW = 0x00000001;
217 | public const uint DSOP_FILTER_SERVICE_ACCOUNTS = 0x00001000;
218 | public const uint DSOP_FILTER_UNIVERSAL_GROUPS_DL = 0x00000010;
219 | public const uint DSOP_FILTER_UNIVERSAL_GROUPS_SE = 0x00000020;
220 | public const uint DSOP_FILTER_USERS = 0x00000002;
221 | public const uint DSOP_FILTER_WELL_KNOWN_PRINCIPALS = 0x00000008;
222 | }
223 |
224 | /// Flags that determine the object picker options.
225 | internal class DSOP_INIT_INFO_FLAGS
226 | {
227 | public const uint DSOP_FLAG_MULTISELECT = 0x00000001;
228 | public const uint DSOP_FLAG_SKIP_TARGET_COMPUTER_DC_CHECK = 0x00000002;
229 | }
230 |
231 | ///
232 | /// Flags that indicate the format used to return ADsPath for objects selected from this scope. The flScope member can also indicate the
233 | /// initial scope displayed in the Look in drop-down list.
234 | ///
235 | internal class DSOP_SCOPE_INIT_INFO_FLAGS
236 | {
237 | public const uint DSOP_SCOPE_FLAG_DEFAULT_FILTER_COMPUTERS = 0x00000100;
238 | public const uint DSOP_SCOPE_FLAG_DEFAULT_FILTER_CONTACTS = 0x00000200;
239 | public const uint DSOP_SCOPE_FLAG_DEFAULT_FILTER_GROUPS = 0x00000080;
240 | public const uint DSOP_SCOPE_FLAG_DEFAULT_FILTER_PASSWORDSETTINGS_OBJECTS = 0x00000800;
241 | public const uint DSOP_SCOPE_FLAG_DEFAULT_FILTER_SERVICE_ACCOUNTS = 0x00000400;
242 | public const uint DSOP_SCOPE_FLAG_DEFAULT_FILTER_USERS = 0x00000040;
243 | public const uint DSOP_SCOPE_FLAG_STARTING_SCOPE = 0x00000001;
244 | public const uint DSOP_SCOPE_FLAG_WANT_DOWNLEVEL_BUILTIN_PATH = 0x00000020;
245 | public const uint DSOP_SCOPE_FLAG_WANT_PROVIDER_GC = 0x00000008;
246 | public const uint DSOP_SCOPE_FLAG_WANT_PROVIDER_LDAP = 0x00000004;
247 | public const uint DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT = 0x00000002;
248 | public const uint DSOP_SCOPE_FLAG_WANT_SID_PATH = 0x00000010;
249 | }
250 |
251 | ///
252 | /// Flags that indicate the scope types described by this structure. You can combine multiple scope types if all specified scopes use
253 | /// the same settings.
254 | ///
255 | internal class DSOP_SCOPE_TYPE_FLAGS
256 | {
257 | public const uint DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN = 0x00000004;
258 | public const uint DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN = 0x00000008;
259 | public const uint DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN = 0x00000040;
260 | public const uint DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN = 0x00000020;
261 | public const uint DSOP_SCOPE_TYPE_GLOBAL_CATALOG = 0x00000010;
262 | public const uint DSOP_SCOPE_TYPE_TARGET_COMPUTER = 0x00000001;
263 | public const uint DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN = 0x00000002;
264 | public const uint DSOP_SCOPE_TYPE_USER_ENTERED_DOWNLEVEL_SCOPE = 0x00000200;
265 | public const uint DSOP_SCOPE_TYPE_USER_ENTERED_UPLEVEL_SCOPE = 0x00000100;
266 | public const uint DSOP_SCOPE_TYPE_WORKGROUP = 0x00000080;
267 | }
268 |
269 | /// The IDsObjectPicker.InvokeDialog result
270 | internal class HRESULT
271 | {
272 | public const int E_NOTIMPL = unchecked((int)0x80004001);
273 | public const int S_FALSE = 1; // The user cancelled the dialog box. ppdoSelections receives NULL. ?
274 | public const int S_OK = 0; // The method succeeded.
275 | }
276 | }
277 |
278 | // ReSharper restore InconsistentNaming
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/Tulpep.ActiveDirectoryObjectPicker.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | ActiveDirectoryObjectPicker
4 | Tulpep.ActiveDirectoryObjectPicker.snk
5 | true
6 | true
7 | true
8 | Tulpep.ActiveDirectoryObjectPicker
9 | Active Directory Object Picker
10 | Armand du Plessis;Tulpep;dahall
11 | Active Directory Object Picker
12 | https://github.com/Tulpep/Active-Directory-Dialogs
13 |
14 | true
15 | Tulpep
16 | embedded
17 | WPF;Active Directory;Dialog;WinForms;Windows Forms;Picker
18 | $(AssemblyName)
19 | net20;net35;net40;net45;net452;net462;net472;net48;netcoreapp3.0;netcoreapp3.1;net5.0-windows;net6.0-windows
20 | false
21 | Tulpep.ActiveDirectoryObjectPicker
22 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml
23 | 3.0.6
24 | true
25 | true
26 | snupkg
27 | MS-PL
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/Tulpep.ActiveDirectoryObjectPicker.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tulpep/Active-Directory-Object-Picker/7888679b16e929b750b6b56b3f931162fa674dc9/Tulpep.ActiveDirectoryObjectPicker/Tulpep.ActiveDirectoryObjectPicker.snk
--------------------------------------------------------------------------------
/Tulpep.ActiveDirectoryObjectPicker/UnmanagedArrayOfStrings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace Tulpep.ActiveDirectoryObjectPicker
6 | {
7 | // based on the packLPArray class. original from mailing list post by Beat Bucheli. or maybe from
8 | // http://blogs.technolog.nl/eprogrammer/archive/2005/11/24/402.aspx or maybe from somewhere else..
9 | /// A packed array of strings.
10 | ///
11 | public sealed class UnmanagedArrayOfStrings : IDisposable
12 | {
13 | private readonly IntPtr[] _unmanagedStrings;
14 |
15 | /// Initializes a new instance of the class.
16 | /// The strings to pack.
17 | public UnmanagedArrayOfStrings(IList strings)
18 | {
19 | if (strings != null)
20 | {
21 | var length = strings.Count;
22 | _unmanagedStrings = new IntPtr[length];
23 | var neededSize = length * IntPtr.Size;
24 | ArrayPtr = Marshal.AllocCoTaskMem(neededSize);
25 | for (var cx = length - 1; cx >= 0; cx--)
26 | {
27 | _unmanagedStrings[cx] = Marshal.StringToCoTaskMemUni(strings[cx]);
28 | Marshal.WriteIntPtr(ArrayPtr, cx * IntPtr.Size, _unmanagedStrings[cx]);
29 | }
30 | }
31 | }
32 |
33 | /// Gets the pointer to the packed array memory.
34 | public IntPtr ArrayPtr { get; private set; }
35 |
36 | /// Releases unmanaged and - optionally - managed resources.
37 | public void Dispose()
38 | {
39 | if (ArrayPtr != IntPtr.Zero)
40 | {
41 | Marshal.FreeCoTaskMem(ArrayPtr);
42 | ArrayPtr = IntPtr.Zero;
43 | }
44 |
45 | foreach (var ptr in _unmanagedStrings)
46 | {
47 | Marshal.FreeCoTaskMem(ptr);
48 | }
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # http://www.appveyor.com/docs/appveyor-yml
2 |
3 | image: Visual Studio 2022
4 |
5 | # version format
6 | version: 3.0.{build}
7 |
8 | # Fix line endings in Windows. (runs before repo cloning)
9 | init:
10 | - git config --global core.autocrlf input
11 |
12 |
13 | # Assembly Info
14 | dotnet_csproj:
15 | patch: true
16 | file: '**\*.csproj'
17 | version: '{version}'
18 | version_prefix: '{version}'
19 |
20 | #Build Relase instead of Debug
21 | configuration: Release
22 |
23 | # Make sure all NuGet packages are installed before building.
24 | before_build:
25 | - nuget restore
26 |
27 | artifacts:
28 | - path: '**\Tulpep.ActiveDirectoryObjectPicker*.nupkg'
29 | name: Tulpep.ActiveDirectoryObjectPicker.nupkg
30 |
31 | #Publish NuGet
32 | deploy:
33 | provider: NuGet
34 | api_key:
35 | secure: 5ldn0g+rIeS2ZHbyFJjilj/nCUyUcTTMN1As3uJ3kCtW2QStDsdk4dCiL4G2KvOL
36 | artifact: /.*\.nupkg/
37 | on:
38 | branch: master
39 |
--------------------------------------------------------------------------------