Monitoring Change Notifications in ABO
Custom administration applications sometimes need to know when the IIS metabase is updated so that it can log changes, update information, or make other decisions based on changes to specific metabase nodes or properties.
Example Code
The following example shows you how to use the C++ programming language to register an application to receive IIS metabase change notifications and then display change notifications as it receives them.
#define STRICT
#define INITGUID
#include <WINDOWS.H>
#include <WINBASE.H>
#include <OLE2.H>
#include <coguid.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "iadmw.h"
#include "iiscnfg.h"
#include "notify.h"
#include "threadid.h"
HANDLE QuitEvent;
IMSAdminBase * pcAdmCom = NULL;
void __cdecl SignalHandler (int Signal)
{
// Signal the event QuitEvent so that the program will exit
SetEvent (QuitEvent);
}
BOOL bHangOnExit = FALSE;
BOOL bLinger = FALSE;
// default linger time is 5 seconds
#define MD_NOTIFY_DEFAULT_LINGER_TIME 5000
DWORD dwLingerTime = MD_NOTIFY_DEFAULT_LINGER_TIME;
#define MAX(X,Y) X>Y?X:Y
int PrintHelp()
{
printf (
"Usage:\n"
" notify <machine name> [(-a|-f) [(-l|-r) [(-h|-p)]]] \n"
" \n");
return 2;
}
int __cdecl main (int argc, char * argv[])
{
HRESULT hRes;
CImpIMSAdminBaseSink *pEventSink = new CImpIMSAdminBaseSink();
IConnectionPoint* pConnPoint = NULL;
IConnectionPointContainer* pConnPointContainer = NULL;
DWORD dwCookie;
BOOL bSinkConnected = FALSE;
DWORD dwThreadingModel = COINIT_MULTITHREADED;
DWORD dwLocalOrRemote = 0; // 0 is local, 1 is remote
OLECHAR rgchMachineName[MAX_PATH];
IClassFactory * pcsfFactory = NULL;
COSERVERINFO csiMachineName;
COSERVERINFO *pcsiParam = NULL;
// Create an event that will trigger when the program is supposed to exit
QuitEvent = CreateEvent (
NULL,// Security Attributes
TRUE,// Manual reset selected
FALSE,// Initial state - unset
NULL);// Unnamed event
// Set up the signal handler
signal (SIGINT, SignalHandler);
if ((argc < 2) || (argc > 5))
{
PrintHelp ();
return 1;
}
// get the threading model (apartment or free)
if (argc > 2)
{
if (toupper (argv[2] [1]) == 'A')
{
printf ("Initializing Apartment Threaded\n");
dwThreadingModel = COINIT_APARTMENTTHREADED;
}
else
printf ("Initializing Free Threaded\n");
}
if (argc > 3)
{
if (toupper (argv[3] [1]) == 'R')
{
printf ("Initializing Remote (No Security)\n");
dwLocalOrRemote = 1;
}
else
printf ("Initializing Local (Security)\n");
}
if (argc > 4)
{
if ( (!_stricmp(argv[4],"-h")) || (!_stricmp (argv[4], "/h")) )
{
printf ("Hanging Variation\n");
bHangOnExit = TRUE;
}
else if ( (!_strnicmp(argv[4],"-p", strlen ("-p"))) || (!_strnicmp(argv[4],"/p", strlen ("/p"))) )
{
if (strlen (argv[4]) > 3)
{
DWORD dwTempLingerValue = atol (&(argv[4][3]));
dwLingerTime = dwTempLingerValue * 1000;
}
printf ("Lingering Variation: %u\n", dwLingerTime);
bLinger = TRUE;
}
else
printf ("No Hanging Variation\n");
}
printf ("Parameter 1: %s\n", argv[1]);
pcsiParam = &csiMachineName;
for (int i = 0; argv [1][i] != '\0'; i++)
rgchMachineName[i] = (OLECHAR) (argv[1][i]);
rgchMachineName[i] = 0;
//fill the structure for CoGetClassObject
csiMachineName.pwszName = rgchMachineName;
csiMachineName.pAuthInfo = NULL;
csiMachineName.dwReserved1 = 0;
csiMachineName.dwReserved2 = 0;
pcsiParam = &csiMachineName;
hRes = CoInitializeEx(NULL, dwThreadingModel);
if (FAILED(hRes))
{
printf("CoInitializeEx Failed: %d (%#x)\n", hRes, hRes);
return 1;
}
hRes = CoInitializeSecurity(
NULL,
-1,
NULL,
NULL,
RPC_C_AUTHN_LEVEL_NONE,
//0,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE,
0);
if (FAILED(hRes))
{
printf("CoInitializeSecurity Failed. Error: %u (%#x)\n", hRes, hRes);
return 1;
}
hRes = CoGetClassObject(GETAdminBaseCLSID(TRUE), CLSCTX_SERVER, pcsiParam,
IID_IClassFactory, (void**) &pCsfFactory);
if (FAILED(hRes))
{
puts ("ERROR: CoGetClassObject Failed(probably ADMCOM not registered locally)\n");
printf ("\tError Code: %#x\n", hRes);
return 1;
}
else
{
hRes = pcsfFactory->CreateInstance(NULL, IID_IMSAdminBase, (void **) &pCAdmCom);
if (FAILED(hRes))
{
printf ("ERROR: CreateInstance Failed! Return Code: %#x", hRes);
pcsfFactory->Release();
return 1;
}
}
// First query the object for its Connection Point Container. This
// essentially asks the object in the server if it is connectable.
hRes = pcAdmCom->QueryInterface(
IID_IConnectionPointContainer,
(PVOID *)&pConnPointContainer);
if (SUCCEEDED(hRes))
{
// Find the requested Connection Point. This AddRef's the
// returned pointer.
hRes = pConnPointContainer->FindConnectionPoint(IID_IMSAdminBaseSink, &pConnPoint);
if (SUCCEEDED(hRes))
{
hRes = pConnPoint->Advise((IUnknown *)pEventSink, &dwCookie);
printf ("Sink Connect Result: %#x\n", hRes);
if (SUCCEEDED (hRes))
bSinkConnected = TRUE;
}
else
{
printf("Failed on calling IConnectionPointContainer::FindConnectionPoint. Error %#x\n", hRes);
}
// RELEASE_INTERFACE(pConnPointContainer);
pConnPointContainer->Release();
}
else
{
printf("Failed to obtain IConnectionPointContainer. Error %#x\n", hRes);
printf("Trying to obtain IConnectionPointContainer through casting ...\n");
pConnPointContainer = (IConnectionPointContainer *)pcAdmCom;
// Find the requested Connection Point. This AddRef's the
// returned pointer.
hRes = pConnPointContainer->FindConnectionPoint(IID_IMSAdminBaseSink, &pConnPoint);
if (SUCCEEDED(hRes))
{
hRes = pConnPoint->Advise((IUnknown *)pEventSink, &dwCookie);
printf ("Sink Connect Result: %#x\n", hRes);
if (SUCCEEDED (hRes))
bSinkConnected = TRUE;
}
else
{
printf("Failed on calling IConnectionPointContainer::FindConnectionPoint. Error %#x\n", hRes);
}
pConnPointContainer->Release();
}
printf("\n****************************\n");
if (bSinkConnected)
{
DWORD dwRefCount = 0;
WaitForSingleObject (QuitEvent, INFINITE);
// Pause to allow the other threads to shut down
fflush (stdout);
printf ("Pausing to allow sink threads to quit\n");
Sleep (20000);
hRes = pConnPoint->Unadvise(dwCookie);
printf("\n");
dwRefCount = pConnPoint->Release ();
printf ("pConnPoint Ref Count after Release: %d\n", dwRefCount);
dwRefCount = pcAdmCom->Release();
printf ("pcAdmCom Ref Count after Release: %d\n", dwRefCount);
}
else
puts ("Not Connected to Sink!\n");
return ERROR_SUCCESS;
}