/*++ Routine Description: This function is executed when a Stop command is sent to the service by SCM. It specifies actions to take when a service stops running. In this code sample, OnStop logs a service-stop message to the Application log, and waits for the finish of the main service function. Be sure to periodically call ReportServiceStatus() with SERVICE_STOP_PENDING if the procedure is going to take a long time. Arguments: VOID Return Value: VOID --*/ VOID CSampleService::OnStop() { // // Log a service stop message to the Application log. // WriteToEventLog(L"SampleService in OnStop", EVENTLOG_INFORMATION_TYPE); // // Indicate that the service is stopping and wait for the finish of the // main service function (ServiceWorkerThread). // m_fStopping = TRUE; if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0) { throw GetLastError(); } // // Clean up the context after the worker thread has finished. // CloseContext(m_Context); }
/* * Function to start as Windows service * The calling party should specify their entry point as input parameter * Returns TRUE if the Service is started successfully */ BOOL RunAsService (INT (*ServiceFunction) (INT, LPTSTR *)) { /* * Set the ServiceEntryPoint */ ServiceEntryPoint = ServiceFunction; /* * By default, mark as Running as a service */ g_fRunningAsService = TRUE; /* * Initialize ServiceTableEntry table */ ServiceTableEntry[0].lpServiceName = app_name_long; /* Application Name */ /* * Call SCM via StartServiceCtrlDispatcher to run as Service * * If the function returns TRUE we are running as Service, */ if (StartServiceCtrlDispatcher (ServiceTableEntry) == FALSE) { g_fRunningAsService = FALSE; /* * Some other error has occurred. */ WriteToEventLog (EVENTLOG_ERROR_TYPE, _T ("Couldn't start service - %s"), app_name_long); } return g_fRunningAsService; }
/*++ Routine Description: The function executes when the system is shutting down. It calls the OnShutdown virtual function in which you can specify what should occur immediately prior to the system shutting down. If an error occurs, the error will be logged in the Application event log. Arguments: VOID Return Value: VOID --*/ VOID CServiceBase::Shutdown() { try { // // Perform service-specific shutdown operations. // OnShutdown(); // // Tell SCM that the service is stopped. // SetServiceStatus(SERVICE_STOPPED); } catch (DWORD Error) { // // Log the error. // WriteToErrorLog(L"Service Shutdown", Error); } catch (...) { // // Log the error. // WriteToEventLog(L"Service failed to shut down.", EVENTLOG_ERROR_TYPE); } }
/*++ Routine Description: This function is executed when a Start command is sent to the service by the SCM or when the operating system starts (for a service that starts automatically). It specifies actions to take when the service starts. In this code sample, OnStart logs a service-start message to the Application log, and queues the main service function for execution in a thread pool worker thread. NOTE: A service application is designed to be long running. Therefore, it usually polls or monitors something in the system. The monitoring is set up in the OnStart method. However, OnStart does not actually do the monitoring. The OnStart method must return to the operating system after the service's operation has begun. It must not loop forever or block. To set up a simple monitoring mechanism, one general solution is to create a timer in OnStart. The timer would then raise events in your code periodically, at which time your service could do its monitoring. The other solution is to spawn a new thread to perform the main service functions, which is demonstrated in this code sample. Arguments: Argc - The number of command line arguments Argv - The array of command line arguments Return Value: VOID --*/ VOID CSampleService::OnStart( DWORD Argc, PWSTR *Argv ) { __debugbreak(); // // Log a service start message to the Application log. // WriteToEventLog(L"SampleService in OnStart", EVENTLOG_INFORMATION_TYPE); // // Set up any variables the service needs. // SetVariables(); // // Set up the context, and register for notifications. // InitializeContext(&m_Context); // // Queue the main service function for execution in a worker thread. // CThreadPool::QueueUserWorkItem(&CSampleService::ServiceWorkerThread, this); }
/*++ Routine Description: Log an error message. Arguments: Function - The function that gives the error Error - The error code Return Value: VOID --*/ VOID WriteToErrorLog( PWSTR Function, DWORD Error ) { WCHAR Message[260]; StringCchPrintf(Message, ARRAYSIZE(Message), L"%ws failed with error: 0x%08x", Function, Error); WriteToEventLog(Message, TRACE_LEVEL_ERROR); }
/*++ Routine Description: This function starts the service. It calls the OnStart virtual function in which you can specify the actions to take when the service starts. If an error occurs during the startup, the error will be logged in the Application event log, and the service will be stopped. Arguments: Argc - The number of command line arguments Argv - The array of command line arguments Return Value: VOID --*/ VOID CServiceBase::Start( DWORD Argc, PWSTR *Argv ) { try { // // Tell SCM that the service is starting. // SetServiceStatus(SERVICE_START_PENDING); // // Perform service-specific initialization. // OnStart(Argc, Argv); // // Tell SCM that the service is started. // SetServiceStatus(SERVICE_RUNNING); } catch (DWORD Error) { // // Log the error. // WriteToErrorLog(L"Service Start", Error); // // Set the service status to be stopped. // SetServiceStatus(SERVICE_STOPPED, Error); } catch (...) { // // Log the error. // WriteToEventLog(L"Service failed to start.", EVENTLOG_ERROR_TYPE); // // Set the service status to be stopped. // SetServiceStatus(SERVICE_STOPPED); } }
/*++ Routine Description: This function stops the service. It calls the OnStop virtual function in which you can specify the actions to take when the service stops. If an error occurs, the error will be logged in the Application event log, and the service will be restored to the original state. Arguments: VOID Return Value: VOID --*/ VOID CServiceBase::Stop() { DWORD OriginalState = m_status.dwCurrentState; try { // // Tell SCM that the service is stopping. // SetServiceStatus(SERVICE_STOP_PENDING); // // Perform service-specific stop operations. // OnStop(); // // Tell SCM that the service is stopped. // SetServiceStatus(SERVICE_STOPPED); } catch (DWORD Error) { // // Log the error. // WriteToErrorLog(L"Service Stop", Error); // // Set the orginal service status. // SetServiceStatus(OriginalState); } catch (...) { // // Log the error. // WriteToEventLog(L"Service failed to stop.", EVENTLOG_ERROR_TYPE); // // Set the orginal service status. // SetServiceStatus(OriginalState); } }
/*++ Routine Description: The function resumes normal functioning after being paused if the service supports pause and continue. It calls the OnContinue virtual function in which you can specify the actions to take when the service continues. If an error occurs, the error will be logged in the Application event log, and the service will still be paused. Arguments: VOID Return Value: VOID --*/ VOID CServiceBase::Continue() { try { // // Tell SCM that the service is resuming. // SetServiceStatus(SERVICE_CONTINUE_PENDING); // // Perform service-specific continue operations. // OnContinue(); // // Tell SCM that the service is running. // SetServiceStatus(SERVICE_RUNNING); } catch (DWORD Error) { // // Log the error. // WriteToErrorLog(L"Service Continue", Error); // // Tell SCM that the service is still paused. // SetServiceStatus(SERVICE_PAUSED); } catch (...) { // // Log the error. // WriteToEventLog(L"Service failed to resume.", EVENTLOG_ERROR_TYPE); // // Tell SCM that the service is still paused. // SetServiceStatus(SERVICE_PAUSED); } }
/*++ Routine Description: The function pauses the service if the service supports pause and continue. It calls the OnPause virtual function in which you can specify the actions to take when the service pauses. If an error occurs, the error will be logged in the Application event log, and the service will become running. Arguments: VOID Return Value: VOID --*/ VOID CServiceBase::Pause() { try { // // Tell SCM that the service is pausing. // SetServiceStatus(SERVICE_PAUSE_PENDING); // // Perform service-specific pause operations. // OnPause(); // // Tell SCM that the service is paused. // SetServiceStatus(SERVICE_PAUSED); } catch (DWORD Error) { // // Log the error. // WriteToErrorLog(L"Service Pause", Error); // // Tell SCM that the service is still running. // SetServiceStatus(SERVICE_RUNNING); } catch (...) { // // Log the error. // WriteToEventLog(L"Service failed to pause.", EVENTLOG_ERROR_TYPE); // // Tell SCM that the service is still running. // SetServiceStatus(SERVICE_RUNNING); } }
/* ** Name: WriteDTCerrToEventLog ** ** Description: ** Decode the error return code into a message and ** report to the Windows Event Log the error message list. ** */ void WriteDTCerrToEventLog(HRESULT hr, char * t) { char message[512]; char workarea[256]; char * s; switch (hr) { case E_FAIL: s="rc=E_FAIL (General failure; Check the Windows Event Viewer for more information)"; break; case E_INVALIDARG: s="rc=E_INVALIDARG (One or more of the parameters are NULL or invalid)"; break; case E_NOINTERFACE: s="rc=E_NOINTERFACE (Unable to provide the requested interface)"; break; case E_OUTOFMEMORY: s="rc=E_OUTOFMEMORY (Unable to allocate resources for the object)"; break; case E_UNEXPECTED: s="rc=E_UNEXPECTED (An unexpected error occurred)"; break; case E_POINTER: s="rc=E_POINTER (Invalid handle)"; break; case XACT_E_CONNECTION_DOWN: s="rc=XACT_E_CONNECTION_DOWN (Lost connection with DTC Transaction Manager)"; break; case XACT_E_TMNOTAVAILABLE: s="rc=XACT_E_TMNOTAVAILABLE (Unable to establish a connection with DTC (MS DTC service probably not started))"; break; case XACT_E_NOTRANSACTION: s="rc=XACT_E_NOTRANSACTION (No transaction corresponding to ITRANSACTION pointer)"; break; case XACT_E_CONNECTION_DENIED: s="rc=XACT_E_CONNECTION_DENIED (Transaction manager refused to accept a connection)"; break; case XACT_E_LOGFULL: s="rc=XACT_E_LOGFULL (Transaction Manager has run out of log space)"; break; case XACT_E_XA_TX_DISABLED: s="rc=XACT_E_XA_TX_DISABLED (Transaction Manager has disabled its support " "for XA transactions. See MS KB article 817066 to enable XA and register " "\"caiiod35.dll\".)"; break; default: s=workarea; wsprintf(s,"0x%8.8lX",(long)hr); } strcpy(message, t); if (hr != S_OK) { strcat(message, "; "); strcat(message,s); /* write return code message to errlog */ } WriteToEventLog("Ingres ODBC", message); return; }
/* * ServiceMain function. */ VOID WINAPI ServiceMain (DWORD argc, LPTSTR argv[]) { SECURITY_ATTRIBUTES SecurityAttributes; unsigned threadId; /* * Input arguments */ DWORD ArgCount = 0; LPTSTR *ArgArray = NULL; TCHAR szRegKey[512]; HKEY hParamKey = NULL; DWORD TotalParams = 0; DWORD i; InputParams ThreadInputParams; /* * Build the Input parameters to pass to worker thread */ /* * SCM sends Service Name as first arg, increment to point * arguments user specified while starting control agent */ /* * Read registry parameter */ ArgCount = 1; /* * Create registry key path */ _sntprintf (szRegKey, CountOf(szRegKey), _T("%s%s\\%s"), _T ("SYSTEM\\CurrentControlSet\\Services\\"), app_name_long, _T("Parameters")); if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ALL_ACCESS, &hParamKey) == ERROR_SUCCESS) { /* * Read startup configuration information */ /* * Find number of subkeys inside parameters */ if (RegQueryInfoKey (hParamKey, NULL, NULL, 0, NULL, NULL, NULL, &TotalParams, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { if (TotalParams != 0) { ArgCount += TotalParams; /* * Allocate memory to hold strings */ ArgArray = calloc(ArgCount, sizeof(ArgArray[0])); if (ArgArray == 0) { WriteToEventLog (EVENTLOG_ERROR_TYPE, _T ("Resource failure")); return; } /* * Copy first argument */ ArgArray[0] = _tcsdup (argv[0]); for (i = 1; i <= TotalParams; i++) { DWORD dwErrorcode; DWORD nSize; DWORD nRegkeyType; TCHAR *szValue; /* * Create Subkey value name */ _sntprintf (szRegKey, CountOf(szRegKey), _T("%s%d"), _T("Param"), i); /* * Query subkey. */ nSize = 0; dwErrorcode = RegQueryValueEx(hParamKey, szRegKey, NULL, &nRegkeyType, NULL, &nSize); if (dwErrorcode == ERROR_SUCCESS) { if (nRegkeyType == REG_SZ || nRegkeyType == REG_EXPAND_SZ) { szValue = malloc(nSize + sizeof(szValue[0])); if (szValue) { dwErrorcode = RegQueryValueEx(hParamKey, szRegKey, NULL, &nRegkeyType, (LPBYTE)szValue, &nSize); if (dwErrorcode == ERROR_SUCCESS) { szValue[nSize] = 0; ArgArray[i] = szValue; } else { free(szValue); WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Querying registry key %s failed: error code %ld"), szRegKey, dwErrorcode); } } else WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Querying registry key %s failed: out of memory"), szRegKey); } else WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Type %ld of registry key %s is incorrect"), nRegkeyType, szRegKey); } else WriteToEventLog(EVENTLOG_ERROR_TYPE, _T("Querying registry key %s failed: error code %ld"), szRegKey, dwErrorcode); if (!ArgArray[i]) { TotalParams = ArgCount = i; break; } } } } RegCloseKey (hParamKey); } if (ArgCount == 1) { /* * No startup args are given */ ThreadInputParams.Argc = argc; ThreadInputParams.Argv = argv; } else { ThreadInputParams.Argc = ArgCount; ThreadInputParams.Argv = ArgArray; } /* * Register Service Control Handler */ hServiceStatus = RegisterServiceCtrlHandler (app_name_long, ControlHandler); if (hServiceStatus == 0) { WriteToEventLog (EVENTLOG_ERROR_TYPE, _T ("RegisterServiceCtrlHandler failed")); return; } /* * Update the service status to START_PENDING. */ UpdateServiceStatus (SERVICE_START_PENDING, NO_ERROR, SCM_WAIT_INTERVAL); /* * Start the worker thread, which does the majority of the work . */ TRY { if (SetSimpleSecurityAttributes (&SecurityAttributes) == FALSE) { WriteToEventLog (EVENTLOG_ERROR_TYPE, _T ("Couldn't init security attributes")); LEAVE; } hServiceThread = (void *) _beginthreadex (&SecurityAttributes, 0, ThreadFunction, (void *) &ThreadInputParams, 0, &threadId); if (hServiceThread == NULL) { WriteToEventLog (EVENTLOG_ERROR_TYPE, _T ("Couldn't start worker thread")); LEAVE; } /* * Set service status to SERVICE_RUNNING. */ UpdateServiceStatus (SERVICE_RUNNING, NO_ERROR, SCM_WAIT_INTERVAL); /* * Wait until the worker thread finishes. */ WaitForSingleObject (hServiceThread, INFINITE); } FINALLY { /* * Release resources */ UpdateServiceStatus (SERVICE_STOPPED, NO_ERROR, SCM_WAIT_INTERVAL); if (hServiceThread) CloseHandle (hServiceThread); FreeSecurityAttributes (&SecurityAttributes); /* * Free allocated argument list */ if (ArgCount > 1 && ArgArray != NULL) { /* * Free all strings */ for (i = 0; i < ArgCount; i++) { free (ArgArray[i]); } free (ArgArray); } } }
/* * The ServiceMain function to start service. */ VOID WINAPI ServiceMain (DWORD argc, LPTSTR argv[]) { SECURITY_ATTRIBUTES SecurityAttributes; DWORD dwThreadId; /* * Input Arguments to function startup */ DWORD ArgCount = 0; LPTSTR *ArgArray = NULL; TCHAR szRegKey[512]; TCHAR szValue[128]; DWORD nSize; HKEY hParamKey = NULL; /* To read startup parameters */ DWORD TotalParams = 0; DWORD i; InputParams ThreadInputParams; /* * Build the Input parameters to pass to worker thread */ /* * SCM sends Service Name as first arg, increment to point * arguments user specified while starting contorl agent */ /* * Read registry parameter */ ArgCount = 1; /* * Create Registry Key path */ _snprintf (szRegKey, sizeof(szRegKey), "%s%s\\%s", _T ("SYSTEM\\CurrentControlSet\\Services\\"), app_name, "Parameters"); if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ALL_ACCESS, &hParamKey) == ERROR_SUCCESS) { /* * Read startup Configuration information */ /* * Find number of subkeys inside parameters */ if (RegQueryInfoKey (hParamKey, NULL, NULL, 0, NULL, NULL, NULL, &TotalParams, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { if (TotalParams != 0) { ArgCount += TotalParams; /* * Allocate memory to hold strings */ ArgArray = (LPTSTR *) malloc (sizeof (LPTSTR) * ArgCount); /* * Copy first argument */ ArgArray[0] = _tcsdup (argv[0]); for (i = 1; i <= TotalParams; i++) { /* * Create Subkey value name */ _snprintf (szRegKey, sizeof(szRegKey), "%s%d", "Param", i); /* * Set size */ nSize = 128; RegQueryValueEx (hParamKey, szRegKey, 0, NULL, (LPBYTE) & szValue, &nSize); ArgArray[i] = _tcsdup (szValue); } } } RegCloseKey (hParamKey); } if (ArgCount == 1) { /* * No statup agrs are given */ ThreadInputParams.Argc = argc; ThreadInputParams.Argv = argv; } else { ThreadInputParams.Argc = ArgCount; ThreadInputParams.Argv = ArgArray; } /* * Register Service Control Handler */ hServiceStatus = RegisterServiceCtrlHandler (app_name, ControlHandler); if (hServiceStatus == 0) { WriteToEventLog (EVENTLOG_ERROR_TYPE, _T ("RegisterServiceCtrlHandler failed")); return; } /* * Update the service status to START_PENDING */ UpdateServiceStatus (SERVICE_START_PENDING, NO_ERROR, SCM_WAIT_INTERVAL); /* * Spin of worker thread, which does majority of the work */ TRY { if (SetSimpleSecurityAttributes (&SecurityAttributes) == FALSE) { WriteToEventLog (EVENTLOG_ERROR_TYPE, _T ("Couldn't init security attributes")); LEAVE; } hServiceThread = (void *) _beginthreadex (&SecurityAttributes, 0, ThreadFunction, (void *) &ThreadInputParams, 0, &dwThreadId); if (hServiceThread == NULL) { WriteToEventLog (EVENTLOG_ERROR_TYPE, _T ("Couldn't start worker thread")); LEAVE; } /* * Set Service Status to Running */ UpdateServiceStatus (SERVICE_RUNNING, NO_ERROR, SCM_WAIT_INTERVAL); /* * Wait for termination event and worker thread to * * spin down. */ WaitForSingleObject (hServiceThread, INFINITE); } FINALLY { /* * Release resources */ UpdateServiceStatus (SERVICE_STOPPED, NO_ERROR, SCM_WAIT_INTERVAL); if (hServiceThread) CloseHandle (hServiceThread); FreeSecurityAttributes (&SecurityAttributes); /* * Delete allocated argument list */ if (ArgCount > 1 && ArgArray != NULL) { /* * Delete all strings */ for (i = 0; i < ArgCount; i++) { free (ArgArray[i]); } free (ArgArray); } } }