Рис.5. Вид формы ожидания завершения длительной операции.
С учетом сказанного метод QueryNamespaces будет иметь такой вид.
void QueryNamespaces()
{
FormSplash.ShowForm("Searching for WMI Namespaces... Please, wait");
RecurseNamespaces("root");
treeNS.Nodes[0].Expand();
xDoc = new XDocument(new XElement("root"));
AddXNodeRecursive(xDoc.Root, treeNS.Nodes[0]);
SaveNamespacesTree();
FormSplash.CloseForm();
}
Рекурсивная процедура AddXNodeRecursive создаст дерево XML-документа (все XML-документы по определению имеют древовидную структуру), которое должно быть изоморфно дереву treeNS. Метод SaveNamespacesTree сохранит XML-документ. При следующем запуске приложения, мы не будем тратить время на опрос базы WMI, а обратимся к методу ReadNamespaces, который быстро прочтет XML-документ и создаст (точнее, восстановит) дерево на экране treeNS. Временно закомментируйте все обращения к FormSplash (мы разработаем этот класс позже) и продолжим развитие WMIControl.
Логика метода RecurseNamespaces должна быть вам понятна, так как вы читаете документ с самого начала и уже усвоили приемы работы с объектами классов ManagementClass и ManagementObject. Ключом к пониманию алгоритма метода являются строки текста "root" и "__namespace". Первую строку мы подали на вход при первом вызове рекурсивного метода. Она определяет тот факт, что мы ищем корень дерева WMI. Вторая строка сообщает WMI API, что нас интересует структура пространств имен.
void RecurseNamespaces(string path)
{
try
{
ManagementClass c = new ManagementClass(
new ManagementScope(path),
new ManagementPath("__namespace"), null);
foreach (ManagementObject mo in c.GetInstances())
{
string name = path + "\\" + mo["Name"].ToString();
nsCount++;
AddNamespace(name);
FormSplash.AddItem(name); // Временно закомментируйте этот оператор
RecurseNamespaces(name);
}
}
catch (ManagementException e)
{
SetError(e.Message, path);
}
}
Заметьте, что повторные (рекурсивные) вызовы метода будут подавать на вход все более точные имена пространств имен. Это позволит просканировать все дерево вглубь, каким бы сложным оно не было. Уточнение пути выполняет строка кода:
string name = path + "\\" + mo["Name"].ToString();
Теперь рассмотрим код метода AddNamespace.
void AddNamespace(string name)
{
string[] tokens = name.Split('\\');
TreeNodeCollection nodes = treeNS.Nodes;
foreach (string s in tokens)
{
if (nodes[s] == null)
nodes.Add(s, s, 1, 2);
nodes = nodes[s].Nodes;
}
}
Этот алгоритм предложил студент 3-го курса Игорь Федчун. Следует отметить, что решение работать с коллекцией узлов TreeNodeCollection и выбор узла из этой коллекции с помощью индексатора (см. nodes[s]), позволяет значительно сократить код и повысить читабельность метода. Но такое решение налагает требование: создавать новые узлы дерева TreeView с помощью самой сложной версии метода Add класса TreeNodeCollection.
public virtual TreeNode Add(string key, string text, int imageIndex, int selectedImageIndex);
Секрет успеха в использовании параметра key. Любопытно, но класс TreeNode имеет 5 конструкторов и ни один из них не позволяет задать ключ для узла. Именно этот факт определяет преимущество работы с TreeNodeCollection, а не с TreeNode.
Метод SetError позволяет аккуратно обработать исключения и удалить неудачно вставленный в дерево узел.
void SetError(string msg, string path)
{
FormSplash.AddMsg(msg + ": " + path);
DeleteNode(path);
}
Избежать процедуры удаления узла не удается, так как исключение возникает уже после вставки узла в дерево.
void DeleteNode(string path)
{
string[] tokens = path.Split('\\');
string name = tokens[tokens.Length - 1];
TreeNodeCollection nodes = treeNS.Nodes;
foreach (string s in tokens)
{
if (s == name)
break;
nodes = nodes[s].Nodes;
}
nodes.Remove(nodes[name]);
nsHidden++; // Счетчик скрытых пространств имен (доступ к ним запрещен)
}
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.