Запись связанных поставщиков WMI с помощью расширения поставщика WMI.NET 2.0

Запись связанных поставщиков WMI с помощью расширения поставщика WMI.NET 2.0

Габриэль Гизила

Microsoft Corporation

Январь 2008 г.

 

Сводка: Сведения о создании связанного поставщика WMI с помощью расширения поставщика WMI.NET 2.0, поставляемого в платформа .NET Framework 3.5

Содержимое

Введение

Простой класс .NET

Атрибут уровня сборки

Атрибуты WMI.NET уровня класса

Требования к среде выполнения

Регистрация с помощью WMI

Расширение классов с помощью наследования

Реализация методов

Исключения и отчеты об ошибках

Другие советы

Заключение

Листинг 1. SCMInterop.cs

Листинг 2. WIN32ServiceHost.cs

 

Введение

Инструментарий управления Windows (WMI) — это широко используемая инфраструктура для управления приложениями Windows и Windows. Несмотря на то, что они очень расширяемы и популярны среди системных администраторов и управляющих приложений, многие разработчики думают о создании поставщиков WMI из-за сложности собственных интерфейсов, которые необходимо реализовать.

Хотя начальные версии платформа .NET Framework были оснащены набором объектов и шаблонов для реализации поставщиков WMI, они ограничивались управлением приложениями, они не позволяли определять методы, а ключи для экземпляров были созданы автоматически. Расширение поставщика WMI.NET версии 2 (WMI.NET) — это новая инфраструктура в Orcas (платформа .NET Framework 3.5), которая позволяет реализовать полный набор функциональных возможностей поставщика WMI. Эта новая инфраструктура сосуществует с моделью поставщика WMI.NET из предыдущих версий, но она гораздо более мощная и расширяемая.

В этой статье основное внимание уделяется написанию связанных поставщиков WMI, что является одной из наиболее важных возможностей в WMI.NET. Нет существенных различий в способе написания несвязанных поставщиков, поэтому статья может дать хорошее начало для читателя, который пытается написать поставщиков WMI любого вида с помощью WMI.NET. В этой статье показано, как создать связанного поставщика WMI, начиная с простого класса .NET, а затем дополнить его несколькими дополнительными функциями. Цель состоит в том, чтобы иметь возможность перечислять процессы, в которых размещаются службы Windows, и иметь возможность перечислять службы Windows в каждом таком процессе и интегрировать эти функции в инструментарий WMI.

Простой класс .NET

Для начала мы создадим класс C#, который будет моделировать процесс, в котором размещаются службы Windows. Каждый экземпляр будет отображать список размещенных служб в связанном процессе. Класс имеет статический метод, возвращающий все экземпляры, связанные с узлами служб, работающими в системе.

    открытый класс WIN32ServiceHost

    {

Класс является оболочкой для класса Process. В поле innerProcess будет храниться ссылка на объект Process.

        Process innerProcess;

Класс имеет один конструктор, который принимает объект процесса в качестве параметра. 

        public WIN32ServiceHost(Process innerProcess)

        {

            this.innerProcess = innerProcess;

        }

Мы включаем метод доступа для идентификатора процесса.

        идентификатор public int

        {

            get { return this.innerProcess.Id; }

        }

Свойство Services возвращает массив с именами размещенных служб. Это свойство имеет значение NULL, если в процессе не выполняются службы.

        public string[] Services

        {

            get

            {

В бездействуемом процессе не размещаются службы.

                if (innerProcess.Id == 0)

                    возвращает значение NULL;

Получение списка всех служб Windows в системе

                ServiceController[] services = ServiceController.GetServices();

                List<ServiceController> servicesForProcess = new List<ServiceController>();

                using (SCM scm = new SCM())

                {

                    для (int svcIndex = 0; службы svcIndex < . Длина; svcIndex++)

                    {

                        ServiceController crtService = services[svcIndex];

                        int processId = scm. GetProcessId(crtService.ServiceName);

Сравните идентификатор процесса, в котором выполняется служба, с идентификатором текущего процесса.

                        if (processId == innerProcess.Id)

                        {

                            servicesForProcess.Add(services[svcIndex]);

                        }

                    }

                }

 

                if (servicesForProcess.Count == 0)

                    возвращает значение NULL;

Подготовка, заполнение и возврат массива с именами службы

                string[] servicesNames = новая строка[servicesForProcess.Count];

 

                для (int serviceIdx = 0; serviceIdx < servicesForProcess.Count; serviceIdx++)

                {

                    servicesNames[serviceIdx] = servicesForProcess[serviceIdx]. ServiceName;

                }

                return servicesNames;

            }

        }

EnumerateServiceHosts — это статический метод, который возвращает IEnumerable для прохождения всех запущенных узлов служб.

        static public IEnumerable EnumerateServiceHosts()

        {

            Process[] processes = Process.GetProcesses();

            foreach (process crtProcess in processes)

            {

                WIN32ServiceHost crtServiceHost = new WIN32ServiceHost(crtProcess);

                if (crtServiceHost.Services != null)

                {

                    yield return crtServiceHost;

                }

            }

        }

    }

Разработчик может использовать этот класс из любого приложения .NET. Однако в этом случае другие приложения управления не смогут использовать его. WMI.NET имеет перехватчики для предоставления класса в этом примере миру WMI, и мы объясним этот процесс в следующих абзацах. Инструментарий WMI предоставляет модель, позволяющую использовать такие объекты и интегрировать их в корпоративные приложения управления, такие как System Management Server или Operations Manager, обеспечить удаленное взаимодействие и позволяет просматривать и использовать этот класс на нескольких платформах.

Атрибут уровня сборки

Первый этап предоставления сборки для инструментирования с помощью WMI.NET — установка атрибута WmiConfiguration на уровне сборки. Этот атрибут помечает сборку как сборку, реализующую поставщик WMI.NET, и позволяет настраивать различные параметры классов, реализованных поставщиком, включая пространство имен, в котором они будут предоставляться. В нашем примере мы определим пространство имен WMI как root\Test и присвоим модели размещения связанную модель поставщика в контексте безопасности NetworkService. Обратите внимание, что все операции будут выполняться в контексте безопасности вызывающего пользователя с помощью олицетворения.

[assembly: WmiConfiguration(@"root\Test", HostingModel = ManagementHostingModel.NetworkService)]

Атрибуты WMI.NET уровня класса

Чтобы инструментировать класс с помощью WMI.NET, класс, его методы, поля и свойства, предоставляемые WMI.NET, должны быть открытыми и должным образом помечены атрибутами WMI.NET. Атрибуты используются для создания метаданных, необходимых инструментарию WMI для использования существующего класса C# в качестве класса WMI.

Инструментирование класса .NET

Атрибут ManagementEntity помечает класс .NET как инструментированные. Во время развертывания инфраструктура WMI.NET создаст соответствующий класс WMI с тем же именем в репозитории WMI. Чтобы изменить это имя, необходимо указать именованный параметр Name в списке аргументов для атрибута ManagementEntity . Name используется в качестве именованного параметра для изменения имени инструментирования для большинства атрибутов. В нашем примере мы решили назвать класс WMI WIN32_ServiceHost без переименования класса .NET.

    [ManagementEntity(Name = "WIN32_ServiceHost")]

    открытый класс WIN32ServiceHost

Обратите внимание, что именование любой сущности может быть немного сложной задачей.  В C# учитывается регистр, а WMI — нет. Таким образом, все имена будут рассматриваться как без учета регистра с точки зрения инфраструктуры WMI.NET. Если имена двух полей или двух методов, являющихся членами одного класса, различаются только в разных случаях, сборка не сможет зарегистрироваться с помощью WMI.

Управление схемой класса WMI

Полезные данные экземпляра предоставляются его свойствами. Необходимо пометить все свойства, которые будут отражены в мире WMI, атрибутами ManagementProbe, ManagementConfiguration или ManagementKey . ManagementProbe — это атрибут, который помечает свойства, предоставляемые как доступные только для чтения в инструментарии WMI. В нашем примере свойство Services является свойством полезных данных, которое мы хотим предоставить миру управления. В нем отображается состояние процесса, которое нельзя изменить напрямую, поэтому мы помечаем его с помощью ManagementProbe. Для свойств чтения и записи необходимо использовать атрибут ManagementConfiguration . В этом случае само свойство должно иметь как метод задания, так и метод получения.

        [ManagementProbe]

        public string[] Services

Идентификатор также является частью полезных данных WIN32ServiceHost, так как он идентифицирует сам процесс. Свойства, которые однозначно идентифицируют экземпляр своего класса, являются ключами для этого класса в мире WMI. Все классы в WMI.NET должны определять ключи и реализовывать их для уникальной идентификации экземпляров, если они не являются абстрактными, одноэлементными или наследуются от класса, который уже определяет ключи. Ключи помечаются атрибутом ManagementKey . В этом примере идентификатор процесса однозначно идентифицирует процесс; таким образом, он однозначно идентифицирует экземпляры нашего класса.

        [ManagementKey]

        идентификатор public int

Требования к среде выполнения

Маркировка класса и его коллекции свойств позволит предоставить схему класса для предоставления WMI, но этого будет недостаточно, чтобы классы предоставляли фактические данные. Необходимо определить точки входа для получения, создания или удаления экземпляра, а также перечисления экземпляров. Мы предоставим код для перечисления экземпляров, а также получение конкретного экземпляра, который практически является минимальным для функционального поставщика WMI. 

Перечисление экземпляров

Для перечисления экземпляров WMI.NET ожидает статический открытый метод без параметров, возвращающих интерфейс IEnumerable . Он должен быть статическим, так как реализует функциональные возможности, относящиеся ко всему классу. Этот метод должен быть помечен атрибутом ManagementEnumerator . EnumerateServiceHosts, уже определенный в нашем примере, соответствует всем требованиям, поэтому его можно просто присвоить и использовать для этой цели.

        [ManagementEnumerator]

        static public IEnumerable EnumerateServiceHosts()

Для этого метода очень важно убедиться, что каждый элемент, возвращаемый в перечислении, является экземпляром инструментированного класса. В противном случае произойдет ошибка среды выполнения.

Привязка к экземпляру

Чтобы получить определенный экземпляр (который называется привязкой в WMI.NET), нам нужен метод, который возвращает экземпляр на основе значений ключей, идентифицирующих его. Нам нужен метод с тем же количеством параметров, что и ключи, при этом параметры имеют те же имена и типы, что и ключи. Типом возвращаемого значения должен быть сам инструментированные классы. Мы будем использовать статический метод. Чтобы связать ключи класса с параметрами, необходимо указать те же инструментированные имена для параметров, что и для ключей.  В нашем примере id является ключом для класса WIN32_ServiceHost, и нам нужно либо присвоить параметру имя для идентификатора метода привязки, либо использовать атрибут ManagementName для предоставления параметра WMI под именем ID. Инфраструктура WMI.NET распознает метод или конструктор привязки, если он помечен атрибутом ManagementBind .

        [ManagementBind]

        static public WIN32ServiceHost GetInstance([ManagementName("ID")] int processId)

        {

            попробуйте выполнить следующее

            {

                Process process = Process.GetProcessById(processId);

                WIN32ServiceHost crtServiceHost = new WIN32ServiceHost(process);

                if (crtServiceHost.Services != null)

                {

                    return crtServiceHost;

                }

                else

                {

                    возвращает значение NULL;

                }

            }

            возникает методом GetProcessById, если процесс с заданным идентификатором не найден

            catch (Исключение ArgumentException)

            {

                возвращает значение NULL;

            }

        }

Важно отметить, что если экземпляр не найден, метод вернет значение NULL. В нашем случае мы сделаем это, если мы не найдем запрошенный процесс вообще или если обнаруженный процесс не содержит никаких служб Windows.

Регистрация с помощью WMI

Класс готов к работе, но нам по-прежнему нужно зарегистрировать его с помощью инструментария WMI и разместить в доступном расположении на диске для загрузки.

Глобальный кэш сборок (GAC) — это место, где мы хотим разместить сборку. Таким образом , .NET может получить его по полному имени .NET. Обратите внимание, что сборка должна иметь строгое имя .NET для регистрации в глобальном кэше сборок. В нашем примере мы будем использовать средство gacutil.exe .NET для хранения сборки в глобальном кэше сборок.

Чтобы получить сведения из инструментируемой сборки в метаданные WMI, необходимо использовать средство .NET InstallUtil.exe для вызова класса WMI.NET с именем DefaultManagementInstaller. DefaultManagementInstaller знает, как проанализировать всю сборку для инструментированных классов и создать соответствующие метаданные WMI. Так как InstallUtil.exe требуется класс, помеченный атрибутом RunInstaller , мы определим пустой класс, производный от DefaultManagementInstaller , который будет вызывать InstallUtil.exe.

    [System.ComponentModel.RunInstaller(true)]

    открытый класс MyInstall: DefaultManagementInstaller

    {

    }

Регистрацию с помощью WMI можно выполнить как в автономном режиме, так и в сети. При онлайн-регистрации метаданные инструментирования сборки будут храниться непосредственно в репозитории WMI. Чтобы напрямую установить метаданные в репозиторий WMI, будет вызвана команда InstallUtil.exe с именем сборки в качестве параметра. Перед выполнением InstallUtil.exe сборка должна находиться в глобальном кэше сборок. Для сборки, созданной в этом примере с именемWMIServiceHost.dll , мы будем использовать следующие команды:

C:> WMIServiceHost.dllgacutil.exe /i

C:>Installutil.exe WMIServiceHost.dll

Автономная регистрация требует двух шагов. Первым шагом является создание метаданных WMI, связанных со сборкой, и сохранение их в MOF-файле. Второй шаг — фактическая регистрация на целевых компьютерах с помощью средстваmofcomp.exe или в составе пакета установки. Преимущество автономной регистрации заключается в том, что MOF-файл можно локализовать и изменить при необходимости. В этом примере можно создать и сохранить метаданные WMI в файле с именем WMIServiceHost.mof с помощью параметра MOF следующим образом:

C:>Installutil.exe /MOF=WMIServiceHost.mof WMIServiceHost.dll

Как и в случае с сетевым режимом, сборка должна находиться в глобальном кэше сборок на целевом компьютере. Чтобы проверить развертывание, можно использовать системное средствоwmic.exe для просмотра экземпляров и значений этого класса.

C:>wmic /NAMESPACE:\\root\test PATH win32_servicehost получить /value

Во время разработки полезно развернуть символы в той же папке в глобальном кэше сборок, где хранится сборка. Таким образом, любой стек, сообщающийся в результате ошибки или сбоя, будет включать полный путь к источнику и номера строк, которые помогут определить фрагмент кода, который является источником ошибки.

Расширение классов с помощью наследования

WIN32_ServiceHost относится к узлам служб, а информация, которую она предоставляет, ограничивается процессами, в которые размещаются службы Windows. Будет интересно расширить эти сведения, включив в них сведения о конкретном процессе, такие как использование памяти, путь к исполняемому файлу, идентификатор сеанса и т. д. Чтобы получить эти сведения, можно расширить схему и написать дополнительный код, чтобы получить столько информации, сколько нам нужно. Хорошая альтернатива написанию дополнительного кода — воспользоваться уже существующим классом WIN32_Process, который находится в операционной системе в пространстве имен root\cimv2 и предоставляет все эти дополнительные сведения для любого процесса, выполняющегося в системе. Этот класс предоставляет обширные сведения о выполняющихся процессах, и мы можем использовать наследование WMI, чтобы расширить его с помощью собственного класса.

Наследование WMI преобразуется в модели программирования WMI.NET в наследование классов. Так как класс, от которого мы хотим наследовать, является классом из пространства WMI, который мы фактически не реализуем в коде, мы должны пометить его определенными способами.

Прежде чем приступить к написанию наследования, необходимо отметить две важные вещи о классе WIN32_Process . Первый WIN32_Process находится в пространстве имен root\cimv2 , и для получения наследования требуется зарегистрировать класс win32_servicehost в том же пространстве имен. Поэтому мы немного изменим оператор атрибута WmiConfiguration .

[assembly: WmiConfiguration(@"root\cimv2", HostingModel = ManagementHostingModel.NetworkService)]

Кроме того, наш суперкласс, win32_process, имеет свойство Handle , определенное как ключ. Этот ключ имеет тип CIM_STRING который преобразуется в . Net System.String. Нам придется прекратить использование id в качестве свойства ключа и использовать свойство Handle .

Чтобы определить внешний класс в WMI.NET, чтобы он соответствовал win32_process, мы просто зеркало его схему, но включим только нужные или необходимые свойства. Ключи в иерархии классов всегда являются обязательными. В настоящее время Handle является единственным интересным свойством, так как это ключ, и он нам понадобится для привязки к конкретному экземпляру.

    [ManagementEntity(External = true)]

    абстрактный открытый класс Win32_Process

    {

        защищенный дескриптор строки;

 

        [ManagementKey]

        public string Handle

        {

            get {

                возвращает значение this.handle;

            }

        }

    }

Если для атрибута ManagementEntity задано значение true, инфраструктура не сможет создавать метаданные WMI во время развертывания, но при этом сохраняется объявленная информация для использования во время выполнения, когда потребуется найти ключи и свойства для производных классов. Обратите внимание, что очень важно управлять содержимым ключей базового класса, так как ключи используются подсистемой WMI для объединения сведений от различных поставщиков.

Чтобы получить класс WMI WIN32_ServiceHost наследовать класс WMI Win32Process, мы наследуем WIN32ServiceHost от только что созданного абстрактного класса в мире .NET.

    [ManagementEntity(Name = "WIN32_ServiceHost")]

    public class WIN32ServiceHost: Win32_Process

   

Мы удаляем свойство ID.

        [ManagementKey]

        идентификатор public int

        {

            get { return this.innerProcess.Id; }

        }

изменение конструктора для заполнения нового ключа в поле дескриптора базового класса

        public WIN32ServiceHost(Process innerProcess)

        {

            this.innerProcess = innerProcess;

            this.handle = innerProcess.Id.ToString();

        }

измените GetInstance для работы со строковым аргументом Handle, только первые несколько строк этого аргумента, так как остальные остаются прежними.

        [ManagementBind]

        static public WIN32ServiceHost GetInstance(string Handle)

        {

            int processId;

            если (! Int32.TryParse(Handle, out processId))

            {

                возвращает значение NULL;

            }

 

            попробуйте выполнить следующее

            [...]

Необходимо выполнить повторную компиляцию и повторно развернуть новую сборку в глобальном кэше сборок. Для развертывания новой схемы используется InstallUtil.exe. Чем мы можем запросить систему с помощью немного измененной командыwmic.exe .

C:>wmic /NAMESPACE:\\root\cimv2 PATH win32_servicehost получить /value

Возвращаемые экземпляры будут заполнены сведениями из обоих классов, win32_process и win32_servicehost. В выходных данных службы будут поступать из win32_servicehost , а все остальное — из win32_process. Чтобы упростить выходные данные, можно указать нужные столбцы.

C:>wmic PATH win32_servicehost get Handle,Caption,CommandLine,Services /value

Это становится еще интереснее, когда мы пытаемся перечислить win32_process. Такой запрос вернет все процессы и заполнит поле Службы только для экземпляров win32_servicehost.

C:>wmic PATH win32_process получить /value

Выходные данные могут быть немного подавляющими, поэтому просто дамп их в файл (добавив > out.txt в конце командной строки) и откройте в Блокноте, чтобы найти свойство Services. Чтобы понять, что происходит, мы можем показать системные свойства, чтобы определить для каждого экземпляра, из какого класса WMI он является.

C:>wmic PATH win32_process get Handle,CommandLine,__CLASS /value

Из результирующего списка выберите экземпляр win32_ServiceHost и отобразите его значения.

C:>wmic path WIN32_Process.Handle="536" get /value

Аналогичные операции можно выполнять из любого клиентского приложения WMI с помощью сценариев Windows, Microsoft PowerShell, управляемого или машинного кода. Система будет обрабатывать эту сборку так же, как и любой другой поставщик.

Реализация методов

WMI.NET поддерживает как статические, так и для каждого экземпляра методы. В нашем случае мы добавим метод для остановки всех служб, размещенных в процессе, чтобы обеспечить чистую остановку процесса, не убивая его во время работы служб. Чтобы сделать открытый метод видимым в WMI, мы помечаем его атрибутом ManagementTask .

        [ManagementTask]

        public bool StopServices(int millisecondsWait)

        {

            if (innerProcess.Id == 0)

                возвращает значение false;

            ServiceController[] services = ServiceController.GetServices();

 

            bool oneFailed = false;

            using (SCM scm = new SCM())

            {

                для (int svcIndex = 0; службы svcIndex < . Длина; svcIndex++)

                {

                    ServiceController crtService = services[svcIndex];

                    int processId = scm. GetProcessId(crtService.ServiceName);

                    if (processId == innerProcess.Id)

                    {

                        попробуйте выполнить следующее

                        {

                            crtService.Stop();

                            if (millisecondsWait != 0)

                            {

                                crtService.WaitForStatus( ServiceControllerStatus.Stopped,

                                                            new TimeSpan((long)millisecondsWait * 10000));

                            }

                        }

                        catch (System.ServiceProcess.TimeoutException)

                        {

                            oneFailed = true;

                        }

                        catch (System.ComponentModel.Win32Exception)

                        {

                            oneFailed = true;

                        }

                        catch (InvalidOperationException)

                        {

                            oneFailed = true;

                        }

                    }

                }

            }

            return !oneFailed;

        }

 Для вызова этого метода требуется экземпляр класса win32_servicehost. Мы получим список доступных узлов служб с вводом:

C:>wmic path win32_servicehost get handle,Services

и выберите один из них с наиболее неопасным списком служб (чтобы не вывести систему из строя, кроме того, некоторые службы также могут быть не остановимы) и используйте его свойство Handle для идентификации экземпляра вызова.

win32_servicehost пути C:>wmic. Handle="540" CALL StopServices(0)

Исключения и отчеты об ошибках

Исключения являются важным аспектом WMI.NET. Инфраструктура использует некоторые исключения для передачи информации и обрабатывает большинство исключений как необработанные.

Принятые исключения

WMI.NET обрабатывает только несколько исключений, которые будут описаны далее в этой статье. Все остальные исключения рассматриваются как ошибки программирования и обрабатываются как необработанные исключения, вызывающие сбой узла поставщика WMI. WMI.NET сообщает об необработанных исключениях в журнале событий Windows.

Принятые исключения фактически претворяются в коды ошибок WMI и отправляются обратно в код клиента, как показано в таблице 1. Таким образом, поставщики WMI.NET будут вести себя так же, как и любой другой собственный поставщик.

System.OutOfMemoryException

WBEM_E_OUT_OF_MEMORY

System.Security.SecurityException

WBEM_E_ACCESS_DENIED

System.ArgumentException

WBEM_E_INVALID_PARAMETER

System.ArgumentOutOfRangeException

WBEM_E_INVALID_PARAMETER

System.InvalidOperationException

WBEM_E_INVALID_OPERATION

System.Management.Instrumentation.InstanceNotFoundException

WBEM_E_NOT_FOUND

System.Management.Instrumentation.InstrumentationException

Из внутреннего исключения, описанного далее в статье

Table1 — преобразование исключений в ошибки WMI

В списке выше всех, кроме двух, являются общие исключения .NET Framework. Все перечисленные исключения можно создавать, чтобы сообщить клиенту о конкретном состоянии, которое представляют эти исключения.

В пространство имен System.Management.Instrumentation были добавлены два новых исключения, как описано далее.

InstanceNotFoundException

Это исключение служит для уведомления о том, что запрошенный экземпляр не найден. В примере в этой статье используется статический метод GetInstance для привязки к данному экземпляру и возвращает значение NULL, если экземпляр не найден. Конструктор можно использовать для той же цели, но ему потребуется создать исключение InstanceNotFoundException , если не удается найти требуемый экземпляр. Ниже приведен конструктор для замены статического метода GetInstance.

        [ManagementBind]

        public WIN32ServiceHost(string Handle)

        {

            int processId;

            если (! Int32.TryParse(Handle, out processId))

            {

                создание экземпляра InstanceNotFoundException();

            }

 

            попробуйте выполнить следующее

            {

                Process process = Process.GetProcessById(processId);

                this.innerProcess = process;

                this.handle = Handle;

                if (this. Services == null)

                {

                    создание экземпляра InstanceNotFoundException();

                }

            }

            создается GetProcessById, если процесс с заданным идентификатором не найден

            catch (ArgumentException)

            {

                создание экземпляра InstanceNotFoundException();

            }

        }

InstrumentationException

InstrumentationException — это оболочка для обрабатываемых исключений. Поставщик может сообщить клиенту об ошибке, которая произошла на его стороне. Для этого создается исключение InstrumentationException. Обратите внимание, что разработчик должен иметь в виду, что исключение возвращается в систему WMI. Таким образом, WMI.NET будет стараться изо всех сил, чтобы превратить его в COM HRESULT. Чтобы получить точный код ошибки обратно клиенту, необходимо передать в качестве внутреннего исключения для InstrumentationException класс Exception , который позволяет задать внутренний HResult в базовом классе исключений напрямую.

Отчеты об ошибках

Любое исключение, не указанное в предыдущем разделе, будет считаться необработанным исключением, что приведет к сбою, сообщаемому как "Необработанное исключение (System.ExecutionEngineException) произошло в wmiprvse.exe[<NNNN>]".", NNNN является номером процесса. Ошибка и стек будут представлены в журнале событий. Наличие символов в той же папке, что и сборка, приведет к завершению стека с именем файла и номером строки.

Другой случай ошибки заключается в том, что не удается загрузить сборку, так как она не была развернута в глобальном кэше сборок. WMI.NET возвращает ошибку загрузки поставщика (WBEM_E_PROVIDER_LOAD_FAILURE) в этом случае.

Частой проблемой во время разработки поставщика является несоответствие между схемой WMI и кодом. Это может произойти при развертывании новой сборки без развертывания схемы в WMI или при отсутствии проблем при развертывании с помощью InstallUtil.exe поскольку информация доступна только во время выполнения. Это было бы так, например, если во время перечисления был возвращен неправильный тип. Инфраструктура WMI.NET сообщает клиенту о сбое поставщика (ошибка WMI WBEM_E_PROVIDER_FAILURE), и в журнале событий Windows будет создано сообщение с описанием проблемы среды выполнения.

Другие советы

Все атрибуты и код WMI.NET находятся в пространстве имен System.Management.Instrumentation . Чтобы создать сборку для WMI.NET, проект должен иметь ссылки на System.Core.dll и System.Management.Infrastructure.dll , так как они содержат определение атрибутов и код среды выполнения соответственно. Более поздняя версия знает, как загружать инструментированные сборки, сопоставлять инструментированные классы с репозиторием WMI и вызывать их соответствующим образом.

Сохраните все классы для определенного типа приложений в одной сборке. Это значительно упрощает обслуживание и развертывание. 

Весь код в этой статье должен быть развернут и запущен от имени администратора. В Windows Vista для запуска от имени администратора потребуется командная строка с повышенными привилегиями безопасности. Поставщики обычно не требуют, чтобы клиенты были администраторами, если они не получают доступ к специально защищенным системным данным, как в нашем примере. 

Заключение

Новая библиотека управления в платформа .NET Framework 3.5 предоставляет разработчикам мощный инструмент для написания поставщиков WMI. Учитывая простоту модели программирования, написание поставщиков WMI становится довольно простой задачей, позволяющей реализовать большинство функций WMI. В примере в этой статье показано, как написать простой поставщик WMI, но библиотека также обеспечивает поддержку разработки разроженных поставщиков и реализации одноэлементных, наследование, абстрактных классов, ссылок и классов ассоциаций, что делает расширение поставщика WMI.NET версии 2 серьезной альтернативой разработке с использованием собственных интерфейсов WMI.

Листинг 1 . SCMInterop.cs

using System;

с помощью System.Runtime.InteropServices;

с помощью System.ServiceProcess;

 

 

пространство имен External.PInvoke

{

    [StructLayout(LayoutKind.Sequential)]

    внутренняя SERVICE_STATUS_PROCESS структуры

    {

        public uint dwServiceType;

        public uint dwCurrentState;

        public uint dwControlsAccepted;

        public uint dwWin32ExitCode;

        public uint dwServiceSpecificExitCode;

        public uint dwCheckPoint;

        public uint dwWaitHint;

        public uint dwProcessId;

        public uint dwServiceFlags;

        public static readonly int SizeOf = Marshal.SizeOf(typeof(SERVICE_STATUS_PROCESS));

    }

 

    [Флаги]

    внутреннее перечисление SCM_ACCESS : uint

    {

        SC_MANAGER_CONNECT = 0x00001,

    }

 

    [Флаги]

    внутреннее перечисление SERVICE_ACCESS : uint

    {

        STANDARD_RIGHTS_REQUIRED = 0xF0000,

        SERVICE_QUERY_CONFIG = 0x00001,

        SERVICE_CHANGE_CONFIG = 0x00002,

        SERVICE_QUERY_STATUS = 0x00004,

        SERVICE_ENUMERATE_DEPENDENTS = 0x00008,

        SERVICE_START = 0x00010,

        SERVICE_STOP = 0x00020,

        SERVICE_PAUSE_CONTINUE = 0x00040,

        SERVICE_INTERROGATE = 0x00080,

        SERVICE_USER_DEFINED_CONTROL = 0x00100,

        SERVICE_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED |

                          SERVICE_QUERY_CONFIG |

                          SERVICE_CHANGE_CONFIG |

                          SERVICE_QUERY_STATUS |

                          SERVICE_ENUMERATE_DEPENDENTS |

                          SERVICE_START |

                          SERVICE_STOP |

                          SERVICE_PAUSE_CONTINUE |

                          SERVICE_INTERROGATE |

                          SERVICE_USER_DEFINED_CONTROL)

    }

 

    внутреннее перечисление SC_STATUS_TYPE

    {

        SC_STATUS_PROCESS_INFO = 0

    }

 

    внутренний класс ServiceHandle: SafeHandle

    {

        public ServiceHandle()

            : base(IntPtr.Zero, true)

        {

        }

 

        public void OpenService(SafeHandle scmHandle, string serviceName)

        {

            IntPtr serviceHandle = SCM. OpenService(scmHandle, serviceName, SERVICE_ACCESS. SERVICE_QUERY_STATUS);

 

            if (serviceHandle == IntPtr.Zero)

            {

                создать исключение System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error(),

                                                                "SCM. QueryServiceStatusEx");

            }

            SetHandle(serviceHandle);

        }

 

        protected override bool ReleaseHandle()

        {

            возвращает SCM. CloseServiceHandle(base.handle);

        }

 

        public override bool IsInvalid

        {

            get { return IsClosed || handle == IntPtr.Zero; }

        }

    }

 

    внутренний класс SCM: SafeHandle

    {

        [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerW", CharSet = CharSet.Unicode,

                                                                     SetLastError = true)]

        public static extern IntPtr OpenSCManager(string machineName,

                                                  string databaseName,

                                    [MarshalAs(UnmanagedType.U4)] SCM_ACCESS dwAccess);

 

        [DllImport("advapi32.dll", EntryPoint = "OpenServiceW", CharSet = CharSet.Unicode,

                                                                     SetLastError = true)]

        public static extern IntPtr OpenService(SafeHandle hSCManager,

                                    [MarshalAs(UnmanagedType.LPWStr)] строка lpServiceName,

                                    [MarshalAs(UnmanagedType.U4)] SERVICE_ACCESS dwDesiredAccess);

 

        [DllImport("advapi32.dll", EntryPoint = "QueryServiceStatusEx", CharSet = CharSet.Auto,

                                                                     SetLastError = true)]

        public static extern bool QueryServiceStatusEx(SafeHandle hService,

                                                      SC_STATUS_TYPE InfoLevel,

                                                      ссылка SERVICE_STATUS_PROCESS dwServiceStatus,

                                                      int cbBufSize,

                                                      ref int pcbBytesNeeded);

 

        [DllImport("advapi32.dll", EntryPoint = "CloseServiceHandle", CharSet = CharSet.Auto,

                                                                     SetLastError = true)]

        public static extern bool CloseServiceHandle(IntPtr hService);

 

        public SCM()

            : base(IntPtr.Zero, true)

        {

            Дескриптор IntPtr = OpenSCManager(null, null, SCM_ACCESS. SC_MANAGER_CONNECT);

            Базы. SetHandle(handle);

        }

 

        protected override bool ReleaseHandle()

        {

            возвращает SCM. CloseServiceHandle(base.handle);

        }

 

        public override bool IsInvalid

        {

            get { return IsClosed || handle == IntPtr.Zero; }

        }

 

        public void QueryService(string serviceName, out SERVICE_STATUS_PROCESS statusProcess)

        {

            statusProcess = new SERVICE_STATUS_PROCESS();

            int cbBytesNeeded = 0;

 

            using (ServiceHandle serviceHandle = new ServiceHandle())

            {

                serviceHandle.OpenService(this, serviceName);

 

                bool scmRet = SCM. QueryServiceStatusEx(serviceHandle,

                                                        SC_STATUS_TYPE. SC_STATUS_PROCESS_INFO,

                                                        Ссылка statusProcess,

                                                        SERVICE_STATUS_PROCESS. Sizeof

                                                        ref cbBytesNeeded);

                if (!scmRet)

                {

                    создать исключение System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error(),

                                                                    "SCM. QueryServiceStatusEx");

                }

            }

        }

 

        public int GetProcessId(string serviceName)

        {

            SERVICE_STATUS_PROCESS serviceStatus;

            Это. QueryService(serviceName, out serviceStatus);

            return (int)serviceStatus.dwProcessId;

        }

    }

}

Листинг 2 — WIN32ServiceHost.cs

using System;

с помощью System.Collections;

using System.Collections.Generic;

с помощью System.Linq;

using System.Text;

 

с помощью System.ServiceProcess;

с помощью System.Diagnostics;

с помощью External.PInvoke;

с помощью System.Management.Instrumentation;

 

[сборка: WmiConfiguration(@"root\cimv2", HostingModel = ManagementHostingModel.NetworkService)]

 

 

пространство имен TestWMI.Hosted

{

    [System.ComponentModel.RunInstaller(true)]

    public class MyInstall: DefaultManagementInstaller

    {

    }

 

 

    [ManagementEntity(External = true)]

    Win32_Process абстрактного открытого класса

    {

        защищенный дескриптор строки;

 

        [ManagementKey]

        public string Handle

        {

            get {

                возвращает значение this.handle;

            }

        }

    }

 

    [ManagementEntity(Name = "WIN32_ServiceHost")]

    public class WIN32ServiceHost: Win32_Process

    {

        Process innerProcess;

 

        <Сводка>

        ///

        </Сводка>

        <param name="innerProcess"></param>

        public WIN32ServiceHost(Process innerProcess)

        {

            this.innerProcess = innerProcess;

            this.handle = innerProcess.Id.ToString();

        }

 

        идентификатор public int

        {

            get { return this.innerProcess.Id; }

        }

 

        [ManagementProbe]

        public string[] Services

        {

            get

            {

                if (innerProcess.Id == 0)

                    возвращает значение NULL;

                ServiceController[] services = ServiceController.GetServices();

                List<ServiceController> servicesForProcess = new List<ServiceController>();

                using (SCM scm = new SCM())

                {

                    для (int svcIndex = 0; службы svcIndex < . Длина; svcIndex++)

                    {

                        ServiceController crtService = services[svcIndex];

                        int processId = scm. GetProcessId(crtService.ServiceName);

                        if (processId == innerProcess.Id)

                        {

                            servicesForProcess.Add(services[svcIndex]);

                        }

                    }

                }

 

                if (servicesForProcess.Count == 0)

                    возвращает значение NULL;

                string[] servicesNames = новая строка[servicesForProcess.Count];

 

                для (int serviceIdx = 0; serviceIdx < servicesForProcess.Count; serviceIdx++)

                {

                    servicesNames[serviceIdx] = servicesForProcess[serviceIdx]. ServiceName;

                }

                return servicesNames;

            }

        }

 

        [ManagementEnumerator]

        static public IEnumerable EnumerateServiceHosts()

        {

            Process[] processes = Process.GetProcesses();

            foreach (process crtProcess in process)

            {

                WIN32ServiceHost crtServiceHost = new WIN32ServiceHost(crtProcess);

                if (crtServiceHost.Services != null)

                {

                    yield return crtServiceHost;

                }

            }

        }

 

        [ManagementBind]

        public WIN32ServiceHost(string Handle)

        {

            int processId;

            если (! Int32.TryParse(Handle, out processId))

            {

                создание экземпляра InstanceNotFoundException();

            }

 

            попробуйте выполнить следующее

            {

                Process process = Process.GetProcessById(processId);

                this.innerProcess = process;

                this.handle = Handle;

                if (this. Services == null)

                {

                    создание экземпляра InstanceNotFoundException();

                }

            }

            создается GetProcessById, если процесс с заданным идентификатором не найден

            catch (ArgumentException)

            {

                создание экземпляра InstanceNotFoundException();

            }

        }

 

        static public WIN32ServiceHost GetInstance(string Handle)

        {

            int processId;

            если (! Int32.TryParse(Handle, out processId))

            {

                возвращает значение NULL;

            }

 

            попробуйте выполнить следующее

            {

                Process process = Process.GetProcessById(processId);

                WIN32ServiceHost crtServiceHost = new WIN32ServiceHost(process);

                if (crtServiceHost.Services != null)

                {

                    return crtServiceHost;

                }

                else

                {

                    возвращает значение NULL;

                }

            }

            создается GetProcessById, если процесс с заданным идентификатором не найден

            catch (ArgumentException)

            {

                возвращает значение NULL;

            }

        }

 

        [ManagementTask]

        public bool StopServices(int millisecondsWait)

        {

            if (innerProcess.Id == 0)

                возвращает значение false;

            ServiceController[] services = ServiceController.GetServices();

 

            bool oneFailed = false;

            using (SCM scm = new SCM())

            {

                для (int svcIndex = 0; службы svcIndex < . Длина; svcIndex++)

                {

                    ServiceController crtService = services[svcIndex];

                    int processId = scm. GetProcessId(crtService.ServiceName);

                    if (processId == innerProcess.Id)

                    {

                        попробуйте выполнить следующее

                        {

                            crtService.Stop();

                            if (millisecondsWait != 0)

                            {

                                crtService.WaitForStatus( ServiceControllerStatus.Stopped,

                                                            new TimeSpan((long)millisecondsWait * 10000));

                            }

                        }

                        catch (System.ServiceProcess.TimeoutException)

                        {

                            oneFailed = true;

                        }

                        catch (System.ComponentModel.Win32Exception)

                        {

                            oneFailed = true;

                        }

                        catch (InvalidOperationException)

                        {

                            oneFailed = true;

                        }

                    }

                }

            }

            return !oneFailed;

        }

    }

}

 

 

 

© 2008 Корпорация Майкрософт. Все права защищены.