I developed a python service using win32serviceutil.ServiceFramework and PythonService.exe. If the service crashes (eg, an unhandled exception is raised) I want the service to restart. So, I configured the service to restart using `ChangeServiceConfig2`. Code looks like this.
``` import win32serviceutil import win32service import os from . import makeWin32Service win32serviceutil.HandleCommandLine(makeWin32Service.PreVeilService) service_name = os.environ.get("PV_SERVICE_NAME") win32service.ChangeServiceConfig2 hscm = win32service.OpenSCManager(None,None,win32service.SC_MANAGER_ALL_ACCESS) try: hs = win32service.OpenService(hscm, service_name, win32service.SERVICE_ALL_ACCESS) try: win32service.ChangeServiceConfig2(hs, win32service.SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, True) service_failure_actions = { 'ResetPeriod': 60*60, # Time in seconds after which to reset the failure count to zero. 'RebootMsg': u'', # Not using reboot option 'Command': u'', # Not using run-command option 'Actions': [ (win32service.SC_ACTION_RESTART, 1000*5), # first action after failure, delay in ms (win32service.SC_ACTION_RESTART, 1000*5), # second action after failure (win32service.SC_ACTION_NONE, 0) # subsequent actions after failure ] } win32service.ChangeServiceConfig2(hs, win32service.SERVICE_CONFIG_FAILURE_ACTIONS, service_failure_actions) except (win32service.error, NotImplementedError): print "ChangeServiceConfig2 failed to set restart behavior" finally: win32service.CloseServiceHandle(hs) finally: win32service.CloseServiceHandle(hscm) ``` This does not work with vanilla pywin32. From the MSDN page for `SERVICE_CONFIG_FAILURE_ACTIONS_FLAG`: "If this member [fFailureActionsOnNonCrashFailures] is TRUE and the service has configured failure actions, the failure actions are queued if the service process terminates without reporting a status of SERVICE_STOPPED or if it enters the SERVICE_STOPPED state but the dwWin32ExitCode member of the SERVICE_STATUS structure is not ERROR_SUCCESS (0). If this member is FALSE and the service has configured failure actions, the failure actions are queued only if the service terminates without reporting a status of SERVICE_STOPPED. This setting is ignored unless the service has configured failure actions. For information on configuring failure actions, see ChangeServiceConfig2." In order for the process to restart PythonService.exe needs to NOT set status to SERVICE_STOPPED or set dwWin32ExitCode to some non zero value. These conditions are not met in PythonService.cpp: ``` void WINAPI service_main(DWORD dwArgc, LPTSTR *lpszArgv) { // ... start = PyObject_GetAttrString(instance, "SvcRun"); if (start==NULL) ReportPythonError(E_PYS_NO_RUN_METHOD); else { // Call the Python service entry point - when this returns, the // service has stopped! PyObject *result = PyObject_CallObject(start, NULL); if (result==NULL) ReportPythonError(E_PYS_START_FAILED); else Py_DECREF(result); } // We are all done. cleanup: // try to report the stopped status to the service control manager. Py_XDECREF(start); Py_XDECREF(instance); if (pe && pe->sshStatusHandle) { // Wont be true if debugging. if (!SetServiceStatus( pe->sshStatusHandle, &stoppedStatus )) ReportAPIError(PYS_E_API_CANT_SET_STOPPED); } return; } ``` Presumably we should do something with the return value from `SvcRun` then conditionally report an error with dwWin32ExitCode. Here is my patch that gives restarts when `SvcRun` throws an exception. The ability to let Windows handle service restarts is important; I would like to get this fixed in upstream. ``` --- win32/src/PythonService.cpp 2017-01-07 14:58:48.156762600 -0500 +++ win32/src/PythonService.cpp 2017-01-09 03:11:08.821727300 -0500 @@ -144,6 +144,15 @@ 0, // dwCheckPoint; 5000 }; +SERVICE_STATUS stoppedErrorStatus = { + SERVICE_WIN32_OWN_PROCESS, + SERVICE_STOPPED, + 0, // dwControlsAccepted, + ERROR_SERVICE_SPECIFIC_ERROR, // dwWin32ExitCode; + 1, // dwServiceSpecificExitCode; + 0, // dwCheckPoint; + 5000 }; + SERVICE_STATUS startingStatus = { SERVICE_WIN32_OWN_PROCESS, SERVICE_START_PENDING, @@ -916,9 +925,13 @@ // Call the Python service entry point - when this returns, the // service has stopped! PyObject *result = PyObject_CallObject(start, NULL); - if (result==NULL) + if (result==NULL) { ReportPythonError(E_PYS_START_FAILED); - else + if (pe && pe->sshStatusHandle) { // Wont be true if debugging. + SetServiceStatus( pe->sshStatusHandle, &stoppedErrorStatus ); + pe->sshStatusHandle = 0; // reset so we don't attempt to set 'stopped' + } + } else Py_DECREF(result); } // We are all done. ``` _______________________________________________ python-win32 mailing list python-win32@python.org https://mail.python.org/mailman/listinfo/python-win32