Commits

Anthony Tuininga committed ddbc5ba

Added support for monitoring session changes in a service.

  • Participants
  • Parent commits 2cf52b3

Comments (0)

Files changed (1)

File source/bases/Win32Service.c

 #define CX_SERVICE_DISPLAY_NAME         "DISPLAY_NAME"
 #define CX_SERVICE_DESCRIPTION          "DESCRIPTION"
 #define CX_SERVICE_AUTO_START           "AUTO_START"
+#define CX_SERVICE_SESSION_CHANGES      "SESSION_CHANGES"
 
 // the following was copied from cx_Interface.c, which is where this
 // declaration normally happens
     PyObject *displayNameFormat;
     PyObject *description;
     DWORD startType;
+    int sessionChanges;
 } udt_ServiceInfo;
 
 // define globals
 //   Called when an attempt to initialize the module zip fails.
 //-----------------------------------------------------------------------------
 static int FatalError(
-    const char *message)		// message to print
+    const char *message)		        // message to print
 {
     return LogPythonException(message);
 }
 //   Set the status for the service.
 //-----------------------------------------------------------------------------
 static int Service_SetStatus(
-    DWORD status)			// status to set
+    udt_ServiceInfo* info,              // service information
+    DWORD status)			            // status to set
 {
     SERVICE_STATUS serviceStatus;
 
     serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
     serviceStatus.dwCurrentState = status;
     serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+    if (info->sessionChanges)
+        serviceStatus.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE;
     serviceStatus.dwWin32ExitCode = 0;
     serviceStatus.dwServiceSpecificExitCode = 0;
     serviceStatus.dwCheckPoint = 0;
 // the main thread is ended or the control GUI does not understand that the
 // service has ended.
 //-----------------------------------------------------------------------------
-static int Service_Stop()
+static int Service_Stop(
+    udt_ServiceInfo* info)              // service information
 {
     PyThreadState *threadState;
     PyObject *result;
 
     // indicate that the service is being stopped
-    if (Service_SetStatus(SERVICE_STOP_PENDING) < 0)
+    if (Service_SetStatus(info, SERVICE_STOP_PENDING) < 0)
         return LogWin32Error(GetLastError(), "cannot set service as stopping");
 
     // create event for the main thread to wait on for the control thread
     PyThreadState_Delete(threadState);
 
     // indicate that the service has stopped
-    if (Service_SetStatus(SERVICE_STOPPED) < 0)
+    if (Service_SetStatus(info, SERVICE_STOPPED) < 0)
         return LogWin32Error(GetLastError(), "cannot set service as stopped");
 
     // now set the control event
 
 
 //-----------------------------------------------------------------------------
+// Service_SessionChange()
+//   Called when a session has changed.
+//-----------------------------------------------------------------------------
+static int Service_SessionChange(
+    DWORD sessionId,                    // session that has changed
+    DWORD eventType)                    // event type
+{
+    PyThreadState *threadState;
+    PyObject *result;
+
+    // create a new Python thread and acquire the global interpreter lock
+    threadState = PyThreadState_New(gInterpreterState);
+    if (!threadState)
+        return LogPythonException("unable to create new thread state");
+    PyEval_AcquireThread(threadState);
+
+    // call Python method
+    result = PyObject_CallMethod(gInstance, "SessionChanged", "ii",
+            sessionId, eventType);
+    if (!result)
+        return LogPythonException("exception calling SessionChanged method");
+    Py_DECREF(result);
+
+    // destroy the Python thread and release the global interpreter lock
+    PyThreadState_Clear(threadState);
+    PyEval_ReleaseThread(threadState);
+    PyThreadState_Delete(threadState);
+
+    return 0;
+}
+
+
+//-----------------------------------------------------------------------------
 // Service_Control()
 //   Function for controlling a service. Note that the controlling thread
 // must be ended before the main thread is ended or the control GUI does not
 // understand that the service has ended.
 //-----------------------------------------------------------------------------
-static void WINAPI Service_Control(
-    DWORD controlCode)			// control code
+static DWORD WINAPI Service_Control(
+    DWORD controlCode,			        // control code
+    DWORD eventType,                    // event type
+    LPVOID eventData,                   // event data
+    LPVOID context)                     // context
 {
-    if (controlCode == SERVICE_CONTROL_STOP)
-        Service_Stop();
+    udt_ServiceInfo *serviceInfo = (udt_ServiceInfo*) context;
+    WTSSESSION_NOTIFICATION *sessionInfo;
+
+    switch (controlCode) {
+        case SERVICE_CONTROL_STOP:
+            Service_Stop(serviceInfo);
+            break;
+        case SERVICE_CONTROL_SESSIONCHANGE:
+            sessionInfo = (WTSSESSION_NOTIFICATION*) eventData;
+            Service_SessionChange(sessionInfo->dwSessionId, eventType);
+            break;
+    }
+
+    return NO_ERROR;
 }
 
 
 //   Initialize logging for the service.
 //-----------------------------------------------------------------------------
 static int Service_StartLogging(
-    const char *fileName)		// name of file for defaults
+    const char *fileName)		        // name of file for defaults
 {
     char defaultLogFileName[PATH_MAX + 1], logFileName[PATH_MAX + 1];
     unsigned logLevel, maxFiles, maxFileSize;
     threadState = PyThreadState_Swap(NULL);
     if (!threadState) {
         LogMessage(LOG_LEVEL_ERROR, "cannot set up interpreter state");
-        Service_SetStatus(SERVICE_STOPPED);
+        Service_SetStatus(info, SERVICE_STOPPED);
         return -1;
     }
     gInterpreterState = threadState->interp;
     else if (temp == Py_True)
         info->startType = SERVICE_AUTO_START;
 
+    // determine if service should monitor session changes (optional)
+    info->sessionChanges = 0;
+    temp = PyObject_GetAttrString(module, CX_SERVICE_SESSION_CHANGES);
+    if (!temp)
+        PyErr_Clear();
+    else if (temp == Py_True)
+        info->sessionChanges = 1;
+
     // import the module which implements the service
     temp = PyObject_GetAttrString(module, CX_SERVICE_MODULE_NAME);
     if (!temp)
 
     // run the service
     LogMessage(LOG_LEVEL_INFO, "starting up service");
-    if (Service_SetStatus(SERVICE_RUNNING) < 0)
+    if (Service_SetStatus(info, SERVICE_RUNNING) < 0)
         return LogWin32Error(GetLastError(), "cannot set service as started");
     temp = PyObject_CallMethod(gInstance, "Run", NULL);
     if (!temp)
 //   Main routine for the service.
 //-----------------------------------------------------------------------------
 static void WINAPI Service_Main(
-    int argc,				// number of arguments
-    char **argv)			// argument values
+    int argc,				            // number of arguments
+    char **argv)			            // argument values
 {
     udt_ServiceInfo info;
 
 
     // register the control function
     LogMessage(LOG_LEVEL_DEBUG, "registering control function");
-    gServiceHandle = RegisterServiceCtrlHandler("", Service_Control);
+    gServiceHandle = RegisterServiceCtrlHandlerEx("", Service_Control, &info);
     if (!gServiceHandle) {
         LogWin32Error(GetLastError(),
                 "cannot register service control handler");
 
     // run the service
     if (Service_Run(&info) < 0) {
-        Service_SetStatus(SERVICE_STOPPED);
+        Service_SetStatus(&info, SERVICE_STOPPED);
         return;
     }
 
     // otherwise, the service terminated normally by some other means
     } else {
         LogMessage(LOG_LEVEL_INFO, "stopping service (internally)");
-        Service_SetStatus(SERVICE_STOPPED);
+        Service_SetStatus(&info, SERVICE_STOPPED);
     }
 }