//*************************************************************** // From the book "Win32 System Services: The Heart of Windows 98 // and Windows 2000" // by Marshall Brain // Published by Prentice Hall // // Copyright 1995, by Prentice Hall. // // This code implements the RPC server. //*************************************************************** // sumserv.cpp #include #include #include #include "sum.h" #include "memstub" // Global variables // The name of the service char *SERVICE_NAME = "SumService"; // Event used to hold ServiceMain from completing HANDLE terminateEvent = NULL; // Handle used to communicate status info with // the SCM. Created by RegisterServiceCtrlHandler SERVICE_STATUS_HANDLE serviceStatusHandle; // Flags holding current state of service BOOL pauseService = FALSE; BOOL runningService = FALSE; // Initializes the service BOOL InitService() { RPC_BINDING_VECTOR *bindVector; runningService = TRUE; // use protocols specified by interface if (RpcServerUseAllProtseqsIf(1, sumup_v1_1_s_ifspec, NULL)) return FALSE; // register the interface if (RpcServerRegisterIf(sumup_v1_1_s_ifspec, NULL, NULL)) return FALSE; // get binding handles available if (RpcServerInqBindings(&bindVector)) return FALSE; // export binding info if (RpcNsBindingExport(RPC_C_NS_SYNTAX_DEFAULT, (UCHAR *) "/.:/autorpc", sumup_v1_1_s_ifspec, bindVector, NULL)) { // release memory used by binding vectors RpcBindingVectorFree(&bindVector); return FALSE; } // release memory used by binding vectors if (RpcBindingVectorFree(&bindVector)) return FALSE; // activate this server if (RpcServerListen(1, 5, TRUE)) return FALSE; return TRUE; } // Resumes a paused service VOID ResumeService() { pauseService=FALSE; // reactivate this server RpcServerListen(1, 5, TRUE); } // Pauses the service VOID PauseService() { pauseService = TRUE; // stop accepting new calls RpcMgmtStopServerListening(NULL); // wait for existing calls to finish RpcMgmtWaitServerListen(); } // Stops the service by allowing ServiceMain // to complete VOID StopService() { runningService=FALSE; // Set the event that is holding ServiceMain // so that ServiceMain can return SetEvent(terminateEvent); } // This function consolidates the activities of // updating the service status // with SetServiceStatus BOOL SendStatusToSCM (DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwServiceSpecificExitCode, DWORD dwCheckPoint, DWORD dwWaitHint) { BOOL success; SERVICE_STATUS serviceStatus; // Fill in all of the SERVICE_STATUS fields serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; serviceStatus.dwCurrentState = dwCurrentState; // If in the process of starting, then accept // no control events, else accept anything if (dwCurrentState == SERVICE_START_PENDING) serviceStatus.dwControlsAccepted = 0; else serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN; // if a specific exit code is defines, set up // the win32 exit code properly if (dwServiceSpecificExitCode == 0) serviceStatus.dwWin32ExitCode = dwWin32ExitCode; else serviceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; serviceStatus.dwServiceSpecificExitCode = dwServiceSpecificExitCode; serviceStatus.dwCheckPoint = dwCheckPoint; serviceStatus.dwWaitHint = dwWaitHint; // Pass the status record to the SCM success = SetServiceStatus (serviceStatusHandle, &serviceStatus); if (!success) StopService(); return success; } // Dispatches events received from the service // control manager VOID ServiceCtrlHandler (DWORD controlCode) { DWORD currentState = 0; BOOL success; switch(controlCode) { // There is no START option because // ServiceMain gets called on a start // Stop the service case SERVICE_CONTROL_STOP: currentState = SERVICE_STOP_PENDING; // Tell the SCM what's happening success = SendStatusToSCM( SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 5000); // Not much to do if not successful // Allow ServiceMain to return SetEvent(terminateEvent); return; // Pause the service case SERVICE_CONTROL_PAUSE: if (runningService && !pauseService) { // Tell the SCM what's happening success = SendStatusToSCM( SERVICE_PAUSE_PENDING, NO_ERROR, 0, 1, 1000); PauseService(); currentState = SERVICE_PAUSED; } break; // Resume from a pause case SERVICE_CONTROL_CONTINUE: if (runningService && pauseService) { ResumeService(); // Tell the SCM what's happening success = SendStatusToSCM( SERVICE_CONTINUE_PENDING, NO_ERROR, 0, 1, 1000); currentState = SERVICE_RUNNING; } break; // Update current status case SERVICE_CONTROL_INTERROGATE: // it will fall to bottom and send status break; // Do nothing in a shutdown. Could do cleanup // here, but it must be very quick. case SERVICE_CONTROL_SHUTDOWN: // Do nothing on shutdown return; default: break; } SendStatusToSCM(currentState, NO_ERROR, 0, 0, 0); } // Handle an error from ServiceMain by cleaning up // and telling SCM that the service didn't start. VOID terminate(DWORD error) { if (!error) { // stop accepting new calls RpcMgmtStopServerListening(NULL); // remove binding info RpcNsBindingUnexport(RPC_C_NS_SYNTAX_DEFAULT, (UCHAR *) "/.:/autorpc", sumup_v1_1_s_ifspec, NULL); // unregister interface and wait // for existing calls to finish RpcServerUnregisterIf(NULL, NULL, TRUE); } // if terminateEvent has been created, close it. if (terminateEvent) CloseHandle(terminateEvent); // Send a message to the SCM to tell about //stopage if (serviceStatusHandle) SendStatusToSCM(SERVICE_STOPPED, error, 0, 0, 0); // Do not need to close serviceStatusHandle } // ServiceMain is called when the SCM wants // to start the service. When it returns, // the service has stopped. It // Therefore waits on an event just before the end // of the function, and that event gets set // when it is time to stop. It also returns // on any error because the // service cannot start if there is an eror. VOID ServiceMain(DWORD argc, LPTSTR *argv) { BOOL success; // immediately call Registration function serviceStatusHandle = RegisterServiceCtrlHandler( SERVICE_NAME, (LPHANDLER_FUNCTION)ServiceCtrlHandler); if (!serviceStatusHandle) { terminate(GetLastError()); return; } // Notify SCM of progress success = SendStatusToSCM( SERVICE_START_PENDING, NO_ERROR, 0, 1, 5000); if (!success) { terminate(GetLastError()); return; } // create the termination event terminateEvent = CreateEvent (0, TRUE, FALSE, 0); if (!terminateEvent) { terminate(GetLastError()); return; } // Notify SCM of progress success = SendStatusToSCM( SERVICE_START_PENDING, NO_ERROR, 0, 2, 1000); if (!success) { terminate(GetLastError()); return; } // Start the service itself success = InitService(); if (!success) { terminate(GetLastError()); return; } // The service is now running. // Notify SCM of progress success = SendStatusToSCM( SERVICE_RUNNING, NO_ERROR, 0, 0, 0); if (!success) { terminate(GetLastError()); return; } // Wait for stop signal, and then terminate WaitForSingleObject (terminateEvent, INFINITE); terminate(0); } VOID main(VOID) { SERVICE_TABLE_ENTRY serviceTable[] = { { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION) ServiceMain}, { NULL, NULL } }; BOOL success; // Register with the SCM success = StartServiceCtrlDispatcher(serviceTable); if (!success) ExitProcess(GetLastError()); }