pipeTestService.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. # A Demo of services and named pipes.
  2. # A multi-threaded service that simply echos back its input.
  3. # * Install as a service using "pipeTestService.py install"
  4. # * Use Control Panel to change the user name of the service
  5. # to a real user name (ie, NOT the SystemAccount)
  6. # * Start the service.
  7. # * Run the "pipeTestServiceClient.py" program as the client pipe side.
  8. import win32serviceutil, win32service
  9. import pywintypes, win32con, winerror
  10. # Use "import *" to keep this looking as much as a "normal" service
  11. # as possible. Real code shouldn't do this.
  12. from win32event import *
  13. from win32file import *
  14. from win32pipe import *
  15. from win32api import *
  16. from ntsecuritycon import *
  17. # Old versions of the service framework would not let you import this
  18. # module at the top-level. Now you can, and can check 'Debugging()' and
  19. # 'RunningAsService()' to check your context.
  20. import servicemanager
  21. import traceback
  22. import _thread
  23. def ApplyIgnoreError(fn, args):
  24. try:
  25. return fn(*args)
  26. except error: # Ignore win32api errors.
  27. return None
  28. class TestPipeService(win32serviceutil.ServiceFramework):
  29. _svc_name_ = "PyPipeTestService"
  30. _svc_display_name_ = "Python Pipe Test Service"
  31. _svc_description_ = "Tests Python service framework by receiving and echoing messages over a named pipe"
  32. def __init__(self, args):
  33. win32serviceutil.ServiceFramework.__init__(self, args)
  34. self.hWaitStop = CreateEvent(None, 0, 0, None)
  35. self.overlapped = pywintypes.OVERLAPPED()
  36. self.overlapped.hEvent = CreateEvent(None,0,0,None)
  37. self.thread_handles = []
  38. def CreatePipeSecurityObject(self):
  39. # Create a security object giving World read/write access,
  40. # but only "Owner" modify access.
  41. sa = pywintypes.SECURITY_ATTRIBUTES()
  42. sidEveryone = pywintypes.SID()
  43. sidEveryone.Initialize(SECURITY_WORLD_SID_AUTHORITY,1)
  44. sidEveryone.SetSubAuthority(0, SECURITY_WORLD_RID)
  45. sidCreator = pywintypes.SID()
  46. sidCreator.Initialize(SECURITY_CREATOR_SID_AUTHORITY,1)
  47. sidCreator.SetSubAuthority(0, SECURITY_CREATOR_OWNER_RID)
  48. acl = pywintypes.ACL()
  49. acl.AddAccessAllowedAce(FILE_GENERIC_READ|FILE_GENERIC_WRITE, sidEveryone)
  50. acl.AddAccessAllowedAce(FILE_ALL_ACCESS, sidCreator)
  51. sa.SetSecurityDescriptorDacl(1, acl, 0)
  52. return sa
  53. # The functions executed in their own thread to process a client request.
  54. def DoProcessClient(self, pipeHandle, tid):
  55. try:
  56. try:
  57. # Create a loop, reading large data. If we knew the data stream was
  58. # was small, a simple ReadFile would do.
  59. d = ''.encode('ascii') # ensure bytes on py2k and py3k...
  60. hr = winerror.ERROR_MORE_DATA
  61. while hr==winerror.ERROR_MORE_DATA:
  62. hr, thisd = ReadFile(pipeHandle, 256)
  63. d = d + thisd
  64. print("Read", d)
  65. ok = 1
  66. except error:
  67. # Client disconnection - do nothing
  68. ok = 0
  69. # A secure service would handle (and ignore!) errors writing to the
  70. # pipe, but for the sake of this demo we dont (if only to see what errors
  71. # we can get when our clients break at strange times :-)
  72. if ok:
  73. msg = ("%s (on thread %d) sent me %s" % (GetNamedPipeHandleState(pipeHandle)[4],tid, d)).encode('ascii')
  74. WriteFile(pipeHandle, msg)
  75. finally:
  76. ApplyIgnoreError( DisconnectNamedPipe, (pipeHandle,) )
  77. ApplyIgnoreError( CloseHandle, (pipeHandle,) )
  78. def ProcessClient(self, pipeHandle):
  79. try:
  80. procHandle = GetCurrentProcess()
  81. th = DuplicateHandle(procHandle, GetCurrentThread(), procHandle, 0, 0, win32con.DUPLICATE_SAME_ACCESS)
  82. try:
  83. self.thread_handles.append(th)
  84. try:
  85. return self.DoProcessClient(pipeHandle, th)
  86. except:
  87. traceback.print_exc()
  88. finally:
  89. self.thread_handles.remove(th)
  90. except:
  91. traceback.print_exc()
  92. def SvcStop(self):
  93. self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
  94. SetEvent(self.hWaitStop)
  95. def SvcDoRun(self):
  96. # Write an event log record - in debug mode we will also
  97. # see this message printed.
  98. servicemanager.LogMsg(
  99. servicemanager.EVENTLOG_INFORMATION_TYPE,
  100. servicemanager.PYS_SERVICE_STARTED,
  101. (self._svc_name_, '')
  102. )
  103. num_connections = 0
  104. while 1:
  105. pipeHandle = CreateNamedPipe("\\\\.\\pipe\\PyPipeTest",
  106. PIPE_ACCESS_DUPLEX| FILE_FLAG_OVERLAPPED,
  107. PIPE_TYPE_MESSAGE | PIPE_READMODE_BYTE,
  108. PIPE_UNLIMITED_INSTANCES, # max instances
  109. 0, 0, 6000,
  110. self.CreatePipeSecurityObject())
  111. try:
  112. hr = ConnectNamedPipe(pipeHandle, self.overlapped)
  113. except error as details:
  114. print("Error connecting pipe!", details)
  115. CloseHandle(pipeHandle)
  116. break
  117. if hr==winerror.ERROR_PIPE_CONNECTED:
  118. # Client is already connected - signal event
  119. SetEvent(self.overlapped.hEvent)
  120. rc = WaitForMultipleObjects((self.hWaitStop, self.overlapped.hEvent), 0, INFINITE)
  121. if rc==WAIT_OBJECT_0:
  122. # Stop event
  123. break
  124. else:
  125. # Pipe event - spawn thread to deal with it.
  126. _thread.start_new_thread(self.ProcessClient, (pipeHandle,))
  127. num_connections = num_connections + 1
  128. # Sleep to ensure that any new threads are in the list, and then
  129. # wait for all current threads to finish.
  130. # What is a better way?
  131. Sleep(500)
  132. while self.thread_handles:
  133. self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING, 5000)
  134. print("Waiting for %d threads to finish..." % (len(self.thread_handles)))
  135. WaitForMultipleObjects(self.thread_handles, 1, 3000)
  136. # Write another event log record.
  137. servicemanager.LogMsg(
  138. servicemanager.EVENTLOG_INFORMATION_TYPE,
  139. servicemanager.PYS_SERVICE_STOPPED,
  140. (self._svc_name_, " after processing %d connections" % (num_connections,))
  141. )
  142. if __name__=='__main__':
  143. win32serviceutil.HandleCommandLine(TestPipeService)