mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
192 lines
6.4 KiB
Python
192 lines
6.4 KiB
Python
import ctypes
|
|
import windows
|
|
|
|
from collections import namedtuple
|
|
from contextlib import contextmanager
|
|
|
|
from windows import utils
|
|
from windows.pycompat import int_types
|
|
import windows.generated_def as gdef
|
|
from windows.generated_def import *
|
|
from windows import security
|
|
from windows.pycompat import basestring
|
|
|
|
"""
|
|
``type`` might be one of:
|
|
|
|
* ``SERVICE_KERNEL_DRIVER(0x1L)``
|
|
* ``SERVICE_FILE_SYSTEM_DRIVER(0x2L)``
|
|
* ``SERVICE_WIN32_OWN_PROCESS(0x10L)``
|
|
* ``SERVICE_WIN32_SHARE_PROCESS(0x20L)``
|
|
* ``SERVICE_INTERACTIVE_PROCESS(0x100L)``
|
|
|
|
``state`` might be one of:
|
|
|
|
* ``SERVICE_STOPPED(0x1L)``
|
|
* ``SERVICE_START_PENDING(0x2L)``
|
|
* ``SERVICE_STOP_PENDING(0x3L)``
|
|
* ``SERVICE_RUNNING(0x4L)``
|
|
* ``SERVICE_CONTINUE_PENDING(0x5L)``
|
|
* ``SERVICE_PAUSE_PENDING(0x6L)``
|
|
* ``SERVICE_PAUSED(0x7L)``
|
|
|
|
``flags`` might be one of:
|
|
|
|
* ``0``
|
|
* ``SERVICE_RUNS_IN_SYSTEM_PROCESS(0x1L)``
|
|
|
|
"""
|
|
|
|
|
|
class ServiceManager(utils.AutoHandle):
|
|
"""An object to query, list and explore services"""
|
|
def _get_handle(self):
|
|
return windows.winproxy.OpenSCManagerA(dwDesiredAccess=gdef.MAXIMUM_ALLOWED)
|
|
|
|
def open_service(self, name, access=gdef.MAXIMUM_ALLOWED):
|
|
return windows.winproxy.OpenServiceA(self.handle, name, access) # Check service exists :)
|
|
|
|
def get_service(self, key, access=gdef.MAXIMUM_ALLOWED):
|
|
"""Get a service by its name/index or a list of services via a slice
|
|
|
|
:return: :class:`Service` or [:class:`Service`] -- A :class:`Service` or list of :class:`Service`
|
|
"""
|
|
if isinstance(key, int_types):
|
|
return self.enumerate_services()[key]
|
|
if isinstance(key, slice):
|
|
# Get service list
|
|
servlist = self.enumerate_services()
|
|
# Extract indexes matching the slice
|
|
indexes = key.indices(len(servlist))
|
|
return [servlist[idx] for idx in range(*indexes)]
|
|
# Retrieve service by its name
|
|
handle = self.open_service(key, access)
|
|
return Service(name=key, handle=handle)
|
|
|
|
__getitem__ = get_service
|
|
"""Get a service by its name/index or a list of services via a slice
|
|
|
|
:return: :class:`Service` or [:class:`Service`] -- A :class:`Service` or list of :class:`Service`
|
|
"""
|
|
|
|
def get_service_display_name(self, name):
|
|
# This API is strange..
|
|
# Why can't we retrieve the display name for a service handle ?
|
|
BUFFER_SIZE = 0x1000
|
|
result = (CHAR * BUFFER_SIZE)()
|
|
size_needed = gdef.DWORD(BUFFER_SIZE)
|
|
windows.winproxy.GetServiceDisplayNameA(self.handle, name, result, size_needed)
|
|
return result.value
|
|
|
|
def _enumerate_services_generator(self):
|
|
"""The generator code behind __iter__.
|
|
Allow to iter over the services on the system
|
|
"""
|
|
size_needed = gdef.DWORD()
|
|
nb_services = gdef.DWORD()
|
|
counter = gdef.DWORD()
|
|
try:
|
|
windows.winproxy.EnumServicesStatusExA(self.handle, SC_ENUM_PROCESS_INFO, SERVICE_TYPE_ALL, SERVICE_STATE_ALL, None, 0, ctypes.byref(size_needed), ctypes.byref(nb_services), byref(counter), None)
|
|
except WindowsError:
|
|
pass
|
|
|
|
while True:
|
|
size = size_needed.value
|
|
buffer = (BYTE * size)()
|
|
try:
|
|
windows.winproxy.EnumServicesStatusExA(self.handle, SC_ENUM_PROCESS_INFO, SERVICE_TYPE_ALL, SERVICE_STATE_ALL, buffer, size, ctypes.byref(size_needed), ctypes.byref(nb_services), byref(counter), None)
|
|
except WindowsError as e:
|
|
continue
|
|
break
|
|
services_array = (gdef.ENUM_SERVICE_STATUS_PROCESSA * nb_services.value).from_buffer(buffer)
|
|
for service_info in services_array:
|
|
shandle = self.open_service(service_info.lpServiceName)
|
|
yield Service(handle=shandle, name=service_info.lpServiceName, description=service_info.lpDisplayName)
|
|
return
|
|
|
|
__iter__ = _enumerate_services_generator
|
|
"""Iter over the services on the system
|
|
|
|
:yield: :class:`Service`
|
|
"""
|
|
|
|
def enumerate_services(self):
|
|
return list(self._enumerate_services_generator())
|
|
|
|
|
|
class Service(gdef.SC_HANDLE):
|
|
"""Represent a service on the system"""
|
|
def __init__(self, handle, name, description=None):
|
|
super(Service, self).__init__(handle)
|
|
self.name = name
|
|
"""The name of the service
|
|
|
|
:type: :class:`str`
|
|
"""
|
|
if description is not None:
|
|
self._description = description # Setup fixedpropety
|
|
|
|
@property
|
|
def description(self):
|
|
"""The description of the service
|
|
|
|
:type: :class:`str`
|
|
"""
|
|
return ServiceManager().get_service_display_name(self.name)
|
|
|
|
@property
|
|
def status(self):
|
|
"""The status of the service
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.SERVICE_STATUS_PROCESS`
|
|
"""
|
|
buffer = windows.utils.BUFFER(gdef.SERVICE_STATUS_PROCESS)()
|
|
size_needed = gdef.DWORD()
|
|
windows.winproxy.QueryServiceStatusEx(self, gdef.SC_STATUS_PROCESS_INFO, buffer.cast(gdef.LPBYTE), ctypes.sizeof(buffer), size_needed)
|
|
return buffer[0]
|
|
|
|
@property # Can change if service is started/stopped when the object exist
|
|
def process(self):
|
|
"""The process running the service (if any)
|
|
|
|
:type: :class:`WinProcess <windows.winobject.process.WinProcess>` or ``None``
|
|
"""
|
|
pid = self.status.dwProcessId
|
|
if not pid:
|
|
return None
|
|
l = windows.WinProcess(pid=pid)
|
|
return l
|
|
|
|
@property
|
|
def security_descriptor(self):
|
|
"""The security descriptor of the service
|
|
|
|
:type: :class:`~windows.security.SecurityDescriptor`
|
|
"""
|
|
return security.SecurityDescriptor.from_service(self.name)
|
|
|
|
def start(self, args=None):
|
|
"""Start the service
|
|
|
|
:param args: a list of :class:`str`
|
|
"""
|
|
nbelt = 0
|
|
if args is not None:
|
|
if isinstance(args, windows.pycompat.anybuff):
|
|
args = [args]
|
|
nbelt = len(args)
|
|
args = (gdef.LPCSTR * (nbelt))(*args)
|
|
return windows.winproxy.StartServiceA(self, nbelt, args)
|
|
|
|
def stop(self):
|
|
"""Stop the service"""
|
|
status = SERVICE_STATUS()
|
|
windows.winproxy.ControlService(self, gdef.SERVICE_CONTROL_STOP, status)
|
|
return status
|
|
|
|
def __repr__(self):
|
|
return """<{0} "{1}" {2!r}>""".format(type(self).__name__, self.name, self.status.state)
|
|
|
|
def __del__(self):
|
|
return windows.winproxy.CloseServiceHandle(self)
|