ControlService.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. # ControlService.py
  2. #
  3. # A simple app which duplicates some of the functionality in the
  4. # Services applet of the control panel.
  5. #
  6. # Suggested enhancements (in no particular order):
  7. #
  8. # 1. When changing the service status, continue to query the status
  9. # of the service until the status change is complete. Use this
  10. # information to put up some kind of a progress dialog like the CP
  11. # applet does. Unlike the CP, allow canceling out in the event that
  12. # the status change hangs.
  13. # 2. When starting or stopping a service with dependencies, alert
  14. # the user about the dependent services, then start (or stop) all
  15. # dependent services as appropriate.
  16. # 3. Allow toggling between service view and device view
  17. # 4. Allow configuration of other service parameters such as startup
  18. # name and password.
  19. # 5. Allow connection to remote SCMs. This is just a matter of
  20. # reconnecting to the SCM on the remote machine; the rest of the
  21. # code should still work the same.
  22. # 6. Either implement the startup parameters or get rid of the editbox.
  23. # 7. Either implement or get rid of "H/W Profiles".
  24. # 8. Either implement or get rid of "Help".
  25. # 9. Improve error handling. Ideally, this would also include falling
  26. # back to lower levels of functionality for users with less rights.
  27. # Right now, we always try to get all the rights and fail when we can't
  28. from pywin.mfc import dialog
  29. import win32ui
  30. import win32con
  31. import win32service
  32. class StartupDlg(dialog.Dialog):
  33. IDC_LABEL = 127
  34. IDC_DEVICE = 128
  35. IDC_BOOT = 129
  36. IDC_SYSTEM = 130
  37. IDC_AUTOMATIC = 131
  38. IDC_MANUAL = 132
  39. IDC_DISABLED = 133
  40. def __init__(self, displayname, service):
  41. dialog.Dialog.__init__(self, self.GetResource())
  42. self.name = displayname
  43. self.service = service
  44. def __del__(self):
  45. win32service.CloseServiceHandle(self.service)
  46. def OnInitDialog(self):
  47. cfg = win32service.QueryServiceConfig(self.service)
  48. self.GetDlgItem(self.IDC_BOOT + cfg[1]).SetCheck(1)
  49. status = win32service.QueryServiceStatus(self.service)
  50. if ((status[0] & win32service.SERVICE_KERNEL_DRIVER) or
  51. (status[0] & win32service.SERVICE_FILE_SYSTEM_DRIVER)):
  52. # driver
  53. self.GetDlgItem(self.IDC_LABEL).SetWindowText('Device:')
  54. else:
  55. # service
  56. self.GetDlgItem(self.IDC_LABEL).SetWindowText('Service:')
  57. self.GetDlgItem(self.IDC_BOOT).EnableWindow(0)
  58. self.GetDlgItem(self.IDC_SYSTEM).EnableWindow(0)
  59. self.GetDlgItem(self.IDC_DEVICE).SetWindowText(str(self.name))
  60. return dialog.Dialog.OnInitDialog(self)
  61. def OnOK(self):
  62. self.BeginWaitCursor()
  63. starttype = self.GetCheckedRadioButton(self.IDC_BOOT, self.IDC_DISABLED) - self.IDC_BOOT
  64. try:
  65. win32service.ChangeServiceConfig(self.service, win32service.SERVICE_NO_CHANGE, starttype,
  66. win32service.SERVICE_NO_CHANGE, None, None, 0, None, None, None, None)
  67. except:
  68. self.MessageBox('Unable to change startup configuration', None,
  69. win32con.MB_ICONEXCLAMATION)
  70. self.EndWaitCursor()
  71. return dialog.Dialog.OnOK(self)
  72. def GetResource(self):
  73. style = win32con.WS_POPUP | win32con.DS_SETFONT | win32con.WS_SYSMENU | win32con.WS_CAPTION | win32con.WS_VISIBLE | win32con.DS_MODALFRAME
  74. exstyle = None
  75. t = [["Service Startup", (6, 18, 188, 107), style, exstyle, (8, 'MS Shell Dlg')], ]
  76. t.append([130, "Device:", self.IDC_LABEL, (6, 7, 40, 8), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.SS_LEFT])
  77. t.append([130, "", self.IDC_DEVICE, (48, 7, 134, 8), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.SS_LEFT])
  78. t.append([128, "Startup Type", -1, (6, 21, 130, 80), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_GROUP | win32con.BS_GROUPBOX])
  79. t.append([128, "&Boot", self.IDC_BOOT, (12, 33, 39, 10), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_AUTORADIOBUTTON])
  80. t.append([128, "&System", self.IDC_SYSTEM, (12, 46, 39, 10), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_AUTORADIOBUTTON])
  81. t.append([128, "&Automatic", self.IDC_AUTOMATIC, (12, 59, 118, 10), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_AUTORADIOBUTTON])
  82. t.append([128, "&Manual", self.IDC_MANUAL, (12, 72, 118, 10), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_AUTORADIOBUTTON])
  83. t.append([128, "&Disabled", self.IDC_DISABLED, (12, 85, 118, 10), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_AUTORADIOBUTTON])
  84. t.append([128, "OK", win32con.IDOK, (142, 25, 40, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.WS_GROUP | win32con.BS_DEFPUSHBUTTON])
  85. t.append([128, "Cancel", win32con.IDCANCEL, (142, 43, 40, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON])
  86. t.append([128, "&Help", win32con.IDHELP, (142, 61, 40, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON])
  87. return t
  88. class ServiceDlg(dialog.Dialog):
  89. IDC_LIST = 128
  90. IDC_START = 129
  91. IDC_STOP = 130
  92. IDC_PAUSE = 131
  93. IDC_CONTINUE = 132
  94. IDC_STARTUP = 133
  95. IDC_PROFILES = 134
  96. IDC_PARAMS = 135
  97. def __init__(self, machineName = ''):
  98. dialog.Dialog.__init__(self, self.GetResource())
  99. self.HookCommand(self.OnListEvent, self.IDC_LIST)
  100. self.HookCommand(self.OnStartCmd, self.IDC_START)
  101. self.HookCommand(self.OnStopCmd, self.IDC_STOP)
  102. self.HookCommand(self.OnPauseCmd, self.IDC_PAUSE)
  103. self.HookCommand(self.OnContinueCmd, self.IDC_CONTINUE)
  104. self.HookCommand(self.OnStartupCmd, self.IDC_STARTUP)
  105. self.machineName = machineName
  106. self.scm = win32service.OpenSCManager(self.machineName, None, win32service.SC_MANAGER_ALL_ACCESS)
  107. def __del__(self):
  108. win32service.CloseServiceHandle(self.scm)
  109. def OnInitDialog(self):
  110. self.listCtrl = self.GetDlgItem(self.IDC_LIST)
  111. self.listCtrl.SetTabStops([158, 200])
  112. if self.machineName:
  113. self.SetWindowText("Services on %s" % self.machineName)
  114. self.ReloadData()
  115. return dialog.Dialog.OnInitDialog(self)
  116. def ReloadData(self):
  117. service = self.GetSelService()
  118. self.listCtrl.SetRedraw(0)
  119. self.listCtrl.ResetContent()
  120. svcs = win32service.EnumServicesStatus(self.scm)
  121. i = 0
  122. self.data = []
  123. for svc in svcs:
  124. try:
  125. status = ('Unknown', 'Stopped', 'Starting', 'Stopping', 'Running',
  126. 'Continuing', 'Pausing', 'Paused')[svc[2][1]]
  127. except:
  128. status = 'Unknown'
  129. s = win32service.OpenService(self.scm, svc[0], win32service.SERVICE_ALL_ACCESS)
  130. cfg = win32service.QueryServiceConfig(s)
  131. try:
  132. startup = ('Boot', 'System', 'Automatic', 'Manual', 'Disabled')[cfg[1]]
  133. except:
  134. startup = 'Unknown'
  135. win32service.CloseServiceHandle(s)
  136. # svc[2][2] control buttons
  137. pos = self.listCtrl.AddString(str(svc[1]) + '\t' + status + '\t' + startup)
  138. self.listCtrl.SetItemData(pos, i)
  139. self.data.append(tuple(svc[2]) + (svc[1], svc[0], ))
  140. i = i + 1
  141. if service and service[1] == svc[0]:
  142. self.listCtrl.SetCurSel(pos)
  143. self.OnListEvent(self.IDC_LIST, win32con.LBN_SELCHANGE)
  144. self.listCtrl.SetRedraw(1)
  145. def OnListEvent(self, id, code):
  146. if code == win32con.LBN_SELCHANGE or code == win32con.LBN_SELCANCEL:
  147. pos = self.listCtrl.GetCurSel()
  148. if pos >= 0:
  149. data = self.data[self.listCtrl.GetItemData(pos)][2]
  150. canstart = (self.data[self.listCtrl.GetItemData(pos)][1] == win32service.SERVICE_STOPPED)
  151. else:
  152. data = 0
  153. canstart = 0
  154. self.GetDlgItem(self.IDC_START).EnableWindow(canstart)
  155. self.GetDlgItem(self.IDC_STOP).EnableWindow(data & win32service.SERVICE_ACCEPT_STOP)
  156. self.GetDlgItem(self.IDC_PAUSE).EnableWindow(data & win32service.SERVICE_ACCEPT_PAUSE_CONTINUE)
  157. self.GetDlgItem(self.IDC_CONTINUE).EnableWindow(data & win32service.SERVICE_ACCEPT_PAUSE_CONTINUE)
  158. def GetSelService(self):
  159. pos = self.listCtrl.GetCurSel()
  160. if pos < 0:
  161. return None
  162. pos = self.listCtrl.GetItemData(pos)
  163. return self.data[pos][-2:]
  164. def OnStartCmd(self, id, code):
  165. service = self.GetSelService()
  166. if not service:
  167. return
  168. s = win32service.OpenService(self.scm, service[1], win32service.SERVICE_ALL_ACCESS)
  169. win32service.StartService(s, None)
  170. win32service.CloseServiceHandle(s)
  171. self.ReloadData()
  172. def OnStopCmd(self, id, code):
  173. service = self.GetSelService()
  174. if not service:
  175. return
  176. s = win32service.OpenService(self.scm, service[1], win32service.SERVICE_ALL_ACCESS)
  177. win32service.ControlService(s, win32service.SERVICE_CONTROL_STOP)
  178. win32service.CloseServiceHandle(s)
  179. self.ReloadData()
  180. def OnPauseCmd(self, id, code):
  181. service = self.GetSelService()
  182. if not service:
  183. return
  184. s = win32service.OpenService(self.scm, service[1], win32service.SERVICE_ALL_ACCESS)
  185. win32service.ControlService(s, win32service.SERVICE_CONTROL_PAUSE)
  186. win32service.CloseServiceHandle(s)
  187. self.ReloadData()
  188. def OnContinueCmd(self, id, code):
  189. service = self.GetSelService()
  190. if not service:
  191. return
  192. s = win32service.OpenService(self.scm, service[1], win32service.SERVICE_ALL_ACCESS)
  193. win32service.ControlService(s, win32service.SERVICE_CONTROL_CONTINUE)
  194. win32service.CloseServiceHandle(s)
  195. self.ReloadData()
  196. def OnStartupCmd(self, id, code):
  197. service = self.GetSelService()
  198. if not service:
  199. return
  200. s = win32service.OpenService(self.scm, service[1], win32service.SERVICE_ALL_ACCESS)
  201. if StartupDlg(service[0], s).DoModal() == win32con.IDOK:
  202. self.ReloadData()
  203. def GetResource(self):
  204. style = win32con.WS_POPUP | win32con.DS_SETFONT | win32con.WS_SYSMENU | win32con.WS_CAPTION | win32con.WS_VISIBLE | win32con.DS_MODALFRAME
  205. exstyle = None
  206. t = [["Services", (16, 16, 333, 157), style, exstyle, (8, 'MS Shell Dlg')], ]
  207. t.append([130, "Ser&vice", -1, (6, 6, 70, 8), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.SS_LEFT])
  208. t.append([130, "Status", -1, (164, 6, 42, 8), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.SS_LEFT])
  209. t.append([130, "Startup", -1, (206, 6, 50, 8), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.SS_LEFT])
  210. t.append([131, "", self.IDC_LIST, (6, 16, 255, 106), win32con.LBS_USETABSTOPS | win32con.LBS_SORT | win32con.LBS_NOINTEGRALHEIGHT | win32con.WS_BORDER | win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_TABSTOP | win32con.LBS_NOTIFY | win32con.WS_VSCROLL])
  211. t.append([128, "Close", win32con.IDOK, (267, 6, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_GROUP | win32con.WS_TABSTOP | win32con.BS_DEFPUSHBUTTON])
  212. t.append([128, "&Start", self.IDC_START, (267, 27, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON])
  213. t.append([128, "S&top", self.IDC_STOP, (267, 44, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON])
  214. t.append([128, "&Pause", self.IDC_PAUSE, (267, 61, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON])
  215. t.append([128, "&Continue", self.IDC_CONTINUE, (267, 78, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON])
  216. t.append([128, "Sta&rtup...", self.IDC_STARTUP, (267, 99, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON])
  217. t.append([128, "H&W Profiles...", self.IDC_PROFILES, (267, 116, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON])
  218. t.append([128, "&Help", win32con.IDHELP, (267, 137, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON])
  219. t.append([130, "St&artup Parameters:", -1, (6, 128, 70, 8), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.SS_LEFT])
  220. t.append([129, "", self.IDC_PARAMS, (6, 139, 247, 12), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_GROUP | win32con.WS_BORDER | win32con.ES_AUTOHSCROLL])
  221. return t
  222. if __name__=='__main__':
  223. import sys
  224. machine = ''
  225. if len(sys.argv)>1:
  226. machine = sys.argv[1]
  227. ServiceDlg(machine).DoModal()