Administering a Server Cluster

When you are using multiple servers to effectively distribute your content, the task of administration can be greatly simplified by propagating server changes either from one central server or from any one of your servers to all of your other servers. There are many different ways to implement such a configuration. One way to configure multiple servers easily is to create your own custom event notification plug-in to respond to events from and send events to all of your servers at the same time. For more information on how to implement your own custom event notification plug-in, see Creating Event Notification Plug-ins. Another option is to use the WMS WMI Event Handler Plug-in and simply create an application to respond to the events passed on to you from that plug-in.

The following Visual Basic .NET, C#, and C++ examples illustrate how to catch the events passed on from the WMS WMI Event Handler Plug-in and mirror the results on a remote server.

Note

   Windows Media Services must be installed on the remote computer. Also, you must use the DCOMCnfg utility to set the access, launch, and configuration permissions, and identify the users who can administer Windows Media Services. For more information, see Using DCOM to Customize Security Settings.

Visual Basic .NET Example

Imports Microsoft.WindowsMediaServices.Interop
Imports System.Management
Imports System.Runtime.InteropServices

' Add a reference to Microsoft WMI Scripting 1.2 
' Library to import the following namespace.
Imports WbemScripting

' Declare variables.
Dim Server As WMSServer

Dim Services As SWbemServices
Dim strQuery As String

Dim tServerType As Type
Dim RemoteServer As WMSServer

Try

    ' Retrieve the type information from the Windows
    ' Media server running on the remote machine.
    tServerType = Type.GetTypeFromProgID("WMSServer.Server", "remove_server_name")

    ' Create an instance of the remote server object locally.
    RemoteServer = Activator.CreateInstance(tServerType)

    ' Initialize the local server.
    Server = New WMSServer()

    ' Create an event sink.
    EventSink = New SWbemSink()

    ' Connect to the WMI event provider.
    Services = GetObject( _
      "winmgmts:{impersonationLevel=impersonate}!\\" _
      & Server.Name & "\Root\cimv2")

    ' Select all the events for which you want to 
    ' receive notification.   
    strQuery = "select * from WMS_LimitChanged_Event"
    Services.ExecNotificationQueryAsync(EventSink, strQuery)


Catch errCom As COMException
    ' TODO: Handle COM exceptions.
Catch err As Exception
    ' TODO: Exception handler goes here.

Finally
    ' TODO: Clean-up code goes here.
End Try

Private Sub EventSink_OnObjectReady( _
      ByVal objWbemObject As SWbemObject, _
      ByVal objWbemAsyncContext As SWbemNamedValueSet)

    ' This example requires the addition of a reference to 
    ' "Microsoft WMI Scripting V1.2 Library".
    Dim PubPointName
    Dim IsNull

    Try
        ' Catch the WMI event and retrieve its class value
        ' indicating the type of event that occurred.
        If objWbemObject.Path_.Class = "WMS_LimitChanged_Event" Then

            ' Retrieve the name of the publishing
            ' point where the event occurred.
            PubPointName = objWbemObject.Properties_.Item( "PublishingPointName").Value

            ' If the publishing point name is NULL, then the event
            ' occurred at the server level.
            If IsNull(PubPointName) Then
                PropagateServerLimitChange(objWbemObject.Properties_)
            End If
        End If
    Catch errCom As COMException
        ' TODO: Handle COM exceptions.
    Catch err As Exception
        ' TODO: Exception handler goes here.
    Finally
        ' TODO: Clean-up code goes here.
    End Try
End Sub

Private Sub PropagateServerLimitChange(ByRef objWbemProps As SWbemPropertySet)

    ' This example requires the addition of a reference to 
    ' "Microsoft WMI Scripting V1.2 Library".

    Try
        ' Based on the SubEvent type, propagate the limit
        ' change to the remote server.
        Select Case objWbemProps.Item("SubEvent").Value
        Case WMS_LIMIT_CHANGE_EVENT_TYPE.WMS_EVENT_LIMIT_CHANGE_CONNECTION_RATE
            RemoteServer.Limits.ConnectionRate = objWbemProps.Item( _
                "NewValue").Value
        End Select
    Catch errCom As COMException
        ' TODO: Handle COM exceptions.
    Catch err As Exception
        ' TODO: Exception handler goes here.
    Finally
        ' TODO: Clean-up code goes here.
    End Try
End Sub

C# Example

using Microsoft.WindowsMediaServices.Interop;
using System.Management;
using System.Runtime.InteropServices;

WMSServer Server;

// WMI variables.
ConnectionOptions oConn;
ManagementScope oMS;
string strServerName="";

try
{
    // Initialize the local server.
    Server = new WMSServerClass();
    strServerName = Server.Name;

    // Set the WMI connection options.
    oConn = new ConnectionOptions();
    oMS = new System.Management.ManagementScope("\\\\" + 
     strServerName + "\\", oConn);
    
    // Use WMI to retrieve the WMServer service
    // and then stop the service.
    EventQuery eventQuery = new EventQuery("select * from WMS_LimitChanged_Event");

    // Initialize an event watcher object with this query.
    ManagementEventWatcher watcher = new ManagementEventWatcher(oMS, eventQuery);
            
    // Set up a handler for incoming events.
    MyHandler handler = new MyHandler();
    watcher.EventArrived += new EventArrivedEventHandler(handler.OnEvent);
            
    // Start watching for events.
    watcher.Start();
    
}
catch (COMException comExc)
{
    // TODO: Handle COM exceptions.
}
catch (Exception exc)
{
    // TODO: Exception handler goes here.
}
finally
{
    // TODO: Clean-up code goes here.
}

public class MyHandler
{
  private Type tServerType;
  private WMSServer RemoteServer;

  // Handles the event when it arrives.
  public void OnEvent(object sender, EventArrivedEventArgs e) 
  {
    try
    {
      // Retrieve the type information from the Windows
      // Media server running on the remote machine.
      tServerType = Type.GetTypeFromProgID("WMSServer.Server", "server_name");

      // Create an instance of the remote server object locally.
      RemoteServer = (WMSServer)Activator.CreateInstance(tServerType);

      // Catch the WMI event and retrieve its class value
      // indicating the type of event that occurred.
      if (e.NewEvent.ClassPath.Path == "WMS_LimitChanged_Event")
      {
        // If the publishing point is NULL, then the event
        // occurred at the server level.
        if (e.NewEvent.Properties["PublishingPointName"].Value == null)
        {
          // Based on the SubEvent type, propagate the limit change
          // to the remote server.
          UInt32 EventType = (UInt32)e.NewEvent.Properties["SubEvent"].Value;
          if (EventType == (UInt32)WMS_LIMIT_CHANGE_EVENT_TYPE.WMS_EVENT_LIMIT_CHANGE_CONNECTION_RATE)
          {
            int EventValue = (int)e.NewEvent.Properties["NewValue"].Value;
            RemoteServer.Limits.ConnectionRate = EventValue;
           }
        }
      }
    }
    catch (COMException comExc)
    {
        // TODO: Handle COM exceptions.
    }
    catch (Exception exc)
    {
        // TODO: Exception handler goes here.
    }
    finally
    {
        // TODO: Clean-up code goes here.
    }

  }

}

C++ Example

#define _WIN32_DCOM     // Enables DCOM extensions.

#include <windows.h>
#include <conio.h>
#include <wbemcli.h>    // Includes WMI related functions.
#include <atlbase.h>    // Includes CComBSTR and CComVariant.
#include "wmsserver.h"

int main()
{
    // Declare variables and interfaces.
    IWMSServer           *pServer;
    IWMSServer           *pRemoteServer;
    IWbemLocator         *pIWbemLocator;
    IWbemServices        *pIWbemServices;
    IEnumWbemClassObject *pIEnumWbemClassObject;
    IWbemClassObject     *pIWbemClassObject;

    HRESULT         hr;
    CComBSTR        bstrName;
    CComBSTR        bstrNamespace;
    CComBSTR        bstrQueryLanguage;
    CComBSTR        bstrQuery;
    CComBSTR        bstrText;
    CComVariant     varValue;
    ULONG           lCount;

    COSERVERINFO    cs;
    MULTI_QI        mqi;

    // Initialize the COM library and retrieve a pointer
    // to an IWMSServer interface.
    hr = CoInitialize(NULL);
    hr = CoCreateInstance(CLSID_WMSServer,
                          NULL,
                          CLSCTX_ALL,
                          IID_IWMSServer,
                          (void **)&pServer);
    if (FAILED(hr)) goto EXIT;
    
    // Create a COSERVERINFO structure containing information about
    // the computer on which to create the IWMSServer interface.
    ZeroMemory(&cs, sizeof(cs));
    cs.pwszName = L"remote-server-name";
    cs.pAuthInfo = NULL;

    // Create a MULTI_QI structure to hold an IUnknown pointer
    // to an IWMSServer interface.
    ZeroMemory(&mqi, sizeof(mqi));
    mqi.pIID = &IID_IWMSServer;
    mqi.pItf = NULL;
    mqi.hr = 0;

    // Retrieve a pointer to the IWMSServer interface.
    hr = CoCreateInstanceEx(CLSID_WMSServer,
                            NULL,
                            CLSCTX_SERVER,
                            &cs,
                            1,
                            &mqi);
    if (FAILED(hr)) goto EXIT;

    // The MULTI_QI structure contains an IUnknown pointer. Call
    // QueryInterface to retrieve a pointer to IWMSServer.
    hr = mqi.pItf->QueryInterface(IID_IWMSServer,
                                 (void**) &pRemoteServer);
    if (FAILED(hr)) goto EXIT;

    // Create an instance of the WbemLocator interface.
    hr = CoCreateInstance(_uuidof(WbemLocator),
                          NULL,
                          CLSCTX_INPROC_SERVER,
                          _uuidof(IWbemLocator),
                          (LPVOID *) &pIWbemLocator);
    if (FAILED(hr)) goto EXIT;

    // Prepare the namespace for WMI event notification.
    hr = pServer->get_Name(&bstrName);
    if (FAILED(hr)) goto EXIT;
    bstrNamespace = "\\\\";
    bstrNamespace += bstrName;
    bstrNamespace += "\\Root\\cimv2";

    // Connect to the WMI event provider.
    hr = pIWbemLocator->ConnectServer(bstrNamespace,
                                      NULL,
                                      NULL,
                                      0L,
                                      0L,
                                      NULL,
                                      NULL,
                                      &pIWbemServices);
    if (FAILED(hr)) goto EXIT;

    // Select all the events for which you want to 
    // receive notification.
    bstrQueryLanguage = "WQL";
    bstrQuery = "select * from WMS_LimitChanged_Event";
    pIWbemServices->ExecNotificationQuery(bstrQueryLanguage,
                              bstrQuery,
                              WBEM_FLAG_FORWARD_ONLY |
                              WBEM_FLAG_RETURN_IMMEDIATELY,
                              NULL,
                              &pIEnumWbemClassObject);

    // Continuously check for new events and
    // process them as they arrive.
    _flushall();
    do
    {
        // Get the next unprocessed event.
        hr = pIEnumWbemClassObject->Next(WBEM_NO_WAIT,
                                         1,
                                         &pIWbemClassObject,
                                         &lCount);
        if (FAILED(hr)) goto EXIT;

        if (lCount != 0)
        {
            // Retrieve the class name that indicates the event type.
            pIWbemClassObject->Get(L"__CLASS", 0, &varValue, NULL, NULL);
            bstrText = varValue.bstrVal;
            varValue.Clear();

            if (bstrText == "WMS_LimitChanged_Event")
            {
                // Retrieve the name of the publishing 
                // point where the event occurred.
                pIWbemClassObject->Get(L"PublishingPointName", 
                                       0, &varValue, NULL, NULL);

                // If the publishing point name is NULL, then the
                // event occurred at the server level.
                if (varValue.vt == VT_NULL)
                    PropagateServerLimitChange(pIWbemClassObject,
                                               pRemoteServer);
            }
        }

        Sleep(1000);
    } while (!_kbhit());

    // Uninitialize the COM library and return.
    CoUninitialize();
    return(hr);
}

STDMETHODIMP PropagateServerLimitChange(IWbemClassObject
                                        *pIWbemClassObject,
                                        IWMSServer *pRemoteServer)
{
    // Declare variables and interfaces.
    IWMSServerLimits *pLimits;

    CComVariant varValue;
    HRESULT     hr;
    long        lVal;

    // Retrieve the subevent value associated with
    // this limit change event.
    hr = pIWbemClassObject->Get(L"SubEvent", 0, &varValue, NULL, NULL);
    if (FAILED(hr)) goto EXIT;
    lVal = varValue.lVal;
    varValue.Clear();

    // Based on the SubEvent type, propagate the limit
    // change to the remote server.
    switch (lVal)
    {
    case WMS_EVENT_LIMIT_CHANGE_CONNECTION_RATE:
        hr = pIWbemClassObject->Get(L"NewValue", 0, 
                                    &varValue, NULL, NULL);
        if (FAILED(hr)) goto EXIT;
        lVal = varValue.lVal;
        varValue.Clear();

        hr = pRemoteServer->get_Limits(&pLimits);
        if (FAILED(hr)) goto EXIT;

        hr = pLimits->put_ConnectionRate(lVal);
        if (FAILED(hr)) goto EXIT;
        break;
    }
    return(hr);
}

EXIT:
    // TODO: Release temporary COM objects and uninitialize COM.

See Also

Reference

WMS_LIMIT_CHANGE_EVENT_TYPE

Concepts

Creating a Local Server Object

Creating a Remote Server Object

Custom Plug-in Basics

External Events

Internal Events

Programming the Server Object Model

WMS_Limit_Changed_Event Class