Во-первых, будьте осторожны с этим:
lvwListView.Items.Add(entry.GetDirectoryEntry().Properties["sAMAccountName"].Value.ToString());
В частности, используйте GetDirectoryEntry()
только для получения значения.Когда вы получаете значение, используя DirectoryEntry.Properties
, оно проверяет, есть ли уже значение, которое вы запрашиваете в кеше.Если нет, он запрашивает у AD каждый атрибут со значением , что обычно будет намного больше данных, чем вам нужно на самом деле.Поскольку вы перебираете несколько учетных записей, это может значительно замедлить работу.
Вы уже запрашиваете sAMAccountName
в результатах поиска, так как вы использовали searcher.PropertiesToLoad.Add("sAMAccountName")
, так что вы можете вытянутьзначение непосредственно из объекта SearchResult
:
lvwListView.Items.Add((string) entry.Properties["sAMAccountName"][0]);
Я подробнее говорю об этом в статье, которую я написал о лучшей производительности при программировании с Active Directory .
Теперь, чтобы действительно ответить на ваш вопрос:
Получение членов - это отдельная история.Вы можете запросить атрибут member
в поиске, но у вас возникают проблемы, когда в группе более 1500 участников.Вы должны попросить членов на страницах.Но также вы должны решить, как вы будете обрабатывать вложенные группы: когда группа является членом группы.Вы хотите просто перечислить это имя группы?Или вы хотите заглянуть в эту группу и получить этих членов?
Я тоже написал об этом целую статью: Найти всех членов группы
Ноболее чем вероятно, что вы можете просто использовать один из примеров методов, которые я покажу в этой статье.Это возьмет DirectoryEntry
объект группы и выдаст вам список строк, содержащих DOMAIN\username
каждого объекта внутри группы (вы можете изменить его, если хотите).Вы можете использовать параметр recursive
, чтобы решить, что вы хотите делать с вложенными группами.
public static IEnumerable<string> GetGroupMemberList(DirectoryEntry group, bool recursive = false) {
var members = new List<string>();
group.RefreshCache(new[] { "member" });
while (true) {
var memberDns = group.Properties["member"];
foreach (string member in memberDns) {
using (var memberDe = new DirectoryEntry($"LDAP://{member.Replace("/", "\\/")}")) {
memberDe.RefreshCache(new[] { "objectClass", "msDS-PrincipalName", "cn" });
if (recursive && memberDe.Properties["objectClass"].Contains("group")) {
members.AddRange(GetGroupMemberList(memberDe, true));
} else {
var username = memberDe.Properties["msDS-PrincipalName"].Value.ToString();
if (!string.IsNullOrEmpty(username)) {
members.Add(username);
}
}
}
}
if (memberDns.Count == 0) break;
try {
group.RefreshCache(new[] {$"member;range={members.Count}-*"});
} catch (COMException e) {
if (e.ErrorCode == unchecked((int) 0x80072020)) { //no more results
break;
}
throw;
}
}
return members;
}
Вы бы назвали его из своего кода следующим образом:
var members = GetGroupMemberList(entry.GetDirectoryEntry());
Тогда вы можетеотображайте их по своему усмотрению.
Это будет хорошо работать, если вы работаете только в одном лесу AD.Но если ваш домен доверяет другим доменам за пределами вашего леса, и вы можете ожидать, что пользователи из этих доменов будут видеть группы, которые вы просматриваете, то вам необходимо учитывать это.Но я расскажу об этом в этой статье.Я также расскажу об основных группах, о которых вам редко нужно заботиться, но вы можете.
Обновление: Чтобы добавить два столбца в ListViewItem
, необходимо создатьэто отдельно.Вы можете использовать конструктор , который принимает строковый массив «подэлементов», как они его называют.
Вы можете поместить что-то подобное в ваш цикл, где вы просматриваете элементы, гдеgroupName
- это имя группы, а member
- это имя члена группы.
lvwListView.Items.Add(new ListViewItem(new [] {
groupName,
member
});
Если вы хотите, чтобы groupName было только первым, тогда вы можете просто вставить пустую строку (""
) вместо groupName
для остальных.