使用 ctypes 实现 Windows 服务。未调用 ServiceCtrl 处理程序

Implementing a Windows Service with ctypes. ServiceCtrl handler not called

我正在尝试使用 ctypes 在 Python 中实施 Windows 服务。

import ctypes
import logging
import logging.config
import threading

import six

logger = logging.getLogger("service3")

SERVICE_MAIN = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_uint32)
HANDLER_FUNCTION = ctypes.WINFUNCTYPE(ctypes.c_uint32, ctypes.c_uint32)

class ServiceTable (ctypes.Structure):
    _fields_ = [("serviceName", ctypes.c_char_p),
                ("serviceProc", SERVICE_MAIN),
                ("serviceNameTerm", ctypes.c_char_p),
                ("serviceProcTerm", ctypes.c_void_p)]

class ServiceStatus (ctypes.Structure):
    _fields_ = [("dwServiceType", ctypes.c_uint32),
                ("dwCurrentState", ctypes.c_uint32),
                ("dwControlsAccepted", ctypes.c_uint32),
                ("dwWin32ExitCode", ctypes.c_uint32),
                ("dwServiceSpecificExitCode", ctypes.c_uint32),
                ("dwCheckPoint", ctypes.c_uint32),
                ("dwWaitHint", ctypes.c_uint32)]

advapi32 = ctypes.windll.advapi32
kernel32 = ctypes.windll.kernel32

advapi32.StartServiceCtrlDispatcherA.argtypes = [ctypes.POINTER(ServiceTable)]
StartServiceCtrlDispatcherA = advapi32.StartServiceCtrlDispatcherA

advapi32.SetServiceStatus.argtypes = [ctypes.c_void_p, ctypes.POINTER(ServiceStatus)]
SetServiceStatus = advapi32.SetServiceStatus

advapi32.RegisterServiceCtrlHandlerA.argtypes = [ctypes.c_char_p, HANDLER_FUNCTION]
advapi32.RegisterServiceCtrlHandlerA.restype = ctypes.c_void_p
RegisterServiceCtrlHandlerA = advapi32.RegisterServiceCtrlHandlerA

SERVICE_WIN32_OWN_PROCESS = 0x10

SERVICE_ACCEPT_STOP = 0x1

SERVICE_CONTROL_STOP = 0x1

SERVICE_RUNNING = 0x4
SERVICE_STOPPED = 0x1
SERVICE_STOP_PENDING = 0x3

class Service:
    serviceName = "RSJTest"
    ServiceStatusHandle = None

    def __init__(self):
        self.TerminateEvent = threading.Event()

    def StartMain(self):
        try:
            logger.debug("ServiceMain")
            self.serviceProc = SERVICE_MAIN(self.StartMain2)
            self.serviceTable = ServiceTable(serviceName=self.encode(self.serviceName), serviceProc=self.serviceProc)
            StartServiceCtrlDispatcherA(self.serviceTable)

        except:
            logger.exception("ServiceMain")

    def StartMain2(self, argC):

        try:
            logger.debug("StartMain2 %d", argC)

            self.handlerProc = HANDLER_FUNCTION(self.HandlerProc)

            self.ServiceStatusHandle = RegisterServiceCtrlHandlerA( self.encode(self.serviceName), self.handlerProc)
            logger.debug("ServiceStatusHandle %xd", self.ServiceStatusHandle)

            self.SetServiceStatus(SERVICE_RUNNING)

            self.Start()
            logger.debug("Start returned")

            self.SetServiceStatus(SERVICE_STOPPED)
        except:
            logger.exception("StartMain2")
        return 0

    def Start(self):
        self.TerminateEvent.wait()

    def Stop(self):
        self.TerminateEvent.set()

    def HandlerProc(self, dwControl):
        try:
            logger.debug("HandlerProc")

            if dwControl == SERVICE_CONTROL_STOP:
                self.SetServiceStatus(SERVICE_STOP_PENDING)

                self.Stop()

        except:
            logger.exception("HandlerProc")
        return 0

    def SetServiceStatus(self, newStatus):
        serviceStatus = ServiceStatus(dwServiceType=SERVICE_WIN32_OWN_PROCESS,
                                      dwCurrentState=newStatus,
                                      dwControlsAccepted=SERVICE_ACCEPT_STOP)

        ret = SetServiceStatus(self.ServiceStatusHandle, serviceStatus)
        logger.debug("SetServiceStatus returned %d", ret)

    def encode(self, stringParm):
        if stringParm == None:
            return None
        return six.ensure_binary(stringParm)

logConfig = {
        "version": 1,
        "disable_existing_loggers": False,
        "loggers": {
            "": {
                "level": "DEBUG",
                "handlers": ["file"]
                }
            },

        "handlers": {

            "file": {
                "class": "logging.handlers.TimedRotatingFileHandler",
                "formatter": "precise",
                "filename": "c:\temp\service.log",
                "when": "midnight",
                "backupCount": 30,
                "level": "NOTSET"
                }
            },

        "formatters": {
            "precise": {
                "format": "%(asctime)s %(levelname)-8s %(name)-15s %(message)s",
                "datefmt": "%Y-%m-%d %H:%M:%S"
            }
        }
    }

def main():
    logging.config.dictConfig(logConfig)

    logger.debug("started")
    service = Service()
    service.StartMain()

if __name__ == "__main__":
    main()

我可以毫无问题地启动我的服务(例如从服务管理器)。 我也可以停止我的服务(从服务管理器)但收到错误 1061:

注意:从 StartMain2 调用 self.handlerProc(0) 会调用 ServiceCtrlHandler

怎么了?

我在 Windows 10.

上进行了测试

在用 RegisterServiceCtrlHandlerExA 替换 RegisterServiceCtrlHandlerA 之后(并在方法调用中包含附加参数(和 HANDLER_FUNCTION),一切都按预期工作。

HANDLER_FUNCTION_EX = ctypes.WINFUNCTYPE(ctypes.c_uint32, ctypes.c_uint32, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_void_p)

advapi32.RegisterServiceCtrlHandlerExA.argtypes = [ctypes.c_char_p, HANDLER_FUNCTION_EX, ctypes.c_void_p]
advapi32.RegisterServiceCtrlHandlerExA.restype = ctypes.c_void_p
RegisterServiceCtrlHandlerExA = advapi32.RegisterServiceCtrlHandlerExA

...
        self.handlerProcEx = HANDLER_FUNCTION_EX(self.HandlerProcEx)
        self.ServiceStatusHandle = RegisterServiceCtrlHandlerExA(self.encode(self.serviceName), self.handlerProcEx, None)

...

    def HandlerProcEx(self, dwControl, dwEventType, lpEventData, lpContext):
...