Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

pipeTestService.py 6.7KB

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