3 * * Windows Service related function definitions
\r
4 * * By Raju Krishnappa(raju_krishnappa@yahoo.com)
\r
11 #include <stdio.h> /* sprintf */
\r
12 #include <process.h> /* beginthreadex */
\r
14 #include <net-snmp/library/winservice.h>
\r
18 * * External global variables used here
\r
25 * This should be decalred by the application, which wants to register as
\r
28 extern LPTSTR g_szAppName;
32 * * Declare global variable
\r
36 * Flag to indicate, whether process is running as Service
38 BOOL g_fRunningAsService = FALSE;
41 * Varibale to maintain Current Service status
43 static SERVICE_STATUS ServiceStatus;
48 static SERVICE_STATUS_HANDLE hServiceStatus = 0L;
53 SERVICE_TABLE_ENTRY ServiceTableEntry[] = {
54 \rNULL, ServiceMain, /* Service Main function */
\r
59 * Handle to Thread, to implement Pause,Resume and stop funcitonality
61 static HANDLE hServiceThread = NULL; /* Thread Handle */
64 * Holds calling partys Function Entry point, that should started
\r
65 * * when entered to service mode
\r
67 static INT(*ServiceEntryPoint) (INT Argc, LPTSTR Argv[]) = 0L;
71 * * To hold Stop Function address, to be called when STOP request
\r
72 * * recived from the SCM
\r
74 static VOID(*StopFunction) () = 0L;
78 * * To register as Windows Service with SCM(Service Control Manager)
\r
79 * * Input - Service Name, Serivce Display Name,Service Description and
\r
80 * * Service startup arguments
\r
82 \rVOID RegisterService(LPCTSTR lpszServiceName,
\rLPCTSTR lpszServiceDisplayName,
\rLPCTSTR lpszServiceDescription,
\rInputParams * StartUpArg) /* Startup argument to the service */
85 \rTCHAR szServicePath[MAX_PATH]; /* To hold module File name */
86 \rTCHAR MsgErrorString[MAX_STR_SIZE]; /* Message or Error string */
87 \rTCHAR szServiceCommand[MAX_PATH + 9]; /* Command to execute */
88 \r\rSC_HANDLE hSCManager = NULL;
89 \rSC_HANDLE hService = NULL;
90 \r\rTCHAR szRegAppLogKey[] =
91 "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
92 \rTCHAR szRegKey[512];
93 \rHKEY hKey = NULL; /* Key to registry entry */
94 \rHKEY hParamKey = NULL; /* To store startup parameters */
95 \rDWORD dwData; /* Type of logging supported */
96 \r\rDWORD i, j; /* Loop variables */
97 \r\rGetModuleFileName(NULL, szServicePath, MAX_PATH);
101 * Open Service Control Manager handle
104 OpenSCManager(NULL,
\rNULL,
\rSC_MANAGER_CREATE_SERVICE);
105 \rif (hSCManager == NULL)
107 \rDisplayError(_T("Can't open SCM"));
112 * Generate the Command to be executed by SCM
114 _stprintf(szServiceCommand, "%s %s", szServicePath,
118 * Create the Desired service
120 hService = CreateService(hSCManager,
\rlpszServiceName,
\rlpszServiceDisplayName,
\rSERVICE_ALL_ACCESS,
\rSERVICE_WIN32_OWN_PROCESS,
\rSERVICE_AUTO_START,
\rSERVICE_ERROR_NORMAL,
\rszServiceCommand,
\rNULL, /* load-order group */
\r
121 NULL, /* group member tag */
\r
122 NULL, /* dependencies */
\r
123 NULL, /* account */
\r
124 NULL); /* password */
\r
125 \rif (hService == NULL)
129 * Generate Error String
131 _stprintf(MsgErrorString, "%s %s",
132 _T("Can't Create Service"),
133 lpszServiceDisplayName);
134 \rDisplayError(MsgErrorString);
139 * Create registry entires for EventLog
142 * Create registry Application event log key
144 _tcscpy(szRegKey, szRegAppLogKey);
145 \r_tcscat(szRegKey, lpszServiceName);
148 * Create registry key
150 if (RegCreateKey(HKEY_LOCAL_MACHINE, szRegKey, &hKey) !=
153 \r_stprintf(MsgErrorString, "%s %s",
154 _T("Unable to create registry entires"),
155 lpszServiceDisplayName);
156 \rDisplayError(MsgErrorString);
161 * Add Event ID message file name to the 'EventMessageFile' subkey
163 RegSetValueEx(hKey,
\r"EventMessageFile",
\r0,
\rREG_EXPAND_SZ,
\r
164 (CONST BYTE *) szServicePath,
165 \r_tcslen(szServicePath) + sizeof(TCHAR));
168 * Set the supported types flags.
171 EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE |
172 EVENTLOG_INFORMATION_TYPE;
173 \rRegSetValueEx(hKey,
\r"TypesSupported",
\r0,
\rREG_DWORD,
174 \r(CONST BYTE *) & dwData,
\rsizeof(DWORD));
182 * Set Service Description String and save startup parameters if present
184 if (lpszServiceDescription != NULL || StartUpArg->Argc > 2)
188 * Create Registry Key path
191 _T("SYSTEM\\CurrentControlSet\\Services\\"));
192 \r_tcscat(szRegKey, g_szAppName);
199 (HKEY_LOCAL_MACHINE,
\rszRegKey,
\r0,
\rKEY_WRITE,
201 * Create and Set access
203 &hKey) != ERROR_SUCCESS)
205 \r_stprintf(MsgErrorString, "%s %s",
206 _T("Unable to create registry entires"),
207 lpszServiceDisplayName);
208 \rDisplayError(MsgErrorString);
213 * Create description subkey and the set value
215 if (lpszServiceDescription != NULL)
217 \rif (RegSetValueEx(hKey,
\r"Description",
\r0,
\rREG_SZ,
\r
218 (CONST BYTE *) lpszServiceDescription,
219 \r_tcslen(lpszServiceDescription) +
220 sizeof(TCHAR)) != ERROR_SUCCESS)
222 \r_stprintf(MsgErrorString, "%s %s",
223 _T("Unable to create registry entires"),
224 lpszServiceDisplayName);
225 \rDisplayError(MsgErrorString);
231 * Save startup arguments if they are present
233 if (StartUpArg->Argc > 2)
237 * Create Subkey parameters
240 (hKey, "Parameters", 0, NULL,
241 \rREG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
242 &hParamKey, NULL) != ERROR_SUCCESS)
244 \r_stprintf(MsgErrorString, "%s %s",
245 _T("Unable to create registry entires"),
246 lpszServiceDisplayName);
247 \rDisplayError(MsgErrorString);
256 * Loop through arguments
258 for (i = 2, j = 1; i < StartUpArg->Argc; i++, j++)
260 \r_stprintf(szRegKey, "%s%d", _T("Param"), j);
263 * Create registry key
266 (hParamKey,
\rszRegKey,
\r0,
\rREG_SZ,
267 \r(CONST BYTE *) StartUpArg->Argv[i],
268 \r_tcslen(StartUpArg->Argv[i]) +
269 sizeof(TCHAR)) != ERROR_SUCCESS)
271 \r_stprintf(MsgErrorString, "%s %s",
272 _T("Unable to create registry entires"),
273 lpszServiceDisplayName);
274 \rDisplayError(MsgErrorString);
281 * Everything is set, delete hKey
283 RegCloseKey(hParamKey);
288 * Ready to Log messages
292 * Successfully registered as service
294 _stprintf(MsgErrorString, "%s %s", lpszServiceName,
295 _T("- Successfully registered as Service"));
298 * Log message to eventlog
300 WriteToEventLog(EVENTLOG_INFORMATION_TYPE, MsgErrorString);
301 \r\rMessageBox(NULL,
\rMsgErrorString,
\rg_szAppName,
302 \rMB_ICONINFORMATION);
306 CloseServiceHandle(hSCManager);
308 CloseServiceHandle(hService);
312 RegCloseKey(hParamKey);
319 * * Unregister the service with the Windows SCM
\r
320 * * Input - ServiceName
\r
323 \rVOID UnregisterService(LPCSTR lpszServiceName)
\r
325 \rTCHAR MsgErrorString[MAX_STR_SIZE]; /* Message or Error string */
326 \r\rSC_HANDLE hSCManager = NULL; /* SCM handle */
327 \rSC_HANDLE hService = NULL; /* Service Handle */
328 \r\rSERVICE_STATUS sStatus;
329 \rTCHAR szRegAppLogKey[] =
330 "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
331 \rTCHAR szRegKey[512];
332 \rHKEY hKey = NULL; /* Key to registry entry */
336 * Open Service Control Manager
339 OpenSCManager(NULL,
\rNULL,
\rSC_MANAGER_CREATE_SERVICE);
340 \rif (hSCManager == NULL)
344 * Error while opening SCM
346 \rMessageBox(NULL,
\r_T("Can't open SCM"),
\rg_szAppName,
352 * Open registered service
355 OpenService(hSCManager,
\rlpszServiceName,
\rSERVICE_ALL_ACCESS);
356 \r\rif (hService == NULL)
358 \r_stprintf(MsgErrorString, "%s %s", _T("Can't open service"),
360 \rMessageBox(NULL,
\rMsgErrorString,
\rg_szAppName,
\rMB_ICONHAND);
365 * Query service status
368 * If running stop before deleting
370 if (QueryServiceStatus(hService, &sStatus))
372 \rif (sStatus.dwCurrentState == SERVICE_RUNNING
373 ||
\rsStatus.dwCurrentState == SERVICE_PAUSED)
377 * Shutdown the service
379 ControlService(hService, SERVICE_CONTROL_STOP,
387 if (DeleteService(hService) == FALSE)
389 \r_stprintf(MsgErrorString, "%s %s", _T("Can't delete service"),
391 \rMessageBox(NULL,
\rMsgErrorString,
\rg_szAppName,
\rMB_ICONHAND);
394 * Log message to eventlog
396 WriteToEventLog(EVENTLOG_INFORMATION_TYPE, MsgErrorString);
401 * Service deleted successfully
403 _stprintf(MsgErrorString, "%s %s", lpszServiceName,
404 _T("- Service deleted"));
407 * Log message to eventlog
409 WriteToEventLog(EVENTLOG_INFORMATION_TYPE, MsgErrorString);
412 * Delete registry entires for EventLog
414 _tcscpy(szRegKey, szRegAppLogKey);
415 \r_tcscat(szRegKey, lpszServiceName);
416 \rRegDeleteKey(HKEY_LOCAL_MACHINE, szRegKey);
417 \r\rMessageBox(NULL,
\rMsgErrorString,
\rg_szAppName,
418 \rMB_ICONINFORMATION);
426 CloseServiceHandle(hService);
428 CloseServiceHandle(hSCManager);
435 * * To write message to Windows Event log
\r
436 * * Input - Event Type, Message string
\r
439 \rVOID WriteToEventLog(WORD wType, LPCTSTR pszFormat,...)
\r
441 \rTCHAR szMessage[512];
444 \rHANDLE hEventSource = NULL;
445 \r\rva_start(ArgList, pszFormat);
446 \r_vstprintf(szMessage, pszFormat, ArgList);
448 \r\rLogStr[0] = szMessage;
449 \r\r\rhEventSource = RegisterEventSource(NULL, g_szAppName);
450 \rif (hEventSource == NULL)
452 \r\rReportEvent(hEventSource,
\rwType,
\r0,
\rDISPLAY_MSG, /* To Just output the text to event log */
\r
453 NULL,
\r1,
\r0,
\rLogStr,
\rNULL);
454 \r\rDeregisterEventSource(hEventSource);
455 \r\rif (!g_fRunningAsService)
459 * We are running in command mode, output the string
468 * * Handle command-line arguments from the user.
\r
469 * * Serivce related options are:
\r
470 * * -register - registers the service
\r
471 * * -unregister - unregisters the service
\r
472 * * -service - run as serivce
\r
473 * * other command-line arguments are unaltered/ignored.
\r
474 * * They should supplied as first arguments(other wise they will be ignored
\r
475 * * Return: Type indicating the option specified
\r
477 \rINT ParseCmdLineForServiceOption(int argc, TCHAR * argv[])
\r
479 \rint nReturn = RUN_AS_CONSOLE; /* Defualted to run as console */
484 * second argument present
486 if (lstrcmpi(_T("-register"), argv[1]) == 0)
488 \rnReturn = REGISTER_SERVICE;
491 else if (lstrcmpi(_T("-unregister"), argv[1]) == 0)
493 \rnReturn = UN_REGISTER_SERVICE;
496 else if (lstrcmpi(_T("-service"), argv[1]) == 0)
498 \rnReturn = RUN_AS_SERVICE;
507 * * To Display an error message describing the last system error
\r
508 * * message, along with a title passed as a parameter.
\r
510 VOID DisplayError(LPCTSTR pszTitle)
\r
517 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
518 \rFORMAT_MESSAGE_FROM_SYSTEM,
\rNULL,
\rGetLastError(),
519 \rMAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
520 \r(LPTSTR) & pErrorMsg,
\r0,
\rNULL);
521 \rif (g_fRunningAsService != FALSE)
523 \rWriteToEventLog(EVENTLOG_ERROR_TYPE, pErrorMsg);
528 \rMessageBox(NULL, pErrorMsg, pszTitle, MB_ICONHAND);
530 \r\rLocalFree(pErrorMsg);
536 * * To update current service status
\r
537 * * Sends the current service status to the SCM. Also updates
\r
538 * * the global service status structure.
\r
541 UpdateServiceStatus(DWORD dwStatus,
\rDWORD dwErrorCode,
542 \rDWORD dwWaitHint)
\r
544 \rBOOL fReturn = FALSE;
545 \rDWORD static dwCheckpoint = 1;
547 SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
548 \r\rif (g_fRunningAsService == FALSE)
550 \r\rZeroMemory(&ServiceStatus, sizeof(ServiceStatus));
551 \rServiceStatus.dwServiceType = SERVICE_WIN32;
552 \rServiceStatus.dwCurrentState = dwStatus;
553 \rServiceStatus.dwWaitHint = dwWaitHint;
556 \rServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
557 \rServiceStatus.dwServiceSpecificExitCode = dwErrorCode;
561 * special cases that depend on the new state
565 \rcase SERVICE_START_PENDING:
568 \r\rcase SERVICE_RUNNING:
569 \rcase SERVICE_STOPPED:
573 \rServiceStatus.dwCheckPoint = dwCheckpoint++;
574 \rServiceStatus.dwControlsAccepted = dwControls;
575 \r\rreturn ReportCurrentServiceStatus();
581 * * Reports current Service status to SCM
\r
584 ReportCurrentServiceStatus()
\r
586 \rreturn SetServiceStatus(hServiceStatus, &ServiceStatus);
592 * * The ServiceMain function to start service.
\r
594 VOID WINAPI ServiceMain(DWORD argc, LPTSTR argv[])
\r
596 \r\rSECURITY_ATTRIBUTES SecurityAttributes;
600 * Input Arguments to function startup
603 \rLPTSTR * ArgArray = NULL;
604 \rTCHAR szRegKey[512];
605 \rTCHAR szValue[128];
607 \rHKEY hParamKey = NULL; /* To read startup parameters */
608 \rDWORD TotalParams = 0;
610 \rInputParams ThreadInputParams;
613 * Build the Input parameters to pass to thread
617 * SCM sends Service Name as first arg, increment to point
\r
618 * * arguments user specified while starting contorl agent
\r
622 * Read registry parameter
625 * Initialize count to 1
630 * Create Registry Key path
632 _stprintf(szRegKey, "%s%s\\%s",
633 _T("SYSTEM\\CurrentControlSet\\Services\\"), g_szAppName,
636 (HKEY_LOCAL_MACHINE, szRegKey, 0, KEY_ALL_ACCESS,
637 &hParamKey) == ERROR_SUCCESS)
641 * Read startup Configuration information
644 * Find number of subkeys inside parameters
646 \rif (RegQueryInfoKey
647 (hParamKey, NULL, NULL, 0, NULL, NULL, NULL, &TotalParams,
648 NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
650 \rif (TotalParams != 0)
652 \rArgCount += TotalParams;
655 * Allocate memory to hold strings
658 (LPTSTR *) malloc(sizeof(LPTSTR) * ArgCount);
661 * Copy first argument
663 ArgArray[0] = _tcsdup(argv[0]);
664 \r\rfor (i = 1; i <= TotalParams; i++)
668 * Create Subkey value name
670 _stprintf(szRegKey, "%s%d", "Param", i);
676 \rRegQueryValueEx(hParamKey, szRegKey, 0, NULL,
677 (LPBYTE) & szValue, &nSize);
678 \rArgArray[i] = _tcsdup(szValue);
682 \rRegCloseKey(hParamKey);
684 \r\r\rif (ArgCount == 1)
688 * No statup agrs are given
690 ThreadInputParams.Argc = argc;
691 \rThreadInputParams.Argv = argv;
696 \rThreadInputParams.Argc = ArgCount;
697 \rThreadInputParams.Argv = ArgArray;
701 * Register Serivce Control Handler
704 RegisterServiceCtrlHandler(g_szAppName,
\rControlHandler);
705 \rif (hServiceStatus == 0)
707 \rWriteToEventLog(EVENTLOG_ERROR_TYPE,
708 \r_T("RegisterServiceCtrlHandler failed"));
713 * Update the service status to START_PENDING
715 UpdateServiceStatus(SERVICE_START_PENDING,
\rNO_ERROR,
716 \rSCM_WAIT_INTERVAL);
719 * Spin of worker thread, which does majority of the work
722 \rif (SetSimpleSecurityAttributes(&SecurityAttributes) == FALSE)
724 \rWriteToEventLog(EVENTLOG_ERROR_TYPE,
725 \r_T("Couldn't init security attributes"));
729 (void *) _beginthreadex(&SecurityAttributes,
\r0,
731 \r(void *) &ThreadInputParams,
\r0,
733 \rif (hServiceThread == NULL)
735 \rWriteToEventLog(EVENTLOG_ERROR_TYPE,
736 \r_T("Couldn't start worker thread"));
741 * Set Service Status to Running
743 UpdateServiceStatus(SERVICE_RUNNING,
\rNO_ERROR,
744 \rSCM_WAIT_INTERVAL);
747 * Wait for termination event and worker thread to
\r
750 \rWaitForSingleObject(hServiceThread, INFINITE);
757 UpdateServiceStatus(SERVICE_STOPPED,
\rNO_ERROR,
758 \rSCM_WAIT_INTERVAL);
759 \r\rif (hServiceThread)
760 \rCloseHandle(hServiceThread);
761 \rFreeSecurityAttributes(&SecurityAttributes);
764 * Delete allocated argument list
766 if (ArgCount > 1 && ArgArray != NULL)
772 for (i = 0; i < ArgCount; i++)
784 * * Function to start as Windows service
\r
785 * * The calling party should specify their entry point as input parameter
\r
786 * * Returns TRUE if the Service is started successfully
\r
788 \rBOOL RunAsService(INT(*ServiceFunction) (INT, LPTSTR *))
\r
792 * Set the ServiceEntryPoint
794 ServiceEntryPoint = ServiceFunction;
797 * By default, mark as Running as a service
799 g_fRunningAsService = TRUE;
802 * Initialize ServiceTableEntry table
804 ServiceTableEntry[0].lpServiceName = g_szAppName; /* Application Name */
807 * Call SCM via StartServiceCtrlDispatcher to run as Service
\r
808 * * If the function returns TRUE we are running as Service,
\r
810 \rif (StartServiceCtrlDispatcher(ServiceTableEntry) == FALSE)
812 \rg_fRunningAsService = FALSE;
815 * Some other error has occurred.
817 WriteToEventLog(EVENTLOG_ERROR_TYPE,
818 \r_T("Couldn't start service - %s"),
821 \r\rreturn g_fRunningAsService;
827 * * Service control handler function
\r
828 * * Responds to SCM commands/requests
\r
829 * * The service handles 4 commands
\r
830 * * commands - interrogate,pause, continue and stop.
\r
832 VOID WINAPI ControlHandler(DWORD dwControl)
\r
836 \rcase SERVICE_CONTROL_STOP:
837 \rProcessServiceStop(); /* To stop the service */
839 \rcase SERVICE_CONTROL_INTERROGATE:
840 \rProcessServiceInterrogate(); /* Report Current state of the Service */
842 \rcase SERVICE_CONTROL_PAUSE:
843 \rProcessServicePause(); /* To puase service */
845 \rcase SERVICE_CONTROL_CONTINUE:
846 \rProcessServiceContinue(); /* To continue Service */
854 * * To stop the service. This invokes registered
\r
855 * * stop function to stop the service(gracefull exit)
\r
856 * * After stopping, Service status is set to STOP in
\r
859 VOID ProcessServiceStop(VOID)
\r
861 \rUpdateServiceStatus(SERVICE_STOP_PENDING,
\rNO_ERROR,
862 \rSCM_WAIT_INTERVAL);
865 * Invoke registered Stop funciton
867 if (StopFunction != NULL)
869 \r(*StopFunction) ();
876 * There is no registered stop function, so terminate the thread
878 TerminateThread(hServiceThread, 0);
885 * * Returns the current state of the service to the SCM.
\r
887 VOID ProcessServiceInterrogate(VOID)
\r
889 \rReportCurrentServiceStatus();
895 * * To Create a security descriptor with a NULL ACL, which
\r
896 * * allows unlimited access. Returns a SECURITY_ATTRIBUTES
\r
897 * * structure that contains the security descriptor.
\r
898 * * The structure contains a dynamically allocated security
\r
899 * * descriptor that must be freed; either manually, or by
\r
900 * * calling FreeSecurityAttributes
\r
902 BOOL SetSimpleSecurityAttributes(SECURITY_ATTRIBUTES * pSecurityAttr)
\r
904 \rBOOL fReturn = FALSE;
905 \rSECURITY_DESCRIPTOR * pSecurityDesc = NULL;
908 * If an invalid address passed as a parameter, return
\r
909 * * FALSE right away.
\r
911 \rif (!pSecurityAttr)
914 (SECURITY_DESCRIPTOR *) LocalAlloc(LPTR,
915 \rSECURITY_DESCRIPTOR_MIN_LENGTH);
916 \rif (!pSecurityDesc)
919 InitializeSecurityDescriptor(pSecurityDesc,
920 \rSECURITY_DESCRIPTOR_REVISION);
921 \rif (fReturn != FALSE)
924 SetSecurityDescriptorDacl(pSecurityDesc, TRUE, NULL, FALSE);
926 \r\rif (fReturn != FALSE)
928 \rpSecurityAttr->nLength = sizeof(SECURITY_ATTRIBUTES);
929 \rpSecurityAttr->lpSecurityDescriptor = pSecurityDesc;
930 \rpSecurityAttr->bInheritHandle = TRUE;
937 * Couldn't initialize or set security descriptor.
939 LocalFree(pSecurityDesc);
947 * * This funciton Frees the security descriptor owned by a SECURITY_ATTRIBUTES
\r
950 VOID FreeSecurityAttributes(SECURITY_ATTRIBUTES * pSecurityAttr)
\r
952 \rif (pSecurityAttr && pSecurityAttr->lpSecurityDescriptor)
953 \rLocalFree(pSecurityAttr->lpSecurityDescriptor);
959 * * This function is spawn as thread.
\r
960 * * Invokes registered service function
\r
961 * * Returns when called registered function returns
\r
963 DWORD WINAPI ThreadFunction(LPVOID lpParam)
\r
967 * lpParam contains argc and argv, pass to service main function
971 * Declare pointer to InputParams
973 InputParams * pInputArg;
974 \rpInputArg = (InputParams *) lpParam;
975 \rreturn (*ServiceEntryPoint) (pInputArg->Argc, pInputArg->Argv);
981 * * To register STOP function with the framework
\r
982 * * This function will be inovked when SCM sends
\r
985 \rVOID RegisterStopFunction(void (*StopFunc) ())
\r
987 \rStopFunction = StopFunc;
992 * * To Pause the service whec SCM sends pause command
\r
993 * * Invokes PauseThread on worker Thread handle, only
\r
994 * * when Service status is Running
\r
996 VOID ProcessServicePause(VOID)
\r
998 \r\rif (ServiceStatus.dwCurrentState == SERVICE_RUNNING)
1000 \rUpdateServiceStatus(SERVICE_PAUSE_PENDING,
\rNO_ERROR,
1001 \rSCM_WAIT_INTERVAL);
1004 * Invoke Thread pause on ThreadHandle
1006 if (SuspendThread(hServiceThread) != -1)
1008 \rUpdateServiceStatus(SERVICE_PAUSED,
\rNO_ERROR,
1009 \rSCM_WAIT_INTERVAL);
1017 * * To Continue paused service
\r
1018 * * Invoke ResumeThread, if thread is paused
\r
1020 VOID ProcessServiceContinue(VOID)
\r
1022 \r\rif (ServiceStatus.dwCurrentState == SERVICE_PAUSED)
1024 \rUpdateServiceStatus(SERVICE_CONTINUE_PENDING,
\rNO_ERROR,
1025 \rSCM_WAIT_INTERVAL);
1028 * Invoke Thread pause on ThreadHandle
1030 if (ResumeThread(hServiceThread) != -1)
1032 \rUpdateServiceStatus(SERVICE_RUNNING,
\rNO_ERROR,
1033 \rSCM_WAIT_INTERVAL);