A C Framework for Windows 2000 Services

While the program in Listing 3-1 is a workable Windows NT and Windows 2000 service, much of the code is boilerplate and can be used by other services. Boilerplate code is ideal for a C++ class that cleanly encapsulates the code while exposing event handlers that can be overridden to provide custom functionality. You can see the first C++ class for Windows 2000 Services in the MSDN documentation in an article by Nigel Thompson called "Creating a Simple Windows NT Service in C++." The class used by the C++ Beep Service example later in this chapter was developed independent of Nigel's example, but certainly no one writing a C++ class for Windows NT or Windows 2000 services should do so without reading Nigel's article.

Writing a C++ Class for Windows 2000 Services Is Harder Than It Looks

Upon first examination, a C++ class seems perfect for solving the problem of creating a service application. There are, however, a couple of problems, all of which center around implementation details of C++ classes. Imagine a simple C++ class that only contains the two callback functions that must be implemented for a Windows 2000 service.

 classCSillyService{ DWORDWINAPIHandlerEx(DWORDdwControl,DWORDdwEventType, LPVOIDlpEventData,LPVOIDlpContext); VOIDWINAPIServiceMain(DWORDdwArgc,LPTSTR*lpszArgv); }; 

The code fragment above declares the two callback functions required for a Windows 2000 service application. Unfortunately, these functions will not work. Internally, every member function or method of a C++ class has an implicit parameter named this that is a pointer to the specific instance of the class. One of the purposes of the implicit this pointer is to allow the methods of a class to dereference the data elements of the class. So while the function declarations look as if they meet the requirements of the service API, they do not.

How, then, can a method of a class be used as a callback function? I oversimplified the example above. All normal class methods have an implicit this parameter. However, classes can have static methods and data elements. For a static data element, there is one and only one copy of the element for all instances of the class. Only static methods can act on static data elements, and the static methods have no concept of this . Chapter 8 will introduce an ODBC class that makes extensive use of static class members, which also come in handy in creating a service class.

Rewriting the CSillyService class to use static methods (which will meet the API requirements for parameters) gives us the following fragment of a class declaration:

 classCPPService{ <;$VE> staticDWORDWINAPIHandlerEx(DWORDdwControl, DWORDdwEventType,LPVOIDlpEventData, LPVOIDlpContext); staticVOIDWINAPIServiceMain(DWORDdwArgc, LPTSTR*lpszArgv); staticCPPService*m_this; }; 

While the loss of the this pointer as an implicit but undeclared parameter eliminates one problem of using a C++ class to create a service, it creates another. Because the static member functions have no concept of this , the class must artificially provide it by means of a data member named m_this that is a static pointer to a CPPService object. The m_this pointer is set in the constructor and then used within the static methods to dereference specific data members of an instance of the class. Thus, static class member variables are similar to global variables, but are local to the class.

While this seems like a win-win situation, problems remain. First and foremost, the CPPService class is limited to supporting service applications that contain a single service, because all instances of the class would share a single m_this pointer. This is seldom a significant problem, but if you have a set of services that should be controlled independently yet share much common code, and so would be natural candidates for inclusion in a single large service application, CPPService might not be sufficient. For the vast majority of cases, however, the limitation of a single service within the application will not be a significant drawback.

How much smaller would a service application using the C++ class be than the HelloService program? Compare the code in Listing 3-1 to the following code in Listing 3-2.

Listing 3-2

CPPBeepService.cpp

 #include"stdafx.h" CBeepService::CBeepService(char*tsvcName,char*tdispName, DWORDtsvcStart):CPPService(tsvcName,tdispName,tsvcStart) { ; } BOOLCBeepService::OnInit(DWORD*status) { logError("OnInit"); *status=0; returntrue; } voidCBeepService::Run() { while(m_isRunning) { if(m_isPaused==false) { Beep(2000,200); } Sleep(1000); } return; } intmain(intargc,char*argv[]) { CBeepServiceMyBeepService("CBeepService","C++BeepService"); //Havewehandledtheargumentsanddoneaninstalloruninstall? if(MyBeepService.ParseArguments(argc,argv)==false) { //Ifnot,starttheservicerunning... MyBeepService.StartService(); } return0; } 

Listing 3-1 contains 275 lines of code, while Listing 3-2 contains 40 lines of code. If you keep in mind the credo of the high-quality coderthe best code is that which does not need to be writtenyou'll understand that this reduction in code is important. Interestingly, the size of the debugged version of the executable generated from the code in Listing 3-2 is about 15 percent smaller than the executable generated from the code in Listing 3-1. (The class declaration for the CBeepService class is in the stdafx.h file of the CPPBeepService project on the book's companion CD-ROM.) The class declaration is simple, showing how the base class needs to be used to implement a new service with minimal code.

 classCBeepService:publicCPPService { public: CBeepService(char*tsvcName,char*tdispName, DWORDtsvcStart=SERVICE_DEMAND_START); virtualBOOLOnInit(DWORD*status); virtualvoidRun(); }; 

Implementing the C++ Service Class

Obviously, there is no free ride, and obviously, the code must be somewhere. In the CPPService class, however, the code can be created once and used by many different services without inadvertently modifying the code during a copy-and-paste session. Just as important, if a bug is discovered in the core code that resides in the base class, it can be fixed once, and with a recompile, all applications using the code can be corrected at once.

The CPPService class handles most of the housekeeping associated with installing, uninstalling, and operating a service. The class provides the callback functions as static member functions and provides many virtual functions that allow descendant classes to provide handlers for many of the events that take place. The one virtual function that will almost always be overridden is Run. This function is called by ServiceMain and should contain the actual functionality of the service.

To get an overview of the structure of the class that implements most of the service functionality, look at Listing 3-3, the header file for the CPPService class.

Listing 3-3

CPPService.h

 #defineSERVICE_CONTROL_USER128 VOIDStartThisService(char*serviceName); classCPPService{ public: CPPService(char*tsvcName,char*tdispName, DWORDtsvcStart=SERVICE_DEMAND_START); virtual~CPPService(); BOOLIsInstalled(); BOOLInstall(); BOOLUnInstall(); BOOLStartService(); voidSetStatus(DWORDdwControl); BOOLInitialize(); BOOLParseArguments(intargc,char*argv[]); char*ServiceName() { returnserviceName; } voidSetUserAndPassword(char*user=NULL, char*pwd=NULL); virtualvoidOnInstall(); virtualvoidOnUninstall(); virtualBOOLOnInit(DWORD*status); virtualvoidRun(); virtualvoidOnStop(); virtualvoidOnPause(); virtualvoidOnContinue(); virtualBOOLOnUserControl(DWORDdwControl); virtualvoidOnParamChange(); virtualvoidOnNetBindAdd(); virtualvoidOnNetBindRemove(); virtualvoidOnNetBindEnable(); virtualvoidOnNetBindDisable(); #ifdef_WIN2K_COMPAT virtualvoidOnDeviceEvent(DWORDdwEventType, LPVOIDpvEventData); virtualvoidOnPowerEvent(DWORDdwEventType, LPVOIDpvEventData); virtualvoidOnHardwareProfileChange(DWORDdwEventType); #endif protected: SC_HANDLEm_scmHandle; SC_HANDLEm_scHandle; SERVICE_STATUSServiceStatus; SERVICE_STATUS_HANDLEServiceStatusHandle; staticCPPService*m_this; charserviceName[128]; charserviceDisplayName[128]; VOIDlogError(char*msg); FILE*log; charm_diag[255]; DWORDm_svcStartType; DWORDm_status; BOOLm_isRunning; BOOLm_isPaused; #ifdef_WIN2K_COMPAT staticDWORDWINAPIHandlerEx(DWORDdwControl, DWORDdwEventType,LPVOIDlpEventData, LPVOIDlpContext); #else staticVOIDWINAPIHandler(DWORDdwControl); #endif staticVOIDWINAPIServiceMain(DWORDdwArgc, LPTSTR*lpszArgv); }; 

Notice a few things about this class declaration. First, the #define of SERVICE_CONTROL_USER lets the class know that service control requests greater than or equal to 128 are user-defined control codes that an application can use to control the service. This is standard for all Windows 2000 services. Second, the class can be compiled for either Windows NT or Windows 2000, depending on the _WIN2K_COMPAT macro being defined. Note that the older style of functions will work on either Windows NT or Windows 2000. The functional difference is the availability of three additional virtual functions that allow the service to react to device events, power events, and hardware profile changes. The other noteworthy change in service support in Windows 2000 is the availability of a context pointer sent to HandlerEx . Because of the way the service class is implemented, the context pointer, which allows the HandlerEx function to distinguish between multiple services, is not terribly useful.

The class constructor for CPPService accepts several parameters:

 CPPService(char*tsvcName,char*tdispName, DWORDtsvcStart=SERVICE_DEMAND_START); 

The first two parameters are the service name and the display name. The third optional parameter allows the service to be installed to start either on demand or automatically. The constructor presented in full below first copies the arguments to class member variables. Many functions within the CPPService class require the handle of the SCM, so the constructor gets the handle of the SCM by calling OpenSCManager . The m_this pointer is set to point to this instance of the class, and m_isRunning and m_isPaused are set to false. These two Boolean variables are used to let the service know whether it is currently running or paused. Finally, the ServiceStatus structure is initialized.

 CPPService::CPPService(char*tsvcName,char*tdispName, DWORDtsvcStartType) { strncpy(serviceName,tsvcName,128); strncpy(serviceDisplayName,tdispName,128); m_svcStartType=tsvcStartType; m_scmHandle=OpenSCManager(NULL,SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); if(m_scmHandle==NULL) { printf("\nOpenSCManagererror%d",GetLastError()); } m_scHandle=0; m_this=this; m_isRunning=false; m_isPaused=false; ServiceStatus.dwServiceType=SERVICE_WIN32_OWN_PROCESS; ServiceStatus.dwCurrentState=SERVICE_START_PENDING; ServiceStatus.dwControlsAccepted= SERVICE_ACCEPT_STOPSERVICE_ACCEPT_PAUSE_CONTINUE; ServiceStatus.dwWin32ExitCode=0; ServiceStatus.dwServiceSpecificExitCode=0; ServiceStatus.dwCheckPoint=0; ServiceStatus.dwWaitHint=0; } 

ParseArguments uses the same logic from the main function in Listing 3-1. Commonly, as in Listing 3-2, ParseArguments should be called from the main function of the service. If ParseArguments returns false , there are no recognizable arguments on the command line and the service should be started using the CPPService member function StartService .

The Run virtual function is overridden in Listing 3-2 and provides the same functionality as the much longer program in Listing 3-1. The Run function controls the beeping and is controlled by the m_isRunning and m_isPaused member variables:

 voidCBeepService::Run() { while(m_isRunning) { if(m_isPaused==false) { Beep(2000,200); } Sleep(1000); } return; } 

Any function performed by the overridden Run method should perform its task as efficiently as possible. For example, if the Sleep line were moved inside the loop controlled by the m_isPaused variable, it would use much more processing time than would normally be required. When testing a service, it is useful to look at the Processes tab of the Task Manager to see the amount of memory and the percentage of the processor a service is using in all of its possible states, as shown for the CBeepService in Figure 3-2. Of course, the service does not show up on the Applications tab of the Task Manager.

Figure 3-2 The Task Manager in Windows 2000 showing information on CBeepService.

Listing 3-4 is the implementation of the CPPService class. Most of this code is similar in function to the code in Listing 3-1, but structured so that it can be used more generically.

Listing 3-4

CPPService.cpp

 #include"Stdafx.h" CPPService*CPPService::m_this=NULL; VOIDStartThisService(char*serviceName) { SERVICE_STATUSssStatus; DWORDdwOldCheckPoint; SC_HANDLEschSCManager= ::OpenSCManager(NULL,//Localmachine NULL,//ServicesActivedatabase SC_MANAGER_ALL_ACCESS);//Fullaccess printf("\nStartingService...\n"); SC_HANDLEschService=OpenService(schSCManager,//SCMdatabase TEXT(serviceName),//Servicename SERVICE_ALL_ACCESS); if(schService==NULL) { printf("\nCan'topenservice...lasterror=%d", GetLastError()); return; } if(!StartService(schService,//Handleofservice 0,//Numberofarguments NULL))//Noarguments { printf("\nStartServicefailure..."); return; } else { printf("Servicestartpending\n"); } //Checkthestatusuntiltheserviceisnolongerstartpending. if(!QueryServiceStatus(schService,//Handleofservice &ssStatus))//Addressofstatusinformation { printf("\nErrorinQueryServiceStatus"); return; } //Someservicestakealongtimetostart,sothereis //anelaboratemechanismtohandlethat.Thisisnot //veryusefulwiththeexampleservicesprovidedbut //couldbemoresoformorecomplexservices. while(ssStatus.dwCurrentState==SERVICE_START_PENDING) { //Savethecurrentcheckpoint. dwOldCheckPoint=ssStatus.dwCheckPoint; //Waitforthespecifiedinterval. Sleep(ssStatus.dwWaitHint);//Checkthestatusagain. if(!QueryServiceStatus(schService,//Handleofservice &ssStatus))//Addressofstatusinformation { break; } //Breakifthecheckpointhasnotbeenincremented. if(dwOldCheckPoint>=ssStatus.dwCheckPoint) { break; } } if(ssStatus.dwCurrentState==SERVICE_RUNNING) { printf("\nServiceStarted\n"); } else { printf("CurrentState:%d\n", ssStatus.dwCurrentState); printf("ExitCode:%d\n",ssStatus.dwWin32ExitCode); printf("ServiceSpecificExitCode:%d\n", ssStatus.dwServiceSpecificExitCode); printf("CheckPoint:%d\n",ssStatus.dwCheckPoint); printf("WaitHint:%d\n",ssStatus.dwWaitHint); } CloseServiceHandle(schService); CloseServiceHandle(schSCManager); } CPPService::CPPService(char*tsvcName,char*tdispName, DWORDtsvcStartType) { strncpy(serviceName,tsvcName,128); strncpy(serviceDisplayName,tdispName,128); m_svcStartType=tsvcStartType; m_scmHandle=OpenSCManager(NULL,SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS); if(m_scmHandle==NULL) { printf("\nOpenSCManagererror%d",GetLastError()); } m_scHandle=0; m_this=this; m_isRunning=false; m_isPaused=false; ServiceStatus.dwServiceType=SERVICE_WIN32_OWN_PROCESS; ServiceStatus.dwCurrentState=SERVICE_START_PENDING; ServiceStatus.dwControlsAccepted= SERVICE_ACCEPT_STOPSERVICE_ACCEPT_PAUSE_CONTINUE; ServiceStatus.dwWin32ExitCode=0; ServiceStatus.dwServiceSpecificExitCode=0; ServiceStatus.dwCheckPoint=0; ServiceStatus.dwWaitHint=0; } CPPService::~CPPService() { if(m_scmHandle!=0) { ::CloseHandle(m_scmHandle); } if(m_scHandle!=0) { ::CloseHandle(m_scHandle); } } BOOLCPPService::ParseArguments(intargc,char**argv) { BOOLresult=false; if(argc>1&&!(strnicmp(argv[1],"-i",2))) { if((Install())) { printf("\n%sinstalled!",ServiceName()); } else { printf("\n%sNOTinstalled!Lasterror%d", ServiceName(),GetLastError()); } result=true; } elseif(argc>1&&!(strnicmp(argv[1],"-u",2))) { if((UnInstall())) { printf("\n%suninstalled!",ServiceName()); } else { printf("\n%sNOTuninstalled!Lasterror%d", ServiceName(),GetLastError()); } result=true; } returnresult; } VOIDCPPService::logError(char*text) { log=fopen("c:\\service.log","a+t"); if(log) { fprintf(log,"%s\n",text); fclose(log); } return; } BOOLCPPService::Install() { charszPath[MAX_PATH]; charszBuffer[MAX_PATH+2]; BOOLresult=true;//Defaulttosuccess GetModuleFileName(GetModuleHandle(NULL),szPath,MAX_PATH); //Whydowedothis?Thepathmaycontainspaces. //Ifitdoes,thepathmustbequoted. strcpy(szBuffer,"\""); strcat(szBuffer,szPath); strcat(szBuffer,"\""); printf("\nInstallingservice%s",szPath); m_scHandle=CreateService(m_scmHandle,serviceName, serviceDisplayName,SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, m_svcStartType, SERVICE_ERROR_NORMAL,szBuffer, NULL,NULL,NULL,NULL,NULL); if(m_scHandle==NULL)//Processerror { DWORDlastError; LastError=GetLastError(); if(lastError==1073)//Servicealreadyinstalled { sprintf(m_diag,"CreateServiceerror=" ServiceAlreadyInstalled(1073)\n"); } else { sprintf(m_diag,"CreateServiceerror=%d\n", lastError); } printf("%s\n",m_diag); logError(m_diag); result=false; } OnInstall(); returnresult; } BOOLCPPService::StartService() { SERVICE_TABLE_ENTRYDispatchTable[]={ {serviceName,CPPService::ServiceMain}, {NULL,NULL} }; BOOLb=::StartServiceCtrlDispatcher(DispatchTable); returnb; } BOOLCPPService::UnInstall() { m_scHandle=OpenService(m_scmHandle,serviceName, SERVICE_ALL_ACCESS); if(m_scHandle!=NULL) { DeleteService(m_scHandle); printf("\n%suninstalled...",serviceName); CloseHandle(m_scHandle); m_scHandle=0; OnUninstall(); return(true); } printf("\nOpenServiceerror:%d",GetLastError()); return(false); } voidCPPService::SetStatus(DWORDdwControl) { ServiceStatus.dwCurrentState=dwControl; ServiceStatus.dwCheckPoint=0; ServiceStatus.dwWaitHint=0; if(!SetServiceStatus(ServiceStatusHandle,&ServiceStatus)) { m_status=GetLastError(); sprintf(m_diag, "%sSetServiceStatuserror%lddwControl=%d\n", serviceName,m_status,dwControl); logError(m_diag); } } VOIDWINAPICPPService::ServiceMain(DWORDargc,LPTSTR*argv) { BOOLinitStat; DWORDspecificError; CPPService*thisPtr; thisPtr=m_this; #ifdef_WIN2K_COMPAT charszContext[255]; strcpy(szContext,thisPtr->ServiceName()); thisPtr->ServiceStatusHandle=RegisterServiceCtrlHandlerEx(thisPtr->ServiceName, HandlerEx, &szContext); #else thisPtr->ServiceStatusHandle=RegisterServiceCtrlHandler(thisPtr->serviceName, Handler); #endif if(thisPtr->ServiceStatusHandle==(SERVICE_STATUS_HANDLE)0) { sprintf(thisPtr->m_diag, "RegisterServiceCtrlHandlerfailed%d\n", GetLastError()); thisPtr->logError(thisPtr->m_diag); return; } thisPtr->SetStatus(SERVICE_START_PENDING); //Initializationcodegoeshere. initStat=thisPtr->OnInit(&specificError); //Handleerrorcondition. if(initStat==false) { thisPtr->SetStatus(SERVICE_STOPPED); sprintf(thisPtr->m_diag, "OnInitfailedinitStat=%d...\n",initStat); thisPtr->logError(thisPtr->m_diag); return; } thisPtr->SetStatus(SERVICE_RUNNING); thisPtr->m_isRunning=true; sprintf(thisPtr->m_diag, "Justbeforeprocessingloop...\n"); thisPtr->logError(thisPtr->m_diag); //Thisiswheretheservicedoesitswork. thisPtr->Run(); //Sendcurrentstatus. thisPtr->SetStatus(SERVICE_STOPPED); return; } #ifdef_WIN2K_COMPAT DWORDWINAPICPPService::HandlerEx(DWORDdwControl, DWORDdwEventType,LPVOIDlpEventData, LPVOIDlpContext) { DWORDstatus=SERVICE_RUNNING; CPPService*thisPtr; thisPtr=m_this; sprintf(thisPtr->m_diag, "Intoservicecontrol...\n"); thisPtr->logError(thisPtr->m_diag); switch(dwControl) { caseSERVICE_CONTROL_PAUSE: //Dowhateverittakestopausehere. sprintf(thisPtr->m_diag,"Pausing...\n"); thisPtr->logError(thisPtr->m_diag); if(!(thisPtr->m_isPaused)) { thisPtr->m_isPaused=true; thisPtr->OnPause(); status=SERVICE_PAUSED; } break; caseSERVICE_CONTROL_CONTINUE: //Dowhateverittakestocontinuehere. sprintf(thisPtr->m_diag,"Continuing...\n"); thisPtr->logError(thisPtr->m_diag); if(thisPtr->m_isPaused) { thisPtr->OnContinue(); thisPtr->m_isPaused=false; status=SERVICE_RUNNING; } break; caseSERVICE_CONTROL_STOP: //Dowhateverittakestostophere. sprintf(thisPtr->m_diag,"Stopping...\n"); thisPtr->logError(thisPtr->m_diag); if(thisPtr->m_isRunning) { thisPtr->OnStop(); status=SERVICE_STOP_PENDING; thisPtr->m_isRunning=false; } thisPtr->SetStatus(status); sprintf(thisPtr->m_diag, "BeeperServiceLeavingHandler\n",0); thisPtr->logError(thisPtr->m_diag); return; caseSERVICE_CONTROL_PARAMCHANGE: thisPtr->OnParamChange(); break; caseSERVICE_CONTROL_NETBINDADD: thisPtr->OnNetBindAdd(); break caseSERVICE_CONTROL_NETBINDDELETE: thisPtr->OnNetBindDelete(); break caseSERVICE_CONTROL_NETBINDREMOVE: thisPtr->OnNetBindRemove(); break caseSERVICE_CONTROL_NETBINDENABLE: thisPtr->OnNetBindEnable(); break caseSERVICE_CONTROL_NETBINDDISABLE: thisPtr->OnNetBindDisable(); break caseSERVICE_CONTROL_DEVICEEVENT: thisPtr->OnDeviceEvent(dwEventType, lpEventData); break; caseSERVICE_CONTROL_POWEREVENT: thisPtr->OnPowerEvent(dwEventType, lpEventData); break; caseSERVICE_CONTROL_HARDWAREPROFILECHANGE: thisPtr->OnHardwareProfileChange(dwEventType); break; caseSERVICE_CONTROL_INTERROGATE: //Fallthroughtosendcurrentstatus. break; default: if(dwControl>=SERVICE_CONTROL_USER) { if(!thisPtr->OnUserControl(dwControl)) { sprintf(thisPtr->m_diag, "%sUnhandledcontrolcode%ld\n", thisPtr->ServiceName(), dwControl); thisPtr->logError(thisPtr->m_diag); } else { sprintf(thisPtr->m_diag, "%sUnrecognizedcontrolcode%ld\n", thisPtr->ServiceName(), dwControl); thisPtr->logError(thisPtr->m_diag); } break; } } //Sendcurrentstatus. thisPtr->SetStatus(status); return0; } #else VOIDWINAPICPPService::Handler(DWORDdwControl) { DWORDstatus=SERVICE_RUNNING; CPPService*thisPtr; thisPtr=m_this; sprintf(thisPtr->m_diag, "Intoservicecontrol...\n"); thisPtr->logError(thisPtr->m_diag); switch(dwControl) { caseSERVICE_CONTROL_PAUSE: //Dowhateverittakestopausehere. sprintf(thisPtr->m_diag, "Pausing...\n"); thisPtr->logError(thisPtr->m_diag); if(!(thisPtr->m_isPaused)) { thisPtr->m_isPaused=true; thisPtr->OnPause(); status=SERVICE_PAUSED; } break; caseSERVICE_CONTROL_CONTINUE: //Dowhateverittakestocontinuehere. sprintf(thisPtr->m_diag, "Continuing...\n"); thisPtr->logError(thisPtr->m_diag); if(thisPtr->m_isPaused) { thisPtr->OnContinue(); thisPtr->m_isPaused=false; status=SERVICE_RUNNING; } break; caseSERVICE_CONTROL_STOP: //Dowhateverittakestostophere. sprintf(thisPtr->m_diag, "Stopping...\n"); thisPtr->logError(thisPtr->m_diag); if(thisPtr->m_isRunning) { thisPtr->OnStop(); status=SERVICE_STOP_PENDING; thisPtr->m_isRunning=false; } thisPtr->SetStatus(status); sprintf(thisPtr->m_diag, "BeeperServiceleavinghandler\n",0); thisPtr->logError(thisPtr->m_diag); return; caseSERVICE_CONTROL_PARAMCHANGE: thisPtr->OnParamChange(); break; caseSERVICE_CONTROL_NETBINDADD: thisPtr->OnNetBindAdd(); break; caseSERVICE_CONTROL_NETBINDDISABLE: thisPtr->OnNetBindDisable(); break; caseSERVICE_CONTROL_NETBINDREMOVE: thisPtr->OnNetBindRemove(); break; caseSERVICE_CONTROL_NETBINDENABLE: thisPtr->OnNetBindEnable(); break; caseSERVICE_CONTROL_INTERROGATE: //Fallthroughtosendcurrentstatus. break; default: if(dwControl>=SERVICE_CONTROL_USER) { if(!thisPtr->OnUserControl(dwControl)) { sprintf(thisPtr->m_diag, "%sUnhandledcontrolcode%ld\n", thisPtr->ServiceName(), dwControl); thisPtr->logError(thisPtr->m_diag); } else { sprintf(thisPtr->m_diag, "%sUnrecognizedcontrolcode%ld\n", thisPtr->ServiceName(), dwControl); thisPtr->logError(thisPtr->m_diag); } break; } } //Sendcurrentstatus. thisPtr->SetStatus(status); return; } #endif //Virtualfunctions;generallyoverriddenifneeded. BOOLCPPService::OnUserControl(DWORDdwControl) { logError("OnContinue"); returnfalse; } voidCPPService::OnContinue() { logError("OnContinue"); return; } voidCPPService::OnPause() { logError("OnPause"); return; } voidCPPService::OnStop() { logError("OnStop"); return; } voidCPPService::OnInstall() { logError("OnInstall"); return; } voidCPPService::OnUninstall() { logError("OnUninstall"); return; } BOOLCPPService::OnInit(DWORD*status) { logError("OnInit"); *status=0; returntrue; } voidCPPService::OnParamChange() { logError("OnParamChange"); return; } voidCPPService::OnNetBindAdd() { logError("OnNetBindAdd"); return; } voidCPPService::OnNetBindRemove() { logError("OnNetBindRemove"); return; } voidCPPService::OnNetBindEnable() { logError("OnNetBindEnable"); return; } voidCPPService::OnNetBindDisable() { logError("OnNetBindDisable"); return; } voidCPPService::Run() { while(m_isRunning) { Sleep(1000); } return; } #ifdef_WIN2K_COMPAT voidOnDeviceEvent(DWORDdwEventType,LPVOIDpvEventData) { logError("OnDeviceEvent"); return; } voidOnPowerEvent(DWORDdwEventType,LPVOIDpvEventData) { logError("OnPowerEvent"); return; } voidOnHardwareProfileChange(DWORDdwEventType) { logError("OnHardwareProfileChange"); return; } #endif 

Most of the functions in Listing 3-4 are base virtual functions that do little more than log that they were called. Each of the virtual functions is called where appropriate by the code within CPPService . For instance, the Handler and HandlerEx functions call the OnUserControl method in the default case of the select statement controlled by dwControl . If the service will allow a service control function to send user-defined control codes to the service to implement service-specific actions, this function should be overridden in the derived class to handle those service-specific control codes. Chapter 12 will show an alternative way of controlling services by using named pipes.

One More Enhancement

An additional desirable feature is the ability of the service to start as it is installed. This is especially handy if a service should start automatically as the server starts. With the CPPService class as currently implemented, that would require the user to restart the server or use the Computer Management tool to manually start the service. Listing 3-4 contains a nonmember function named StartThisService . This function takes as its single parameter the name of the service to start. It then performs some standard operations, including calling OpenSCManager to get the handle of the SCM and calling OpenService to get the handle of the service. StartService is called to actually start the service.

Once the start has been requested, the function enters a loop to wait for the service to load or fail.

 while(ssStatus.dwCurrentState==SERVICE_START_PENDING) { //Savethecurrentcheckpoint. dwOldCheckPoint=ssStatus.dwCheckPoint; //Waitforthespecifiedinterval. Sleep(ssStatus.dwWaitHint);//Checkthestatusagain. if(!QueryServiceStatus(schService,//Handleofservice &ssStatus))//Addressofstatusinformation { break; } //Breakifthecheckpointhasnotbeenincremented. if(dwOldCheckPoint>=ssStatus.dwCheckPoint) { break; } } if(ssStatus.dwCurrentState==SERVICE_RUNNING) { printf("\nServiceStarted\n"); } else { printf("CurrentState:%d\n",ssStatus.dwCurrentState); printf("ExitCode:%d\n",ssStatus.dwWin32ExitCode); printf("ServiceSpecificExitCode:%d\n", ssStatus.dwServiceSpecificExitCode); printf("CheckPoint:%d\n",ssStatus.dwCheckPoint); printf("WaitHint:%d\n",ssStatus.dwWaitHint); } 

The while loop tracks the checkpoint set by the service and sleeps the number of milliseconds specified by the wait hint set by the service. If the time elapses without the service having changed the checkpoint value, the function assumes the service has failed to start and generates error information that will detail the reason for the failure.

To allow the CPPBeepService class to start the service upon installation, the OnInstall virtual function is overridden and StartThisService is called with the name of the service. There are some limitations to how services can be started that make implementing this separate function preferable to including the functionality directly in the class. Furthermore, the function can be used to start any arbitrary service, not just the service represented by the class.



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