JavaScript, Visual Basic, WMI

Fundamentos

WMI (Windows Management Instrumentation)
Programación del sistema operativo con JavaScript y Visual Basic

Septiembre/2000

Marino Posadas

Marino Posadas es MVP en .NET Framework, Microsoft Certified Solution Developer (MCSD), MCAD (Microsoft Certified Application Developer en .NET), MCT(Microsoft Certified Trainer) y autor de numerosos artículos para revistas especializadas, así como de varias obras editadas por Editorial Ra-Ma en solitario y junto a Grupo EIDOS. Trabaja como Consultor para Grupo EIDOS.


La aparición de Windows 2000 no sólo ha supuesto un cambio importante en las posibilidades de administración del sistema operativo, sino también un reto para el programador en nuevas tecnologías, gracias a un conjunto de servicios que, en parte extienden (COM+) y en parte introducen avances sobre lo disponible bajo Win98/NT.

Una de esas novedades es WMI (Windows Management Instrumentation), el nombre que Microsoft ha dado a su implementación de un estándar para la gestión integrada de sistemas llamado Web Based Enterprise Management (gestión basada en la web). Dicha información incluye información sobre el estado de la memoria del sistema, inventarios de todas las aplicaciones instaladas en un equipo o en un dominio, y mucha otra información de cliente adicional.

Se ha implementado para todas las plataformas de Windows (incluyendo Windows 95), y permite que sistemas, aplicaciones, redes y componentes sean representados utilizando un modelo de objetos programable llamado CIM (Common Information Model o Modelo común de información), otro estándar publicado por la DMTF (Distributed Management Task Force).

Además de la modelización de datos, WMI ofrece un potente conjunto de servicios de base que incluyen consultas sobre información genérica del sistema y aplicaciones y suscripción a eventos (la capacidad que tiene el sistema de avisar al usuario cuando se produzca una circunstancia predefinida por él). La programación de estos servicios se basa en COM y el paquete de desarrollo de WMI incluye herramientas para crear aplicaciones cliente y proveedores WMI.

Requisitos del sistema

Para instalar el paquete de desarrollo (WMI SDK), es necesario tener instalado Windows 2000 ó Windows NT 4.0 SP5, además de Internet Explorer 5.0 (por la máquina virtual de Java). Debe de instalarse con permiso de administrador bajo estas plataformas.

Para la instalación de los servicios cliente, puede usarse Windows 2000, Windows NT 4 SP4, Windows 98 o Windows 95 SR2. En cuanto a hardware, se precisa un Pentium con 32MB de RAM, 30MB de espacio en disco, tarjeta gráfica de 256 colores (resolución de 800/600) y tarjeta de red. Finalmente, respecto al compilador, WMI está pensado para usar Visual Studio 6.0.

Infraestructura de WMI

WMI consiste, fundamentalmente, de dos elementos:

Pero la utilización de WMI, supone la presencia de un programa (aplicación gestora), que haga uso de los servicios proporcionados por WMI. Para obtener la información, la aplicación realizará peticiones mediante métodos de las COM API o bien de las Scripting API, dos interfaces de programación que exponen al desarrollador un conjunto de métodos de acceso a los servicios de WMI.

Mediante estas API, el programador tiene acceso a un variado conjunto de servicios, tales como inventarios de red, información de configuración y utilización del sistema, respuesta a evento o la puesta en marcha o detención de servicios del sistema. Para conseguirlo, WMI soporta varias estrategias para la implementación de aplicaciones de gestión de sistema:

Mientras el COM API está disponible directamente a los programadores de C++, Scripting API puede ser usado por programadores que conozcan Visual Basic (estándar o script) para controlar o visualizar los objetos del CIM. Concretamente, WMI suministra soporte para los siguientes lenguajes:

El gráfico adjunto (tomado del WMI SDK) ilustra estos posibles escenarios de trabajo:

 

 

Proveedores WMI

Los proveedores WMI actúan como intermediarios entre WMI y los objetos manejados. Cuando WMI recibe una petición de una aplicación acerca de información que no está disponible en el repositorio CIM o en el caso de necesitarse una notificación sobre un evento que no soporta CIM directamente, se emite esa petición al proveedor. Cada proveedor suministra información específica de su dominio. Desde el punto de vista de la programación, dichos servidores son componentes DCOM (ActiveX DLL, servicios de NT/2000 locales o remotos o ejecutables estándar). También es posible crear proveedores específicos de usuario para aplicaciones de terceras partes.

CIM

La labor de CIM es presentar al programador un modelo consistente y uniforme de todos los tipos de objetos físicos y lógicos de un sistema. Los objetos se representan mediante clases que contienen propiedades y métodos para describir sus características y comportamiento.

En CIM se definen 3 niveles de clases:

Pueden existir clases derivadas, que heredan de sus ancestros tanto métodos como propiedades.

El programa WinMgmt.exe

Winmgt.exe es el componente principal de la infraestructura de gestión de WMI. Bajo Windows 98, funciona como un ejecutable independiente. Bajo Windows NT/2000, como un servicio. Cuando una aplicación de gestión WMI solicita un dato, el programa determina si éste se encuentra en el repositorio CIM o es necesario obtenerlo a partir de un proveedor. Como norma general, los datos estáticos suelen encontrarse siempre en el repositorio CIM, mientras que los dinámicos requieren el concurso de un proveedor.

Además de estas labores, WMI suministra los siguientes servicios adicionales:

WMI en la práctica

La explicación completa de este sistema de gestión de entornos de trabajo está fuera del ámbito de ésta introducción, si bien puede encontrarla el lector en el SDK de WMI accesible desde la dirección de Internet http://msdn.microsoft.com/downloads/sdks/wmi/tutorial.asp.

No obstante, para completar esta visión inicial del producto, vamos a poner algunos ejemplos de funcionamiento, que ayudarán a comprender sus posibilidades.

Llamadas a valores del sistema

Con WMI podemos obtener mediante programación valores del sistema operativo y datos de configuración. Para ello, debemos usar los objetos definidos a tal efecto y llamarlos utilizando una de las dos interfaces de programación de aplicaciones que hemos comentado anteriormente: El API COM (a través de C/C++) o bien el Script API, para las llamadas desde entornos de más alto nivel, como Visual Basic, o una página Web.

1) Llamadas desde una página Web usando Javascript

Muchos de los métodos de Scripting WMI se encuentran disponibles como métodos síncronos o asíncronos. Cuando usamos Javascript para llamar a un servicio y obtener un valor, debemos tener instalados algunos componentes que nos permitan establecer esas conexiones, y más tarde invocar sus métodos y propiedades. 

En el ejemplo, se utiliza una página web muy simple, en la que se hace referencia a un objeto que nos proporciona el acceso a los datos del CIM, llamado WbemScripting.SWbemLocator.

La salida en el explorador es la siguiente:

 

Información del servicio mostrada por la página de prueba en Internet Explorer 5

 

Una vez que se muestra la ventana, ya existe una instancia del objeto WbemScripting.SWbemLocator, con el nombre Buscador. Al pulsar sobre la página, se invoca el método ConnectServer, para obtener una referencia a los servicios de CIM. A continuación  se llama al método Get de ese objeto, para obtener una referencia al servicio de Win32 llamando Winmgmt (el propio servicio de CIM). Finalmente, se muestra el valor correspondiente al nombre del servicio (DisplayName) en un objeto SPAN de HTML. La secuencia completa de código es la siguiente:

 

Código de la llamada al nombre de un servicio mediante Javascript

<!- Copyright (c) 1997-1999 Microsoft Corporation->
<!- Modificado y traducido por Marino Posadas (Grupo EIDOS) ->
<HTML>
<!---------------------------------------------------------------

Ejemplo de uso de WMI Scripting API en una página
HTML, usando JavaScript. Muestra el nombre de 
Win32_service usando un API síncrono. Se instancia 
el objeto WMI mediante su CLSID

------------------------------------------------------------------>

<head>
<title>WMI Scripting HTML (JavaScript síncrono)</title>
<script FOR="document" EVENT="onclick()" LANGUAGE="JScript">
var servicio = Buscador.ConnectServer();
var proceso = servicio.Get('Win32_Service.Name="Winmgmt"');
document.all.DatosServicio.innerText = proceso.DisplayName;
</script>
</head>

<body>

<P>Pulse sobre la ventana para obtener el nombre del servicio: 
<SPAN style="color:red" ID="DatosServicio">Desconocido</SPAN>. 
<object ID="Buscador" 
CLASSID="CLSID:76A64158-CB41-11D1-8B02-00600806D9B6">
</object>
</P>
</body>
</HTML>

 

De la misma forma, podríamos haber usado VBScript, instanciando el objeto Buscador a través de su nombre registrado (WbemScripting.SWbemLocator), según el código adjunto:

 

Código de la llamada al nombre de un servicio mediante VBScript 

<!- Copyright (c) 1997-1999 Microsoft Corporation->
<!- Modificado y traducido por Marino Posadas (Grupo EIDOS) ->
<HTML>
<!---------------------------------------------------------------

Ejemplo de uso de WMI Scripting API en una página
HTML, usando VBScript. Muestra el nombre de 
Win32_service usando un API síncrono. Se instancia 
el objeto WMI mediante su nombre registrado.

------------------------------------------------------------------>

<head>
<title>WMI Scripting HTML (VBScript síncrono)</title>
<script LANGUAGE="JScript">

Sub window_onload()

Set Buscador = CreateObject("WbemScripting.SWbemLocator")
var servicio = Buscador.ConnectServer();
var proceso = servicio.Get('Win32_Service.Name="Winmgmt"');
document.all.DatosServicio.innerText = proceso.DisplayName;

End Sub
</script>
</head>

<body>

<P>Pulse sobre la ventana para obtener el nombre del servicio: 
<SPAN style="color:red" ID="DatosServicio">Desconocido</SPAN>. 
</P>
</body>
</HTML>

 

Como vemos, los dos ejemplos son muy similares, con la salvedad del lenguaje de script utilizado y la forma de instanciar el objeto.

La otra forma de trabajo que mencionábamos, es a través de un lenguaje de programación como Visual Basic. Vamos a ver un ejemplo del SDK de Microsoft, adaptado y traducido para este artículo. Se trata de un formulario que permite visualizar el estado de todos los servicios de Windows 2000/NT y permite detenerlos, reanudarlos, e iniciarlos.

El ejemplo utiliza un formulario básico de Visual Basic con unos botones de control y un ListView para mostrar la lista de servicios. Para conseguir el manejo de los objetos que representan los servicios, usa instancias de las clases predefinidas de WMI SWbemLocator (para los accesos, como en el programa anterior) y SWbemServices,  para los servicios del sistema. Así mismo, para controlar el momento en que un servicio ha cambiado su estado (Iniciado, Detenido, etc.), utiliza una instancia del objeto SWbemSink, para capturar el evento OnObjectReady, que tiene lugar cada vez que un objeto WMI cambia su estado.

Una vez que el programa se pone en funcionamiento, obtenemos una lista de servicios en el ListView como se aprecia en la figura adjunta:

 

Vista de los Servicios de Windows 2000 mediante el ejemplo

 

Y el código fuente correspondiente a dicha salida es el siguiente:

 

Título explicativo o nombre del fichero asociado al código fuente

' Copyright (c) 1997-1999 Microsoft Corporation
' Revisión y Adaptación: Marino Posadas (Grupo EIDOS)
'-------------------------------------------------------------------------
'
' Este ejemplo ilustra el uso del API WMI Scripting con VB.
' Muestra información de los servicios de un host local o remoto
' y permite un control básico (parar, iniciar, reanudar...)
'
'----------------------------------------------------------------------------

Public Buscador As SWbemLocator
Public Servicios As SWbemServices
Public Cronometro
Public Item As ListItem

Dim WithEvents Depósito As SWbemSink
-----------------------------------------------------------------------------
Public Sub InitialiseView()
ListView1.ListItems.Clear
End Sub
-----------------------------------------------------------------------------
Private Sub Reloj_Timer()

Cronometro = Cronometro + 1
If Cronometro > 10 Then
Item.Bold = False
Reloj.Enabled = False
Cronometro = 0
Else
Item.Bold = Not Item.Bold
End If

End Sub
-----------------------------------------------------------------------------
Private Sub Depósito_OnObjectReady(ByVal Object As WbemScripting.ISWbemObject, ByVal AsyncContext As WbemScripting.ISWbemNamedValueSet)

Dim NombreServicio
Dim Serviciostatus

NombreServicio = Object.TargetInstance.Name
Serviciostatus = Object.TargetInstance.State
Set Item = ListView1.FindItem(NombreServicio)
Item.SubItems(2) = Serviciostatus
Item.Bold = True
Reloj.Enabled = True
Cronometro = 0

End Sub
-----------------------------------------------------------------------------
Public Sub LoadView()

Dim Enumerator As SWbemObjectSet
Dim Object As SWbemObject
Dim Item As ListItem

On Error Resume Next

SavePointer = Form1.MousePointer
Form1.MousePointer = vbHourglass
Form1.Enabled = False
ListView1.ListItems.Clear

Depósito.Cancel

Set Servicios = Buscador.ConnectServer(Server.Text)
Servicios.ExecNotificationQueryAsync Depósito, "Select * from __InstanceModificationEvent Within 2.0 Where TargetInstance Isa 'Win32_Service'"
Set Enumerator = Servicios.ExecQuery("Select * From Win32_Service")

For Each Object In Enumerator

Set Item = ListView1.ListItems.Add(, Object.Name, Object.Name)
Item.SubItems(1) = Object.Description
Item.SubItems(2) = Object.State

Next

Form1.Enabled = True
Form1.MousePointer = SavePointer

End Sub
-----------------------------------------------------------------------------
Private Sub Form_Load()

Set Buscador = New SWbemLocator
Set Depósito = New SWbemSink

InitialiseView
LoadView

End Sub
-----------------------------------------------------------------------------
Private Sub cmdConectar_Click()

LoadView

End Sub
-----------------------------------------------------------------------------
Private Sub cmdPausar_Click()

Dim ObjetoServicio As SWbemObject
Dim NombreServicio

On Error Resume Next
NombreServicio = ListView1.SelectedItem.Text
If Err.Number = 0 Then

Set ObjetoServicio = Servicios.Get("Win32_Service='" & NombreServicio & "'")
'Obsérvese como el método "PauseService" de Win32_Service
'se ejecuta como si fuera método de automatización de SWbemObject
ObjetoServicio.PauseService
End If

End Sub
-----------------------------------------------------------------------------
Private Sub cmdIniciar_Click()

Dim ObjetoServicio As SWbemObject
Dim NombreServicio

On Error Resume Next
NombreServicio = ListView1.SelectedItem.Text
If Err.Number = 0 Then

' Obsérvese cómo el método CIM "StartService" de Win32_Service
' se ejecuta como si fuera un método de automatización de SWbemObject
Set ObjetoServicio = Servicios.Get("Win32_Service='" & NombreServicio & "'")
ObjetoServicio.StartService
End If

End Sub
-----------------------------------------------------------------------------
Private Sub cmdParar_Click()

Dim ObjetoServicio As SWbemObject
Dim NombreServicio

On Error Resume Next
NombreServicio = ListView1.SelectedItem.Text
If Err.Number = 0 Then

Set ObjetoServicio = Servicios.Get("Win32_Service='" & NombreServicio & "'")
ObjetoServicio.StopService
End If

End Sub

 

Esto sólo han sido un par de ejemplos de las posibilidades de WMI para los programadores y para los administradores de sistemas. El horizonte es mucho más amplio que lo mencionado aquí, tanto en el apartado teórico como en el práctico. Recomendamos a los lectores interesados la descarga del SDK completo (unos 8 MB) disponible en la dirección de Internet citada más arriba.

  Código de ejemplo