Using ISAPI to Monitor MonitorService

MonitorService knows quite a bit more about what is going on within the program than it actually logs to a file. Much of this information is state information that might not even make sense out of context. Simply knowing that one or another thread of the program is working can be at the very least interesting. And when you're debugging a multithreaded application, understanding the interactions between threads can be critical to understanding failures.

CMonitorService Modifications

To implement the ability to get real-time trace information, I have added several data members and methods to the base class for CMonitorService , CPPService . These members and methods are

  • CRITICAL_SECTION m_csTrace. A critical section object used to control access by multiple threads to the trace- related variables .
  • BOOL m_debugging. Set when the -D flag is passed on the command line. This causes trace messages to be immediately printed to the console.
  • LONG m_traceHead. Used to point to the logical start of messages within an array of messages used in round- robin fashion.
  • LONG m_traceTail. Used to point to the logical end of messages in conjunction with m_traceHead .
  • char * m_traces[]. Holds the pointers to the actual trace messages.
  • DWORD Trace(LPCSTR traceStr). A virtual method that places trace messages into the m_traces array.

You'll find the complete source for CPPService.h and CPPService.cpp on the companion CD-ROM. The Trace method is at the core of the changes to CPPService . Trace is shown in Listing 15-11.

Listing 15-11

CPPService.cpp

 DWORDCPPService::Trace(LPCSTRtraceStr) { charszTs[255]; structtm*time_now; time_tsecs_now; time(&secs_now); time_now=localtime(&secs_now); strftime(szTs,30, "%Y-%m-%d%H:%M", time_now); EnterCriticalSection(&m_csTrace); //Loopback,sowekeep100inmemory if(m_traceTail==MAX_TRACES) { m_traceTail=0; m_traceHead=1; } if(m_traces[m_traceTail]!=0) { delete[]m_traces[m_traceTail]; } m_traces[m_traceTail]=newchar[strlen(traceStr)+ strlen(szTs)+10]; //Tracestringcontainstimestampfollowed //bythemessagetext. strcpy(m_traces[m_traceTail],szTs); strcat(m_traces[m_traceTail],":"); strcat(m_traces[m_traceTail],traceStr); if(m_debugging) { printf("\n%s",m_traces[m_traceTail]); } if(m_traceHead>m_traceTail) { m_traceHead++; if(m_traceHead==MAX_TRACES) { m_traceHead=0; } } m_traceTail++; LeaveCriticalSection(&m_csTrace); return((DWORD)m_traceHead); } 

The job of Trace is to place the string passed as traceStr into the m_traces pointer array. Before managing the m_traceHead , m_traceTail , and m_traces variables, the function enters a critical section object, m_csTrace . In classes derived from CPPService , other threads will manipulate the m_traceHead , m_traceTail , and m_traces variables, so we must serialize access to these variables. The critical section object is initialized and deleted in the constructor and destructor, respectively. The first and last messages are pointed to by m_traceHead and m_traceTail , respectively. These pointers are manipulated so that the most recent MAX_TRACES messages in the array are always available. These traces could have also been placed in a SQL Server table; however, making the trace messages available in this way presents a more interesting example. Finally, if the service is being debugged and run as a console mode application rather than as a service, the message is displayed on the console using printf .

I made changes to CMonitorService in two areas. First, the trace statements must be placed in the appropriate functions of the service. You'll find the complete code for the modified CMonitorService on the companion CD-ROM, and you'll find calls to the new Trace method of CPPService in DoTask and MonitorAlerts . The second area changed in CMonitorService is DoWork , shown in Listing 15-12.

Listing 15-12

MonitorService.cpp

 voidCMonitorService::DoWork(SOCKETs,WORDThreadNum) { chardiag[255]; chardata[2048]; BOOLfSuccess; intlen; staticintblksSent=0; intbytesRead; intmaxData=512; intnumSent=0; boolfinalSent=false; bytesRead=0L; memset((void*)data,' 
 void  CMonitorService::DoWork(SOCKET  s,  WORD  ThreadNum) {         char  diag[255];         char  data[2048];         BOOL  fSuccess;         int  len;         static  int  blksSent  =  0;         int  bytesRead;         int  maxData  =  512;         int  numSent  =  0;         bool  finalSent  =  false;         bytesRead  =  0L; memset ((void  *)data,  '\0',  1024);         do  {                 if  ((len  =  recv(s,                         ((char  *)data),                         maxData,  0))  ==  SOCKET_ERROR)                 {                         fSuccess  =  FALSE;                         wsprintf(diag,  "SOCKET  ERROR",  data);                         LogEvent(EVENTLOG_ERROR_TYPE,                                 EVMSG_DEBUG,  diag);                 }                 else                 {                         EnterCriticalSection(&m_csTrace);                         long  curPacket  =  m_traceHead;                         while  (len  !=  SOCKET_ERROR  &&  IsRunning())                         {                                 DATA_PACKET  *d;                                 d  =  (DATA_PACKET  *)&data[0];                                 switch  (d->ServiceCode)                                 {                                 case  REQ_FIRST_TRACE:                                         curPacket  =  m_traceHead+1;                                         numSent  =  0; //                                        break;    //  Step  through  this.                                 case  REQ_NEXT_TRACE:                                         if  (curPacket  >=  MAX_TRACES)                                         {                                                 curPacket  =  0;                                         }                                         if  (m_traces[curPacket]  ==  0                                                   curPacket  ==  (m_traceTail)                                                   (curPacket  ==  MAX_TRACES-1  &&                                                 m_traceHead  ==  0)  &&                                                 numSent  <  MAX_TRACES)                                         {                                                 //  Send  a  signal  that  we  are  done.                                                 d->DataLen  =  0;                                                 len  =  send(s,  (const  char  *)d,                                                         (14+d->DataLen)+1,  0);                                                 finalSent  =  true;                                         }                                         else                                         {                                                 d->DataLen  =  strlen(m_traces[curPacket])+1; strncpy (d->Buffer,                                                         m_traces[curPacket],  d->BufLen);                                                 d->Buffer[d->DataLen+1]  =  '\0';                                                 len  =  send(s,  (const  char  *)d,                                                         (14+d->DataLen)+1,  0);                                                 curPacket++;                                                 numSent++;                                         }                                         break;                                 default:                                         d->DataLen  =  0;                                         break;                                 }                                 if  (!finalSent  &&  len  !=  SOCKET_ERROR)                                 {                                         //  Get  the next message  from  the  client.                                         do  {                                                 len  =  recv(s,                                                         ((char  *)data),                                                         maxData,  0);                                                 if  (IsRunning()  &&  len  ==  0)                                                 {                                                         Sleep(100);                                                 }                                         }  while  (len  ==  0  &&  IsRunning());                                 }                                 else                                 {                                         len  =  SOCKET_ERROR;                                 }                         }                         LeaveCriticalSection(&m_csTrace);                 }         }  while  (!(finalSent)  &&  IsRunning()  &&                 len  !=  SOCKET_ERROR  &&  len  !=  0); closesocket (s); } 
',1024); do{ if((len=recv(s, ((char*)data), maxData,0))==SOCKET_ERROR) { fSuccess=FALSE; wsprintf(diag,"SOCKETERROR",data); LogEvent(EVENTLOG_ERROR_TYPE, EVMSG_DEBUG,diag); } else { EnterCriticalSection(&m_csTrace); longcurPacket=m_traceHead; while(len!=SOCKET_ERROR&&IsRunning()) { DATA_PACKET*d; d=(DATA_PACKET*)&data[0]; switch(d->ServiceCode) { caseREQ_FIRST_TRACE: curPacket=m_traceHead+1; numSent=0; //break;//Stepthroughthis. caseREQ_NEXT_TRACE: if(curPacket>=MAX_TRACES) { curPacket=0; } if(m_traces[curPacket]==0 curPacket==(m_traceTail) (curPacket==MAX_TRACES-1&& m_traceHead==0)&& numSent<MAX_TRACES) { //Sendasignalthatwearedone. d->DataLen=0; len=send(s,(constchar*)d, (14+d->DataLen)+1,0); finalSent=true; } else { d->DataLen=strlen(m_traces[curPacket])+1; strncpy(d->Buffer, m_traces[curPacket],d->BufLen); d->Buffer[d->DataLen+1]='
 void  CMonitorService::DoWork(SOCKET  s,  WORD  ThreadNum) {         char  diag[255];         char  data[2048];         BOOL  fSuccess;         int  len;         static  int  blksSent  =  0;         int  bytesRead;         int  maxData  =  512;         int  numSent  =  0;         bool  finalSent  =  false;         bytesRead  =  0L; memset ((void  *)data,  '\0',  1024);         do  {                 if  ((len  =  recv(s,                         ((char  *)data),                         maxData,  0))  ==  SOCKET_ERROR)                 {                         fSuccess  =  FALSE;                         wsprintf(diag,  "SOCKET  ERROR",  data);                         LogEvent(EVENTLOG_ERROR_TYPE,                                 EVMSG_DEBUG,  diag);                 }                 else                 {                         EnterCriticalSection(&m_csTrace);                         long  curPacket  =  m_traceHead;                         while  (len  !=  SOCKET_ERROR  &&  IsRunning())                         {                                 DATA_PACKET  *d;                                 d  =  (DATA_PACKET  *)&data[0];                                 switch  (d->ServiceCode)                                 {                                 case  REQ_FIRST_TRACE:                                         curPacket  =  m_traceHead+1;                                         numSent  =  0; //                                        break;    //  Step  through  this.                                 case  REQ_NEXT_TRACE:                                         if  (curPacket  >=  MAX_TRACES)                                         {                                                 curPacket  =  0;                                         }                                         if  (m_traces[curPacket]  ==  0                                                   curPacket  ==  (m_traceTail)                                                   (curPacket  ==  MAX_TRACES-1  &&                                                 m_traceHead  ==  0)  &&                                                 numSent  <  MAX_TRACES)                                         {                                                 //  Send  a  signal  that  we  are  done.                                                 d->DataLen  =  0;                                                 len  =  send(s,  (const  char  *)d,                                                         (14+d->DataLen)+1,  0);                                                 finalSent  =  true;                                         }                                         else                                         {                                                 d->DataLen  =  strlen(m_traces[curPacket])+1; strncpy (d->Buffer,                                                         m_traces[curPacket],  d->BufLen);                                                 d->Buffer[d->DataLen+1]  =  '\0';                                                 len  =  send(s,  (const  char  *)d,                                                         (14+d->DataLen)+1,  0);                                                 curPacket++;                                                 numSent++;                                         }                                         break;                                 default:                                         d->DataLen  =  0;                                         break;                                 }                                 if  (!finalSent  &&  len  !=  SOCKET_ERROR)                                 {                                         //  Get  the next message  from  the  client.                                         do  {                                                 len  =  recv(s,                                                         ((char  *)data),                                                         maxData,  0);                                                 if  (IsRunning()  &&  len  ==  0)                                                 {                                                         Sleep(100);                                                 }                                         }  while  (len  ==  0  &&  IsRunning());                                 }                                 else                                 {                                         len  =  SOCKET_ERROR;                                 }                         }                         LeaveCriticalSection(&m_csTrace);                 }         }  while  (!(finalSent)  &&  IsRunning()  &&                 len  !=  SOCKET_ERROR  &&  len  !=  0); closesocket (s); } 
'; len=send(s,(constchar*)d, (14+d->DataLen)+1,0); curPacket++; numSent++; } break; default: d->DataLen=0; break; } if(!finalSent&&len!=SOCKET_ERROR) { //Getthenextmessagefromtheclient. do{ len=recv(s, ((char*)data), maxData,0); if(IsRunning()&&len==0) { Sleep(100); } }while(len==0&&IsRunning()); } else { len=SOCKET_ERROR; } } LeaveCriticalSection(&m_csTrace); } }while(!(finalSent)&&IsRunning()&& len!=SOCKET_ERROR&&len!=0); closesocket(s); }

DoWork first tries to receive a package on the socket passed as the first parameter. Recall that this is a socket that has been retrieved by WSCommDispatcher and Thread , first described in the CommService example in Chapter 12. The buffer received should be a DATA_PACKET structure, also first described in Chapter 12. DATA_PACKET is shown here:

 typedefstruct _DATA_PACKET {     WORD ServerHandle;     WORD ServiceCode;     WORD ReturnCode;     DWORD DataLen;     DWORD BufLen;     char Buffer[MAX_DATA_SIZE]; } DATA_PACKET; 

ServiceCode can be set to one of two values, REQ_FIRST_TRACE or REQ_NEXT_TRACE , requesting the first trace message or the next trace message, respectively. ServiceCode controls a switch statement. If the first trace message is requested , I set the local variable curPacket to m_traceHead and pass through to the second case statement (note the commented-out break after the REQ_FIRST_TRACE case). Depending on whether there is another message to send, I either send the message ”setting DataLen to the length of the message ”or I send a DATA_PACKET with DataLen set to 0. If there is no message to send, the local Boolean variable finalSent is set to true . After the switch statement, I try to receive another request from the client, breaking out only if recv returns SOCKET_ERROR or the IsRunning method of CPPService returns false. Finally, after all messages are sent, I leave the m_csTrace critical section and close the socket.

Look at the code in Listing 15-12. You can see that every loop is controlled by the return value of IsRunning , in addition to whatever other logic controls the loop. For instance, rather than just testing len to see if a socket error has occurred, the fragment below checks len as well as the return value of IsRunning.

 while(len!=SOCKET_ERROR&&IsRunning()) { //Continuelooping 

This makes for a somewhat more complicated function, but it is essential to ensure that the function will return to DoWork whenever the service is stopped so that the thread running DoWork can terminate normally.

The ISAPIMonitor ISAPI Extension

Once the service is running and tracing its operation, we need to get at the information. An ideal way to do this is from the Web. Look back at the Monitor Trace link in Figure 15-1. Clicking on this link leads to the screen shown in Figure 15-6. The screen displays all of the trace statements currently cached in the service.

click to view at full size.

Figure 15-6 The trace statements screen.

The information in this screen can be generated in a couple of ways. A server-side ActiveX control can generate the table information for an ASP page, using TCP/IP to communicate to CMonitorService . An alternative is to use an ISAPI extension. For this example, I will use ISAPI to create the entire page. Listing 15-13 shows ISAPIMonitor, the ISAPI extension I created to display the monitor trace information.

Listing 15-13

ISAPIMonitor.cpp

 //ISAPIMONITOR.CPP-ImplementationfileMonitorTrace //display #include"stdafx.h" #include"ISAPIMonitor.h" ///////////////////////////////////////////////////////////////////// //TheoneandonlyCWinAppobject //NOTE:Youcanremovethisobjectifyoualteryourprojecttono //longeruseMFCinaDLL. //CWinApptheApp; ///////////////////////////////////////////////////////////////////// //command-parsingmap BEGIN_PARSE_MAP(CISAPIMonitorExtension,CHttpServer) //TODO:insertyourON_PARSE_COMMAND()and //ON_PARSE_COMMAND_PARAMS()heretohookupyourcommands. //Forexample: ON_PARSE_COMMAND(Default,CISAPIMonitorExtension,ITS_EMPTY) DEFAULT_PARSE_COMMAND(Default,CISAPIMonitorExtension) END_PARSE_MAP(CISAPIMonitorExtension) ///////////////////////////////////////////////////////////////////// //TheoneandonlyCISAPIMonitorExtensionobject CISAPIMonitorExtensiontheExtension; ///////////////////////////////////////////////////////////////////// //CISAPIMonitorExtensionimplementation CISAPIMonitorExtension::CISAPIMonitorExtension() { } CISAPIMonitorExtension::~CISAPIMonitorExtension() { } BOOLCISAPIMonitorExtension::GetExtensionVersion(HSE_VERSION_INFO*pVer) { //Calldefaultimplementationforinitialization. CHttpServer::GetExtensionVersion(pVer); //Loaddescriptionstring. TCHARsz[HSE_MAX_EXT_DLL_NAME_LEN+1]; ISAPIVERIFY(::LoadString(AfxGetResourceHandle(), IDS_SERVER,sz,HSE_MAX_EXT_DLL_NAME_LEN)); _tcscpy(pVer->lpszExtensionDesc,sz); returnTRUE; } BOOLCISAPIMonitorExtension::TerminateExtension(DWORDdwFlags) { //Extensionisbeingterminated //TODO:Cleanupanyper-instanceresources. returnTRUE; } ///////////////////////////////////////////////////////////////////// //CISAPIMonitorExtensioncommandhandlers voidCISAPIMonitorExtension::Default(CHttpServerContext*pCtxt) { DWORDdwBytes=0; DATA_PACKETd; StartContent(pCtxt); WriteTitle(pCtxt); memset((char*)&d,' 
 //  ISAPIMONITOR.CPP  -  Implementation  file  Monitor  Trace //    display #include  "stdafx.h" #include  "ISAPIMonitor.h" ///////////////////////////////////////////////////////////////////// //  The  one  and  only  CWinApp  object //  NOTE:  You  can  remove  this  object  if  you  alter  your  project  to  no //    longer  use  MFC  in  a  DLL. //CWinApp  theApp; ///////////////////////////////////////////////////////////////////// //  command-parsing  map BEGIN_PARSE_MAP(CISAPIMonitorExtension,  CHttpServer)         //  TODO:  insert  your  ON_PARSE_COMMAND()  and         //    ON_PARSE_COMMAND_PARAMS()  here  to  hook  up  your  commands.         //  For  example:         ON_PARSE_COMMAND(Default,  CISAPIMonitorExtension,  ITS_EMPTY)         DEFAULT_PARSE_COMMAND(Default,  CISAPIMonitorExtension) END_PARSE_MAP(CISAPIMonitorExtension) ///////////////////////////////////////////////////////////////////// //  The  one  and  only  CISAPIMonitorExtension  object CISAPIMonitorExtension  theExtension; ///////////////////////////////////////////////////////////////////// //  CISAPIMonitorExtension  implementation CISAPIMonitorExtension::CISAPIMonitorExtension() { } CISAPIMonitorExtension::~CISAPIMonitorExtension() { } BOOL  CISAPIMonitorExtension::GetExtensionVersion(HSE_VERSION_INFO*  pVer) {         //  Call  default  implementation  for  initialization.         CHttpServer::GetExtensionVersion(pVer);         //  Load  description  string.         TCHAR  sz[HSE_MAX_EXT_DLL_NAME_LEN+1];         ISAPIVERIFY(::LoadString(AfxGetResourceHandle(),                 IDS_SERVER,  sz,  HSE_MAX_EXT_DLL_NAME_LEN));         _tcscpy(pVer->lpszExtensionDesc,  sz);         return  TRUE; } BOOL  CISAPIMonitorExtension::TerminateExtension(DWORD  dwFlags) {         //  Extension  is  being terminated //  TODO:  Clean  up  any  per-instance  resources.         return  TRUE; } ///////////////////////////////////////////////////////////////////// //  CISAPIMonitorExtension  command  handlers void  CISAPIMonitorExtension::Default(CHttpServerContext*  pCtxt) {         DWORD  dwBytes  =  0;         DATA_PACKET  d;         StartContent(pCtxt);         WriteTitle(pCtxt);         memset((char  *)&d,  '\0',  sizeof(DATA_PACKET));         //  Of  course,  this  should  be  a  parameter,  a         //    registry  entry,  or  at  least  modified         //    in  the  source  for  your  environment.         CWinSockClientComm  MyClient("DUAL",  5043);         if  (MyClient.Open()  ==  -1)         {                 *pCtxt  <<  _T("<CENTER><H2>Sorry.  Can't  connect  to  ");                 *pCtxt  <<  _T("CMonitorService<CENTER></H2>");                 return;         }         *pCtxt  <<  _T("<CENTER><H2>Trace  Statements  ");         *pCtxt  <<  _T("from  <I>MonitorService</I></H2><BR>");         *pCtxt  <<                  _T("<TABLE  WIDTH=85%  BORDER=1  CELLSPACING=1 CELLPADDING =1>");         *pCtxt  <<  _T("<TR><TD  BGCOLOR=AQUA>  ");         *pCtxt  <<  _T("Trace  Information  </TR></TD>  ");         //  Initialize  these.         d.ServiceCode  =  REQ_FIRST_TRACE;         d.DataLen  =  16;         do  {                 d.BufLen  =  1024;                 MyClient.Write((char  *)&d,  sizeof(d),  &dwBytes);                 //  Initialize  this.                 d.DataLen  =  0;                 memset(d.Buffer,  '\0',  sizeof(d.Buffer));                 MyClient.Read((char  *)&d,  sizeof(d),  &dwBytes);                   if  (d.DataLen)                 {                         *pCtxt  <<  _T("<TR><TD>  ");                         *pCtxt  <<  _T(d.Buffer);                         *pCtxt  <<  _T("</TD></TR>");                         d.ServiceCode  =  REQ_NEXT_TRACE;                         d.DataLen  =  16;                 }         }  while  (d.DataLen  &&  dwBytes);         MyClient.Close();         *pCtxt  <<  _T("</TABLE>  ");         *pCtxt  <<  _T("<FORM  action=\"Default.asp\"  ");         *pCtxt  <<  _T("method=POST  id=form1  name=form1>");         *pCtxt  <<  _T("<INPUT  type=\"submit\"  ");         *pCtxt  <<  _T("value=\"Return  to  Alert  List\"  ");         *pCtxt  <<  _T("  id=submit1 name =submit1>");         *pCtxt  <<  _T("</FORM>");         EndContent(pCtxt); } //  Do  not  edit  the  following  lines,  which  are  needed  by  ClassWizard. #if  0 BEGIN_MESSAGE_MAP(CISAPIMonitorExtension,  CHttpServer)         //{{AFX_MSG_MAP(CISAPIMonitorExtension)         //}}AFX_MSG_MAP END_MESSAGE_MAP() #endif        //  0 ///////////////////////////////////////////////////////////////////// //  If  your  extension  will  not  use  MFC,  you'll  need  this  code  to  make //    sure  the  extension  objects  can  find  the  resource  handle  for  the //    module.  If  you  convert  your  extension  to  not  be  dependent  on  MFC, //    remove  the  comments  around  the  following  AfxGetResourceHandle() //    and  DllMain()  functions,  as  well  as  the  g_hInstance  global. /****/ static  HINSTANCE  g_hInstance; HINSTANCE  AFXISAPI  AfxGetResourceHandle() {         return  g_hInstance; } BOOL  WINAPI  DllMain(HINSTANCE  hInst,  ULONG  ulReason,         LPVOID  lpReserved) {         if  (ulReason  ==  DLL_PROCESS_ATTACH)         {                 g_hInstance  =  hInst;         }         return  TRUE; } /****/ 
',sizeof(DATA_PACKET)); //Ofcourse,thisshouldbeaparameter,a //registryentry,oratleastmodified //inthesourceforyourenvironment. CWinSockClientCommMyClient("DUAL",5043); if(MyClient.Open()==-1) { *pCtxt<<_T("<CENTER><H2>Sorry.Can'tconnectto"); *pCtxt<<_T("CMonitorService<CENTER></H2>"); return; } *pCtxt<<_T("<CENTER><H2>TraceStatements"); *pCtxt<<_T("from<I>MonitorService</I></H2><BR>"); *pCtxt<< _T("<TABLEWIDTH=85%BORDER=1CELLSPACING=1CELLPADDING=1>"); *pCtxt<<_T("<TR><TDBGCOLOR=AQUA>"); *pCtxt<<_T("TraceInformation</TR></TD>"); //Initializethese. d.ServiceCode=REQ_FIRST_TRACE; d.DataLen=16; do{ d.BufLen=1024; MyClient.Write((char*)&d,sizeof(d),&dwBytes); //Initializethis. d.DataLen=0; memset(d.Buffer,'
 //  ISAPIMONITOR.CPP  -  Implementation  file  Monitor  Trace //    display #include  "stdafx.h" #include  "ISAPIMonitor.h" ///////////////////////////////////////////////////////////////////// //  The  one  and  only  CWinApp  object //  NOTE:  You  can  remove  this  object  if  you  alter  your  project  to  no //    longer  use  MFC  in  a  DLL. //CWinApp  theApp; ///////////////////////////////////////////////////////////////////// //  command-parsing  map BEGIN_PARSE_MAP(CISAPIMonitorExtension,  CHttpServer)         //  TODO:  insert  your  ON_PARSE_COMMAND()  and         //    ON_PARSE_COMMAND_PARAMS()  here  to  hook  up  your  commands.         //  For  example:         ON_PARSE_COMMAND(Default,  CISAPIMonitorExtension,  ITS_EMPTY)         DEFAULT_PARSE_COMMAND(Default,  CISAPIMonitorExtension) END_PARSE_MAP(CISAPIMonitorExtension) ///////////////////////////////////////////////////////////////////// //  The  one  and  only  CISAPIMonitorExtension  object CISAPIMonitorExtension  theExtension; ///////////////////////////////////////////////////////////////////// //  CISAPIMonitorExtension  implementation CISAPIMonitorExtension::CISAPIMonitorExtension() { } CISAPIMonitorExtension::~CISAPIMonitorExtension() { } BOOL  CISAPIMonitorExtension::GetExtensionVersion(HSE_VERSION_INFO*  pVer) {         //  Call  default  implementation  for  initialization.         CHttpServer::GetExtensionVersion(pVer);         //  Load  description  string.         TCHAR  sz[HSE_MAX_EXT_DLL_NAME_LEN+1];         ISAPIVERIFY(::LoadString(AfxGetResourceHandle(),                 IDS_SERVER,  sz,  HSE_MAX_EXT_DLL_NAME_LEN));         _tcscpy(pVer->lpszExtensionDesc,  sz);         return  TRUE; } BOOL  CISAPIMonitorExtension::TerminateExtension(DWORD  dwFlags) {         //  Extension  is  being terminated //  TODO:  Clean  up  any  per-instance  resources.         return  TRUE; } ///////////////////////////////////////////////////////////////////// //  CISAPIMonitorExtension  command  handlers void  CISAPIMonitorExtension::Default(CHttpServerContext*  pCtxt) {         DWORD  dwBytes  =  0;         DATA_PACKET  d;         StartContent(pCtxt);         WriteTitle(pCtxt);         memset((char  *)&d,  '\0',  sizeof(DATA_PACKET));         //  Of  course,  this  should  be  a  parameter,  a         //    registry  entry,  or  at  least  modified         //    in  the  source  for  your  environment.         CWinSockClientComm  MyClient("DUAL",  5043);         if  (MyClient.Open()  ==  -1)         {                 *pCtxt  <<  _T("<CENTER><H2>Sorry.  Can't  connect  to  ");                 *pCtxt  <<  _T("CMonitorService<CENTER></H2>");                 return;         }         *pCtxt  <<  _T("<CENTER><H2>Trace  Statements  ");         *pCtxt  <<  _T("from  <I>MonitorService</I></H2><BR>");         *pCtxt  <<                  _T("<TABLE  WIDTH=85%  BORDER=1  CELLSPACING=1 CELLPADDING =1>");         *pCtxt  <<  _T("<TR><TD  BGCOLOR=AQUA>  ");         *pCtxt  <<  _T("Trace  Information  </TR></TD>  ");         //  Initialize  these.         d.ServiceCode  =  REQ_FIRST_TRACE;         d.DataLen  =  16;         do  {                 d.BufLen  =  1024;                 MyClient.Write((char  *)&d,  sizeof(d),  &dwBytes);                 //  Initialize  this.                 d.DataLen  =  0;                 memset(d.Buffer,  '\0',  sizeof(d.Buffer));                 MyClient.Read((char  *)&d,  sizeof(d),  &dwBytes);                   if  (d.DataLen)                 {                         *pCtxt  <<  _T("<TR><TD>  ");                         *pCtxt  <<  _T(d.Buffer);                         *pCtxt  <<  _T("</TD></TR>");                         d.ServiceCode  =  REQ_NEXT_TRACE;                         d.DataLen  =  16;                 }         }  while  (d.DataLen  &&  dwBytes);         MyClient.Close();         *pCtxt  <<  _T("</TABLE>  ");         *pCtxt  <<  _T("<FORM  action=\"Default.asp\"  ");         *pCtxt  <<  _T("method=POST  id=form1  name=form1>");         *pCtxt  <<  _T("<INPUT  type=\"submit\"  ");         *pCtxt  <<  _T("value=\"Return  to  Alert  List\"  ");         *pCtxt  <<  _T("  id=submit1 name =submit1>");         *pCtxt  <<  _T("</FORM>");         EndContent(pCtxt); } //  Do  not  edit  the  following  lines,  which  are  needed  by  ClassWizard. #if  0 BEGIN_MESSAGE_MAP(CISAPIMonitorExtension,  CHttpServer)         //{{AFX_MSG_MAP(CISAPIMonitorExtension)         //}}AFX_MSG_MAP END_MESSAGE_MAP() #endif        //  0 ///////////////////////////////////////////////////////////////////// //  If  your  extension  will  not  use  MFC,  you'll  need  this  code  to  make //    sure  the  extension  objects  can  find  the  resource  handle  for  the //    module.  If  you  convert  your  extension  to  not  be  dependent  on  MFC, //    remove  the  comments  around  the  following  AfxGetResourceHandle() //    and  DllMain()  functions,  as  well  as  the  g_hInstance  global. /****/ static  HINSTANCE  g_hInstance; HINSTANCE  AFXISAPI  AfxGetResourceHandle() {         return  g_hInstance; } BOOL  WINAPI  DllMain(HINSTANCE  hInst,  ULONG  ulReason,         LPVOID  lpReserved) {         if  (ulReason  ==  DLL_PROCESS_ATTACH)         {                 g_hInstance  =  hInst;         }         return  TRUE; } /****/ 
',sizeof(d.Buffer)); MyClient.Read((char*)&d,sizeof(d),&dwBytes); if(d.DataLen) { *pCtxt<<_T("<TR><TD>"); *pCtxt<<_T(d.Buffer); *pCtxt<<_T("</TD></TR>"); d.ServiceCode=REQ_NEXT_TRACE; d.DataLen=16; } }while(d.DataLen&&dwBytes); MyClient.Close(); *pCtxt<<_T("</TABLE>"); *pCtxt<<_T("<FORMaction=\"Default.asp\""); *pCtxt<<_T("method=POSTid=form1name=form1>"); *pCtxt<<_T("<INPUTtype=\"submit\""); *pCtxt<<_T("value=\"ReturntoAlertList\""); *pCtxt<<_T("id=submit1name=submit1>"); *pCtxt<<_T("</FORM>"); EndContent(pCtxt); } //Donoteditthefollowinglines,whichareneededbyClassWizard. #if0 BEGIN_MESSAGE_MAP(CISAPIMonitorExtension,CHttpServer) //{{AFX_MSG_MAP(CISAPIMonitorExtension) //}}AFX_MSG_MAP END_MESSAGE_MAP() #endif//0 ///////////////////////////////////////////////////////////////////// //IfyourextensionwillnotuseMFC,you'llneedthiscodetomake //suretheextensionobjectscanfindtheresourcehandleforthe //module.IfyouconvertyourextensiontonotbedependentonMFC, //removethecommentsaroundthefollowingAfxGetResourceHandle() //andDllMain()functions,aswellastheg_hInstanceglobal. /****/ staticHINSTANCEg_hInstance; HINSTANCEAFXISAPIAfxGetResourceHandle() { returng_hInstance; } BOOLWINAPIDllMain(HINSTANCEhInst,ULONGulReason, LPVOIDlpReserved) { if(ulReason==DLL_PROCESS_ATTACH) { g_hInstance=hInst; } returnTRUE; } /****/

The majority of Listing 15-13 is the Wizard-generated code. I have removed the reliance on MFC classes outside the MFC ISAPI classes, following the instructions in the source code as well as the extra steps described in Chapter 10.

The Default method is used to generate the HTML code required to display the monitor trace in the user 's browser. Recall that Default is created by the MFC ISAPI Extension Wizard as the entry point called when no specific function is requested on an incoming URL. If you need your ISAPI extension to do more than one thing, you should add other class members and change the parse map macros, as we saw in Chapter 10.

Default creates the headers for the table by streaming the HTML code through the pCtxt pointer passed as the sole parameter to Default. Default relies upon the CWinSockClientComm class first described in Chapter 12. Recall that this class descends from CClientComm , a generic base class. If CMonitorService used named pipes instead of WinSock, I could have used the same method calls after creating a CNPClientComm object rather than a CWinSockClientComm object. The constructor for CWinSockClientComm is called with the name of the server and the port number. The server must be running CMonitorService , and the port must match the port used by CMonitorService . I call the Open method of CWinSockClientComm to actually create the socket. If Open fails and returns -1, a message is written to the HTML output stream and the function exits.

The key to getting any communication working reliably between a client and server program is to ensure that both programs observe a known protocol. As with communication between people, if both parties talk or listen at once they aren't likely to be as productive as if they talked and listened in turn . Default ensures that the protocol is followed by calling the Write and Read methods in a loop in the opposite order CMonitorService calls them. As each message is read, Default creates a table row and cell to contain the message. After all messages are read, I call the Close method of CWinSockClientComm to end the connection. Finally I stream out the code to create a button at the bottom of the page that allows the user to return to the default screen, which shows tasks with active alerts.



Inside Server-Based Applications
Inside Server-Based Applications (DV-MPS General)
ISBN: 1572318171
EAN: 2147483647
Year: 1999
Pages: 91

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net