Ei, Equipe de Scripts!Investigando a fundo o WMI

A Equipe de Scripts da Microsoft

Sumário

Namespaces
Classes WMI
Propriedades
Métodos

Quando um sujeito da equipe de scripts era mais jovem, havia dois hobbies que ele realmente adorava. Um era beber bebidas fermentadas; o outro era acampar no inverno. Ele desconfia que os dois estavam relacionados. Um amigo dele dessa época gostava de sugerir que eles "entrassem mais a fundo". Bem, este mês seguiremos o conselho do velho amigo. Iremos nos aprofundar na selva do WMI (isto é, na Instrumentação de Gerenciamento do Windows).

Felizmente, não precisamos fazer caminhada para nenhum lugar neste caso, exceto talvez até a máquina de café e de volta ao nosso lugar. Em vez disso, iremos investigar o WMI a fundo usando scripts.

Bem, a Equipe de Scripts é bem conhecida por ser muito prática. Tentamos apresentar soluções para problemas reais, em vez de explicações grandiosas que parecem deixar poucos detalhes — por exemplo, como realmente realizar algo — ao leitor. Não pense que estamos indo nessa onda. Embora essa coluna não se concentre em realizar determinadas tarefas de administração do sistema, ela possui um objetivo prático. O objetivo principal é instruí-lo sobre a infra-estrutura do WMI. E também gostaríamos de colocar alguns scripts de investigação úteis nas suas mãos. Abra o Bloco de Notas — iremos mergulhar!

Namespaces

O repositório do WMI é um banco de dados e é usado para armazenar o CIM (Modelo Comum de Informações). Esse modelo é orientado a objeto, o que significa que ele consiste em um conjunto de descrições (classes WMI) que representam coisas que o WMI pode gerenciar. A classe WMI Win32_Process, por exemplo, representa processos. As classes WMI são armazenadas em diferentes seções do repositório do WMI. Uma seção do repositório do WMI é conhecida como um namespace. Se você se deparasse com um repositório do WMI na selva, a primeira coisa que você provavelmente observaria seria que ele é dividido nesses namespaces de alto nível. O script exibido na Figura 1

Figura 1 Exibindo namespaces

strComputer = "."
Call EnumNameSpaces("root")

Sub EnumNameSpaces(strNameSpace)
    On Error Resume Next
    WScript.Echo strNameSpace
    Set objWMIService=GetObject _
        ("winmgmts:{impersonationLevel=impersonate}\\" & _ 
            strComputer & "\" & strNameSpace)

    Set colNameSpaces = objWMIService.InstancesOf("__NAMESPACE")

    For Each objNameSpace in colNameSpaces
        Call EnumNameSpaces(strNameSpace & "\" & objNameSpace.Name)
    Next
End Sub

exibe os nomes de todos os namespaces no repositório do WMI no computador especificado em strComputer (um ponto significa o computador local).

Os namespaces podem incluir subnamespaces. Você pode pensar na coisa toda como se fosse uma estrutura de diretórios. Então, se fôssemos apenas exigir todos os namespaces (a partir do namespace root de nível superior), iríamos recuperar apenas o primeiro nível de namespaces dentro de root, o que perderia qualquer subnamespace. Em vez disso, usamos o truque da recursão. Criamos uma sub-rotina chamada EnumNameSpaces que adota um namespace como parâmetro e retorna todos os seus subnamespaces.

Damos início às coisas chamando EnumNameSpaces com root como o parâmetro. Isso retornará todos os namespaces no namespace root. Em seguida, fazemos a recursão. Observe que EnumNameSpaces realmente chama a si próprio, transmitindo cada um dos subnamespaces que ele identifica. Dê um grande gole no seu café e pense nisso. O resultado é que cada namespace será processado e, se ele possuir subnamespaces, todos serão exibidos.

Observe que incluímos uma instrução On Error Resume Next no início da sub-rotina. Isso ocorre se você executa o script sob um contexto de segurança que não possui acesso a todos os namespaces. Se for este o caso, o script ainda será executado, embora fique um pouco lento, uma vez que ele espera tempos limites.

Sim, você poderia simplesmente usar wbemtest.exe (está em todos os computadores com o WMI instalado) ou Scriptomatic (go.microsoft.com/fwlink/?LinkId=125976) para fazer isso. Mas com um script de partida e suas habilidades de elite em scripts, você pode filtrar esses namespaces ou transmiti-los ao Excel ou comparar os namespaces em dois computadores.

Agora que podemos ver como o repositório é dividido, vamos desenvolver um script que nos permite examinar o que há em cada uma dessas seções. Sabemos que as classes WMI são armazenadas em cada um desses namespaces, então vamos começar listando-os.

Classes WMI

Lembra que mencionamos que o repositório do WMI hospeda o Modelo Comum de Informações? Bem, esse modelo é armazenado no namespace CIMV2 (V2 para a versão 2). Se você olhar no namespace CIMV2, deverá ver todas as classes WMI que formam esse modelo. O script a seguir faz isso:

strComputer = "."
Set objWMIService=GetObject("winmgmts: _
    {impersonationLevel=impersonate}!\\" & _
    strComputer & "\root\cimv2")

For Each objclass in 
    objWMIService.SubclassesOf()
    Wscript.Echo objClass.Path_.Class
Next

Vamos ver exatamente como esse script funciona. A chamada a GetObject retorna um objeto SWbemServices. GetObject é uma função de VBScript que retorna referências a objetos COM programáveis por scripts. Neste caso, porque transmitimos a ele a cadeia de caracteres "winmgmts:…", GetObject retorna um objeto da Biblioteca de Scripts do WMI. Observe que essa cadeia de caracteres transmitida a GetObject inclui o namespace ao qual estamos nos conectando, \root\cimv2, neste caso. Então, o objeto SWbemServices que possuímos para trabalhar está vinculado ao namespace particular que especificamos. Consulte a documentação do SWbemServices para ver quais possíveis ações você pode adotar no seu script.

Uma que provavelmente parece muito familiar a você é ExecQuery. Esse é o método que permite executar uma consulta WQL (linguagem de consulta da Instrumentação de Gerenciamento do Windows) em relação ao namespace ao qual você está conectado. Mas existem várias outras ações que você pode adotar depois de ter um objeto SWbemServices associado a um namespace.

Queremos ver todas as classes WMI no namespace e SubClassesOf realiza essa ação. A documentação diz que ele retorna um SWbemObjectSet. Isso não soa como algo que você gostaria de enfrentar sozinho na floresta! Apenas se concentre nas últimas três letras — é um conjunto (set). E, como os criadores de script WMI sabem, você pode percorrer um conjunto usando For Each.

Agora, o que não é muito surpreendente, cada membro de um SWbemObjectSet é um SWbemObject. Cada um desses SWbemObjects representa uma das classes WMI no namespace CIMV2. Dê uma olhada na documentação de SWbemObject e você verá todas as informações que poderia transmitir sobre essas classes.

No nosso script, escolhemos apenas exibir o nome da classe. Para fazer isso, acessamos a propriedade Path_. Acontece que a propriedade Path_ é em si um objeto. É um SWbemObjectPath e, sendo um objeto, possui várias de suas próprias propriedades. Estamos usando Class, que é o nome da classe.

Então, não apenas você possui um script que pode exibir todas as classes WMI em um namespace, mas também possui um script que pode ser facilmente atualizado para exibir várias outras coisas pertencentes a essas classes. Por exemplo, as classes WMI podem ser extensões de outras classes WMI. Imagine que você possui um modelo para um carro (Win32_Car), mas você realmente precisa gerenciar caminhonetes. Tudo no modelo do carro se aplica a caminhonetes.

Mas você precisa de algumas coisas adicionais, como um Booleano indicando se ele possui aquele painel de madeira caprichado. Você não iria gostar de recriar todas as funcionalidades do Win32_Car. O que você iria gostar seria um mecanismo para estender a classe Win32_Car para incluir todas as propriedades novas. O WMI inclui exatamente esse mecanismo.

Para ver se uma classe WMI herda propriedades de outra classe WMI, você pode verificar a propriedade Derivation_ da classe SWbemObject associada a essa classe WMI. O script na Figura 2 exibe as classes WMI no namespace CIMV2 juntamente com uma lista das classes das quais elas derivam.

Figura 2 Derivações da classe CIMV2

strComputer = "."
Set objWMIService=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
    strComputer & "\root\cimv2")

For Each objclass in objWMIService.SubclassesOf()
    WScript.StdOut.Write objclass.Path_.Class
    arrDerivativeClasses = objClass.Derivation_ 
    For Each strDerivativeClass in arrDerivativeClasses 
       WScript.StdOut.Write " <- " & strDerivativeClass
    Next
    WScript.StdOut.Write vbNewLine
Next

    For Each objNameSpace in colNameSpaces
        Call EnumNameSpaces(strNameSpace & "\" & objNameSpace.Name)
    Next
End Sub

O script é iniciado exatamente como o anterior. Ele usa WScript.StdOut.Write em vez de WScript.Echo para evitar adicionar automaticamente um novo caractere de linha após a cadeia de caracteres exibida. (Observação: você precisa executar seu script usando Cscript.exe em vez de Wscript.exe para que StdOut.Write funcione.)

Ao examinar a documentação de SWbemObject, você poderá ver que ele possui uma propriedade Derivation_. Essa propriedade é uma matriz de cadeias de caracteres que contém os nomes de classes das quais a classe atual foi derivada. No nosso script, percorremos essa matriz usando For Each e exibimos todas as classes separadas por uma seta ascii. Depois que você estiver acostumado a começar com o objeto SWbemServices retornado por GetObject e estiver percorrendo as possibilidades na documentação da Biblioteca de Scripts do WMI, você poderá testar propriedades e métodos nesses objetos com nomes estranhos para descobrir o que é possível.

Entender com que objeto da Biblioteca de Scripts do WMI você está trabalhando, em qualquer ponto do seu script, permite que você passe para o próximo nível de script do WMI. Em vez de usar apenas nossos scripts, você também entenderá por que podemos chamar ExecQuery ou fazer referência à propriedade Properties_. E você poderá levar as coisas adiante.

A ferramenta Scriptomatic não corresponde às suas expectativas? Sem problemas. Vá em frente e a modifique para fazer exatamente o que você quer. Talvez outras pessoas comprem de você a sua criação. Neste caso, apenas envie um email com as instruções para scripter@microsoft.com detalhando como iremos receber nossos pagamentos de royalty.

Propriedades

Cada classe WMI modela algo que você pode gerenciar usando um conjunto de propriedades e métodos. As propriedades são as características da coisa. Um processo, por exemplo, possui uma identificação e uma prioridade e usa uma certa quantidade de memória. Essas propriedades são todas incluídas na classe WMI Win32_Process.

Depois de ter identificado a classe para gerenciar uma entidade, examine as propriedades disponíveis para ver se o que você deseja gerenciar está no modelo de gerenciamento. A classe SWbemObject inclui uma propriedade chamada Properties_. Engraçado, não? O valor dessa propriedade é um objeto SWbemPropertySet que inclui uma coleção de objetos SWbemProperty. Cada um desses objetos SWbemProperty corresponde a uma propriedade na classe WMI associada a SWbemObject. Eu sei. Todos esses nomes SWbem* fazem tudo soar terrivelmente complicado, mas não é tão ruim assim. Observe a Figura 3.

fig03.gif

Figura 3 SWbemObject expõe as propriedades da classe WMI à qual ele está vinculado (clique na imagem para ampliá-la)

Tenha em mente que as classes que começam com SWbem* são membros da biblioteca de objetos de script WMI. Esses são os objetos que permitem a você trabalhar com o WMI. Eles não são parte do modelo WMI de coisas que você pode gerenciar.

Na Figura 3, SWbemObject representa uma classe WMI, Win32_SomeClass, que possui propriedades: Property_1, Property_2 e Property_3. Ele as expõe por sua própria propriedade Properties_. É claro que, se estivesse vinculado a outra classe WMI, Win32_SomeOtherClass, o nome de sua propriedade não seria alterado. Ainda seria Properties_. Mas as propriedades da classe à qual ele está vinculado provavelmente serão diferentes.

Essencialmente, SWbemObject assume as propriedades da classe WMI particular à qual ele está vinculado, mas permite que você chegue a essas propriedades diferentes usando o mesmo mecanismo Properties_. Faz sentido? Tome outro gole de café e medite sobre o diagrama. Tudo ficará claro.

O script na Figura 4 aproveita SWbemObject e sua propriedade Properties_ para recuperar e exibir todas as propriedades da classe WMI Win32_Service. O início do script deveria ser familiar. A principal alteração é que fatoramos o namespace e a classe WMI, tornando-os mais fáceis de serem alterados. Por exemplo, você pode apenas alterar o valor de strClass para Win32_BIOS para ver as propriedades dessa classe, em vez das propriedades de Win32_Service. No loop For Each, iteramos pela coleção SWbemPropertySet (objClass.Properties) exibindo o nome de cada SWbemProperty.

Figura 4 Obtendo as propriedades de Win32_Service

strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_Service"

Set objClass = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _ 
    strComputer & "\" & strNameSpace & ":" & strClass)

WScript.Echo strClass & " Class Properties"
WScript.Echo "------------------------------"

For Each objClassProperty in objClass.Properties_
    WScript.Echo objClassProperty.Name
Next

Métodos

Finalmente, algumas classes WMI vão além de propriedades de modelagem ou características de uma entidade gerenciável e incluem métodos que fornecem acesso a comportamentos ou ações que uma entidade pode adotar — ou adotou em relação a ela.

O formato do script a retornar todos os métodos de uma classe WMI (mostrado na Figura 5) fica exatamente em paralelo com o script que retorna propriedades.

Figura 5 Obtendo os métodos para uma classe WMI

strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_Service"

Set objClass = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _ 
    strComputer & "\" & strNameSpace & ":" & strClass)

WScript.Echo strClass & " Class Methods"
WScript.Echo "---------------------------"

For Each objClassMethod in objClass.Methods_
    WScript.Echo objClassMethod.Name
Next

A diferença, é claro, é que a propriedade Methods_ é usada no lugar da propriedade Properties_. Agora, eu penso, também seria possível exibir os tipos dos parâmetros que esses métodos adotam? Como podemos exibir apenas as classes WMI que realmente possuem métodos? Esses são os tipos de perguntas para cujas respostas você deveria tentar escrever scripts após terminar de ler esta coluna.

Esperamos que tenhamos fornecido a você uma primeira idéia útil sobre como investigar a fundo o WMI. Você realmente precisa encontrar o seu caminho pela densa floresta do SWbem*s. Mas os scripts fornecem um mecanismo leve e interessante para você se aprofundar. Os dois amigos do acampamento de inverno não tiveram tanta sorte. Eles realmente nunca chegaram muito a fundo. Acontece que um suprimento adequado das bebidas fermentadas necessárias não era transportado com muita facilidade nas profundezas da floresta no meio do inverno.

A Equipe de Scripts trabalha na – quer dizer, é contratada pela – Microsoft. Quando não está jogando/treinando/assistindo beisebol (e diversas outras atividades), ela administra o Script Center da TechNet. Confira no site www.scriptingguys.com.