# ControlService.py # # A simple app which duplicates some of the functionality in the # Services applet of the control panel. # # Suggested enhancements (in no particular order): # # 1. When changing the service status, continue to query the status # of the service until the status change is complete. Use this # information to put up some kind of a progress dialog like the CP # applet does. Unlike the CP, allow canceling out in the event that # the status change hangs. # 2. When starting or stopping a service with dependencies, alert # the user about the dependent services, then start (or stop) all # dependent services as appropriate. # 3. Allow toggling between service view and device view # 4. Allow configuration of other service parameters such as startup # name and password. # 5. Allow connection to remote SCMs. This is just a matter of # reconnecting to the SCM on the remote machine; the rest of the # code should still work the same. # 6. Either implement the startup parameters or get rid of the editbox. # 7. Either implement or get rid of "H/W Profiles". # 8. Either implement or get rid of "Help". # 9. Improve error handling. Ideally, this would also include falling # back to lower levels of functionality for users with less rights. # Right now, we always try to get all the rights and fail when we can't import win32con import win32service import win32ui from pywin.mfc import dialog class StartupDlg(dialog.Dialog): IDC_LABEL = 127 IDC_DEVICE = 128 IDC_BOOT = 129 IDC_SYSTEM = 130 IDC_AUTOMATIC = 131 IDC_MANUAL = 132 IDC_DISABLED = 133 def __init__(self, displayname, service): dialog.Dialog.__init__(self, self.GetResource()) self.name = displayname self.service = service def __del__(self): win32service.CloseServiceHandle(self.service) def OnInitDialog(self): cfg = win32service.QueryServiceConfig(self.service) self.GetDlgItem(self.IDC_BOOT + cfg[1]).SetCheck(1) status = win32service.QueryServiceStatus(self.service) if (status[0] & win32service.SERVICE_KERNEL_DRIVER) or ( status[0] & win32service.SERVICE_FILE_SYSTEM_DRIVER ): # driver self.GetDlgItem(self.IDC_LABEL).SetWindowText("Device:") else: # service self.GetDlgItem(self.IDC_LABEL).SetWindowText("Service:") self.GetDlgItem(self.IDC_BOOT).EnableWindow(0) self.GetDlgItem(self.IDC_SYSTEM).EnableWindow(0) self.GetDlgItem(self.IDC_DEVICE).SetWindowText(str(self.name)) return dialog.Dialog.OnInitDialog(self) def OnOK(self): self.BeginWaitCursor() starttype = ( self.GetCheckedRadioButton(self.IDC_BOOT, self.IDC_DISABLED) - self.IDC_BOOT ) try: win32service.ChangeServiceConfig( self.service, win32service.SERVICE_NO_CHANGE, starttype, win32service.SERVICE_NO_CHANGE, None, None, 0, None, None, None, None, ) except: self.MessageBox( "Unable to change startup configuration", None, win32con.MB_ICONEXCLAMATION, ) self.EndWaitCursor() return dialog.Dialog.OnOK(self) def GetResource(self): style = ( win32con.WS_POPUP | win32con.DS_SETFONT | win32con.WS_SYSMENU | win32con.WS_CAPTION | win32con.WS_VISIBLE | win32con.DS_MODALFRAME ) exstyle = None t = [ ["Service Startup", (6, 18, 188, 107), style, exstyle, (8, "MS Shell Dlg")], ] t.append( [ 130, "Device:", self.IDC_LABEL, (6, 7, 40, 8), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.SS_LEFT, ] ) t.append( [ 130, "", self.IDC_DEVICE, (48, 7, 134, 8), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.SS_LEFT, ] ) t.append( [ 128, "Startup Type", -1, (6, 21, 130, 80), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_GROUP | win32con.BS_GROUPBOX, ] ) t.append( [ 128, "&Boot", self.IDC_BOOT, (12, 33, 39, 10), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_AUTORADIOBUTTON, ] ) t.append( [ 128, "&System", self.IDC_SYSTEM, (12, 46, 39, 10), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_AUTORADIOBUTTON, ] ) t.append( [ 128, "&Automatic", self.IDC_AUTOMATIC, (12, 59, 118, 10), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_AUTORADIOBUTTON, ] ) t.append( [ 128, "&Manual", self.IDC_MANUAL, (12, 72, 118, 10), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_AUTORADIOBUTTON, ] ) t.append( [ 128, "&Disabled", self.IDC_DISABLED, (12, 85, 118, 10), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_AUTORADIOBUTTON, ] ) 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, ] ) t.append( [ 128, "Cancel", win32con.IDCANCEL, (142, 43, 40, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON, ] ) t.append( [ 128, "&Help", win32con.IDHELP, (142, 61, 40, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON, ] ) return t class ServiceDlg(dialog.Dialog): IDC_LIST = 128 IDC_START = 129 IDC_STOP = 130 IDC_PAUSE = 131 IDC_CONTINUE = 132 IDC_STARTUP = 133 IDC_PROFILES = 134 IDC_PARAMS = 135 def __init__(self, machineName=""): dialog.Dialog.__init__(self, self.GetResource()) self.HookCommand(self.OnListEvent, self.IDC_LIST) self.HookCommand(self.OnStartCmd, self.IDC_START) self.HookCommand(self.OnStopCmd, self.IDC_STOP) self.HookCommand(self.OnPauseCmd, self.IDC_PAUSE) self.HookCommand(self.OnContinueCmd, self.IDC_CONTINUE) self.HookCommand(self.OnStartupCmd, self.IDC_STARTUP) self.machineName = machineName self.scm = win32service.OpenSCManager( self.machineName, None, win32service.SC_MANAGER_ALL_ACCESS ) def __del__(self): win32service.CloseServiceHandle(self.scm) def OnInitDialog(self): self.listCtrl = self.GetDlgItem(self.IDC_LIST) self.listCtrl.SetTabStops([158, 200]) if self.machineName: self.SetWindowText("Services on %s" % self.machineName) self.ReloadData() return dialog.Dialog.OnInitDialog(self) def ReloadData(self): service = self.GetSelService() self.listCtrl.SetRedraw(0) self.listCtrl.ResetContent() svcs = win32service.EnumServicesStatus(self.scm) i = 0 self.data = [] for svc in svcs: try: status = ( "Unknown", "Stopped", "Starting", "Stopping", "Running", "Continuing", "Pausing", "Paused", )[svc[2][1]] except: status = "Unknown" s = win32service.OpenService( self.scm, svc[0], win32service.SERVICE_ALL_ACCESS ) cfg = win32service.QueryServiceConfig(s) try: startup = ("Boot", "System", "Automatic", "Manual", "Disabled")[cfg[1]] except: startup = "Unknown" win32service.CloseServiceHandle(s) # svc[2][2] control buttons pos = self.listCtrl.AddString(str(svc[1]) + "\t" + status + "\t" + startup) self.listCtrl.SetItemData(pos, i) self.data.append( tuple(svc[2]) + ( svc[1], svc[0], ) ) i = i + 1 if service and service[1] == svc[0]: self.listCtrl.SetCurSel(pos) self.OnListEvent(self.IDC_LIST, win32con.LBN_SELCHANGE) self.listCtrl.SetRedraw(1) def OnListEvent(self, id, code): if code == win32con.LBN_SELCHANGE or code == win32con.LBN_SELCANCEL: pos = self.listCtrl.GetCurSel() if pos >= 0: data = self.data[self.listCtrl.GetItemData(pos)][2] canstart = ( self.data[self.listCtrl.GetItemData(pos)][1] == win32service.SERVICE_STOPPED ) else: data = 0 canstart = 0 self.GetDlgItem(self.IDC_START).EnableWindow(canstart) self.GetDlgItem(self.IDC_STOP).EnableWindow( data & win32service.SERVICE_ACCEPT_STOP ) self.GetDlgItem(self.IDC_PAUSE).EnableWindow( data & win32service.SERVICE_ACCEPT_PAUSE_CONTINUE ) self.GetDlgItem(self.IDC_CONTINUE).EnableWindow( data & win32service.SERVICE_ACCEPT_PAUSE_CONTINUE ) def GetSelService(self): pos = self.listCtrl.GetCurSel() if pos < 0: return None pos = self.listCtrl.GetItemData(pos) return self.data[pos][-2:] def OnStartCmd(self, id, code): service = self.GetSelService() if not service: return s = win32service.OpenService( self.scm, service[1], win32service.SERVICE_ALL_ACCESS ) win32service.StartService(s, None) win32service.CloseServiceHandle(s) self.ReloadData() def OnStopCmd(self, id, code): service = self.GetSelService() if not service: return s = win32service.OpenService( self.scm, service[1], win32service.SERVICE_ALL_ACCESS ) win32service.ControlService(s, win32service.SERVICE_CONTROL_STOP) win32service.CloseServiceHandle(s) self.ReloadData() def OnPauseCmd(self, id, code): service = self.GetSelService() if not service: return s = win32service.OpenService( self.scm, service[1], win32service.SERVICE_ALL_ACCESS ) win32service.ControlService(s, win32service.SERVICE_CONTROL_PAUSE) win32service.CloseServiceHandle(s) self.ReloadData() def OnContinueCmd(self, id, code): service = self.GetSelService() if not service: return s = win32service.OpenService( self.scm, service[1], win32service.SERVICE_ALL_ACCESS ) win32service.ControlService(s, win32service.SERVICE_CONTROL_CONTINUE) win32service.CloseServiceHandle(s) self.ReloadData() def OnStartupCmd(self, id, code): service = self.GetSelService() if not service: return s = win32service.OpenService( self.scm, service[1], win32service.SERVICE_ALL_ACCESS ) if StartupDlg(service[0], s).DoModal() == win32con.IDOK: self.ReloadData() def GetResource(self): style = ( win32con.WS_POPUP | win32con.DS_SETFONT | win32con.WS_SYSMENU | win32con.WS_CAPTION | win32con.WS_VISIBLE | win32con.DS_MODALFRAME ) exstyle = None t = [ ["Services", (16, 16, 333, 157), style, exstyle, (8, "MS Shell Dlg")], ] t.append( [ 130, "Ser&vice", -1, (6, 6, 70, 8), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.SS_LEFT, ] ) t.append( [ 130, "Status", -1, (164, 6, 42, 8), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.SS_LEFT, ] ) t.append( [ 130, "Startup", -1, (206, 6, 50, 8), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.SS_LEFT, ] ) 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, ] ) 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, ] ) t.append( [ 128, "&Start", self.IDC_START, (267, 27, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON, ] ) t.append( [ 128, "S&top", self.IDC_STOP, (267, 44, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON, ] ) t.append( [ 128, "&Pause", self.IDC_PAUSE, (267, 61, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON, ] ) t.append( [ 128, "&Continue", self.IDC_CONTINUE, (267, 78, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON, ] ) t.append( [ 128, "Sta&rtup...", self.IDC_STARTUP, (267, 99, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON, ] ) 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, ] ) t.append( [ 128, "&Help", win32con.IDHELP, (267, 137, 60, 14), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON, ] ) t.append( [ 130, "St&artup Parameters:", -1, (6, 128, 70, 8), win32con.WS_VISIBLE | win32con.WS_CHILD | win32con.SS_LEFT, ] ) 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, ] ) return t if __name__ == "__main__": import sys machine = "" if len(sys.argv) > 1: machine = sys.argv[1] ServiceDlg(machine).DoModal()