【问题标题】:Python Windows Service Problems - Works when using debug argument but not while started as a service?Python Windows 服务问题 - 在使用调试参数时有效,但在作为服务启动时无效?
【发布时间】:2009-11-22 21:30:55
【问题描述】:

希望这里有人能对我的问题有所了解:D

我一直在用 python 创建一个 Windows XP 服务,旨在监视/修复选定的 Windows/应用程序/服务设置,atm 我一直专注于默认 DCOM 设置。

我们的想法是在另一个注册表项中备份我们的默认配置以供参考。每 30 分钟(目前每 30 秒进行一次测试)我希望该服务从注册表中查询当前的 windows 默认 DCOM 设置,并将结果与​​默认配置进行比较。如果发现差异,该服务将使用自定义配置设置替换当前的 Windows 设置。

我已经创建/测试了我的类来处理注册表检查/修复,到目前为止它运行完美。直到我将它编译为 exe 并将其作为服务运行。

服务本身启动得很好,它似乎按照定义每 30 秒循环一次,但我处理注册表检查/修复的模块似乎没有按规定运行。

我创建了一个日志文件,得到以下错误:

回溯(最近一次通话最后一次):
文件“DCOMMon.pyc”,第 52 行,在 RepairDCOM
GetDefaultDCOM
中的文件“DCOMMon.pyc”,第 97 行 调用
中的文件“pywmi.pyc”,第 396 行 文件“pywmi.pyc”,第 189 行,在 handle_com_error
x_wmi: -0x7ffdfff7 - 发生异常。
错误:SWbemObjectEx
-0x7ffbfe10 -

当我停止服务并手动运行 exe 时,指定调试参数:DCOMMon.exe debug,服务启动并正常运行,按预期执行所有任务。我能看到的唯一区别是服务以 SYSTEM 用户而不是登录用户身份启动进程,这让我相信(只是在这里猜测)它可能是 SYSTEM 用户的某种遗漏权限/策略?我已经测试过以另一个用户身份运行该服务,但那里也没有区别。

其他想法是将 wmi 服务添加到我的服务的依赖项中,但说实话我不知道会做什么:P 这是我第一次尝试在 python 中创建 Windows 服务,而不使用类似的东西srvany.exe。

我昨晚和今天的大部分时间都在尝试谷歌搜索并找到一些关于 py2exe 和 wmi 兼容性的信息,但到目前为止我发现的建议并没有帮助解决上述问题。

任何建议将不胜感激。

PS:不要因为糟糕的日志记录而讨厌我,我从不同的脚本中剪切/粘贴了我的记录器并且我没有进行适当的更改,它可能会使每一行加倍:P。日志文件可以在这里找到:“%WINDIR%\system32\DCOMMon.log”

更新

我试图将这个项目分成两个 exe 文件而不是一个。让服务对其他 exe 进行外部调用以运行 wmi 注册表部分。同样,当使用 debug arg 运行时,它工作得很好,但是当我将它作为服务启动时,它会记录相同的错误消息。越来越多这开始看起来像是权限问题而不是程序问题:(

更新

DCOMMon.py - 需要 pywin32、wmi(重命名为 pywmi),

# DCOMMon.py


import win32api, win32service, win32serviceutil, win32event, win32evtlogutil, win32traceutil
import logging, logging.handlers, os, re, sys, thread, time, traceback, pywmi # pywmi == wmi module renamed as suggested in online post
import _winreg as reg


DCOM_DEFAULT_CONFIGURATION      = ["EnableDCOM", "EnableRemoteConnect", "LegacyAuthenticationLevel", "LegacyImpersonationLevel", "DefaultAccessPermission",
                                   "DefaultLaunchPermission", "MachineAccessRestriction", "MachineLaunchRestriction"]

DCOM_DEFAULT_ACCESS_PERMISSION  = [1, 0, 4, 128, 92, 0, 0, 0, 108, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 72, 0, 3, 0, 0, 0, 0, 0, 24, 0, 7, 0, 0, 0, 1, 2,
                                   0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 20, 0, 7, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 0, 20, 0, 7,
                                   0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32,
                                   0, 0, 0, 32, 2, 0, 0]

DCOM_DEFAULT_LAUNCH_PERMISSION  = [1, 0, 4, 128, 132, 0, 0, 0, 148, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 112, 0, 5, 0, 0, 0, 0, 0, 24, 0, 31, 0, 0, 0, 1,
                                   2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 20, 0, 31, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 0, 20, 0,
                                   31, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 20, 0, 31, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 4, 0, 0, 0, 0, 0, 20, 0,
                                   31, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 5, 18, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5,
                                   32, 0, 0, 0, 32, 2, 0, 0]

DCOM_MACHINE_ACCESS_RESTRICTION = [1, 0, 4, 128, 68, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 48, 0, 2, 0, 0, 0, 0, 0, 20, 0, 3, 0, 0, 0, 1, 1,
                                   0, 0, 0, 0, 0, 5, 7, 0, 0, 0, 0, 0, 20, 0, 7, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0,
                                   0, 0, 32, 2, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]

DCOM_MACHINE_LAUNCH_RESTRICTION = [1, 0, 4, 128, 72, 0, 0, 0, 88, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 0, 52, 0, 2, 0, 0, 0, 0, 0, 24, 0, 31, 0, 0, 0, 1,
                                   2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 0, 0, 20, 0, 31, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 2, 0, 0,
                                   0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0]

COMPUTER  = os.environ["COMPUTERNAME"]
REGISTRY  = pywmi.WMI(COMPUTER, namespace="root/default").StdRegProv
LOGFILE   = os.getcwd() + "\\DCOMMon.log"


def Logger(title, filename):
    logger = logging.getLogger(title)
    logger.setLevel(logging.DEBUG)
    handler = logging.handlers.RotatingFileHandler(filename, maxBytes=0, backupCount=0)
    handler.setLevel(logging.DEBUG)
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    handler.setFormatter(formatter)
    logger.addHandler(handler)
    return logger


def LogIt(filename=LOGFILE):
    #try:
    #    if os.path.exists(filename):
    #        os.remove(filename)
    #except:
    #    pass
    log = Logger("DCOMMon", filename)
    tb  = str(traceback.format_exc()).split("\n")
    log.error("")
    for i, a in enumerate(tb):
        if a.strip() != "":
            log.error(a)

class Monitor:

    def RepairDCOM(self):
        try:
            repaired = {}
            dict1    = self.GetDefaultDCOM()
            dict2    = self.GetCurrentDCOM()
            compared = self.CompareDCOM(dict1, dict2)

            for dobj in DCOM_DEFAULT_CONFIGURATION:
                try:
                    compared[dobj]
                    if dobj == "LegacyAuthenticationLevel" or dobj == "LegacyImpersonationLevel":
                        REGISTRY.SetDWORDValue(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole", sValueName=dobj, uValue=dict1[dobj])
                    elif dobj == "DefaultAccessPermission" or dobj == "DefaultLaunchPermission" or \
                         dobj == "MachineAccessRestriction" or dobj == "MachineLaunchRestriction":
                        REGISTRY.SetBinaryValue(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole", sValueName=dobj, uValue=dict1[dobj])
                    elif dobj == "EnableDCOM" or dobj == "EnableRemoteConnect":
                        REGISTRY.SetStringValue(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole", sValueName=dobj, sValue=dict1[dobj])
                except KeyError:
                    pass
        except:
            LogIt(LOGFILE)

    def CompareDCOM(self, dict1, dict2):
        compare = {}
        for (key, value) in dict2.iteritems():
            try:
                if dict1[key] != value:
                    compare[key] = value
            except KeyError:
                compare[key] = value
        return compare

    def GetCurrentDCOM(self):
        current = {}
        for name in REGISTRY.EnumValues(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole")[1]:
            value = REGISTRY.GetStringValue(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole", sValueName=str(name))[1]
            if value:
                current[str(name)] = str(value)
            else:
                value = REGISTRY.GetDWORDValue(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole", sValueName=str(name))[1]
                if not value:
                    value = REGISTRY.GetBinaryValue(hDefKey=reg.HKEY_LOCAL_MACHINE, sSubKeyName="SOFTWARE\\Microsoft\\Ole", sValueName=str(name))[1]
            current[str(name)] = value
        return current

    def GetDefaultDCOM(self):
        default = {}
        # Get Default DCOM Settings
        for name in REGISTRY.EnumValues(hDefKey=reg.HKEY_CURRENT_USER, sSubKeyName="Software\\DCOMMon")[1]:
            value = REGISTRY.GetStringValue(hDefKey=reg.HKEY_CURRENT_USER, sSubKeyName="Software\\DCOMMon", sValueName=str(name))[1]
            if value:
                default[str(name)] = str(value)
            else:
                value = REGISTRY.GetDWORDValue(hDefKey=reg.HKEY_CURRENT_USER, sSubKeyName="Software\\DCOMMon", sValueName=str(name))[1]
                if not value:
                    value = REGISTRY.GetBinaryValue(hDefKey=reg.HKEY_CURRENT_USER, sSubKeyName="Software\\DCOMMon", sValueName=str(name))[1]
            default[str(name)] = value
        return default

class DCOMMon(win32serviceutil.ServiceFramework):
    _svc_name_         = "DCOMMon"
    _svc_display_name_ = "DCOM Monitoring Service"
    _svc_description_  = "DCOM Monitoring Service"
    _svc_deps_         = ["EventLog"]

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
        self.isAlive   = True

    def SvcDoRun(self):
        import servicemanager
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED,
                              (self._svc_name_, ': DCOM Monitoring Service - Service Started'))
        self.timeout=30000  # In milliseconds
        while self.isAlive:
            rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
            if rc == win32event.WAIT_OBJECT_0:
                 break
            else:
                servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED,
                                      (self._svc_name_, ': DCOM Monitoring Service - Examining DCOM Configuration'))
                Monitor().RepairDCOM()
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED,
                              (self._svc_name_, ': DCOM Monitoring Service - Service Stopped'))
        return

    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)
        LOG.close()
        self.isAlive = False
        return

#def ctrlHandler(ctrlType):
#    return True

if __name__ == '__main__':
#    win32api.SetConsoleCtrlHandler(ctrlHandler, True)
    #print Monitor().RepairDCOM()
    win32serviceutil.HandleCommandLine(DCOMMon)

DCOMMon_setup.py - 需要 py2exe(自可执行,不需要 py2exe arg)

# DCOMMon_setup.py (self executable, no need for py2exe arg)

# Usage:
# DCOMMon.exe install
# DCOMMon.exe start
# DCOMMon.exe stop
# DCOMMon.exe remove
# DCOMMon.exe debug

# you can see output of this program running python site-packages\win32\lib\win32traceutil



try:
    # (snippet I found somewhere, searching something??)
    # if this doesn't work, try import modulefinder
    import py2exe.mf as modulefinder
    import win32com, sys
    for p in win32com.__path__[1:]:
        modulefinder.AddPackagePath("win32com", p)
    for extra in ["win32com.shell"]: #,"win32com.mapi"
        __import__(extra)
        m = sys.modules[extra]
        for p in m.__path__[1:]:
            modulefinder.AddPackagePath(extra, p)
except ImportError:
    print "NOT FOUND"


from distutils.core import setup
import py2exe, sys

if len(sys.argv) == 1:
    sys.argv.append("py2exe")
    #sys.argv.append("-q")

class Target:
    def __init__(self, **kw):
        self.__dict__.update(kw)
        # for the versioninfo resources
        self.version      = "1.0.0.1"
        self.language     = "English (Canada)"
        self.company_name = "Whoever"
        self.copyright    = "Nobody"
        self.name         = "Nobody Home"


myservice = Target(
    description = 'DCOM Monitoring Service',
    modules = ['DCOMMon'],
    cmdline_style='pywin32'
    #dest_base = 'DCOMMon'
)

setup(
    options = {"py2exe": {"compressed": 1, "bundle_files": 1, "ascii": 1, "packages": ["encodings"]} },   
    console=["DCOMMon.py"],
    zipfile = None,
    service=[myservice]
) 

【问题讨论】:

  • 我还没有浏览上面的所有转储(而且我不是 Windows 的大用户/开发人员),但在 Linux 上,我可能会怀疑在守护进程模式下运行时不同的权限之类的东西用户。也许这可能是一个提示?
  • 到目前为止,我已经为 wmimgmt.msc 中的 SYSTEM 帐户添加了完整权限,我已将 SYSTEM 添加到 dcomcnfg 中的 WMI 设置中,尝试以当前用户身份运行该服务,现在我退出了想法。不知道还有什么要检查的?我收到的 SWbemObjectEx 错误让我相信这是一个 wmi 错误,但通常该错误会包含更多详细信息,例如“拒绝访问”。这次它是空白的.. :( 如果有人可以阐明登录用户和系统用户之间的永久差异,这是我第一次尝试创建服务,将不胜感激。

标签: python windows-services wmi py2exe pywin32


【解决方案1】:

所以我想是时候承认我的愚蠢了......:P

事实证明,这不是 python 问题、py2exe 问题,也不是 WMI 问题。 :(

这或多或少是一个简单的权限问题。如此简单,我在一个月的大部分时间里都忽略了它。 :(

经验法则,如果您想创建一个调用特定注册表项以获取(在这种情况下)默认配置设置的服务......

也许……只是也许……

应该将他们的默认密钥放在 "HKEY_LOCAL_MACHINE" 中,而不是 "HKEY_CURRENT_USER"? :P

是的,就是这么简单……

我应该从我过去从事的其他项目中记住这条规则。当您作为本地系统帐户运行时,简单地说,没有 "HKEY_CURRENT_USER" 子项可以访问。如果您绝对必须访问特定用户的 "HKEY_CURRENT_USER" 子键,我猜唯一的方法就是冒充该用户。对我来说幸运的是,这对于我想要完成的事情来说不是必需的。

以下链接为我提供了记忆和解决问题所需的内容:

http://msdn.microsoft.com/en-us/library/ms684190%28VS.85%29.aspx

长话短说,我将所有默认值迁移到 "HKEY_LOCAL_MACHINE\SOFTWARE" 子项,我的服务运行良好。 :D

感谢你们的帮助,但是这个问题已经解决了;)

【讨论】:

  • UPDATE 我能够使用 HKEY_USERS 配置单元访问用户的密钥。这有点棘手,因为您必须获取当前用户的 SID 才能访问正确的子项: 从 SYSTEM 帐户工作:“HKEY_USERS\\Software\kMonitor” 不能从 SYSTEM 帐户工作:“HKEY_CURRENT_USER\Software\kMonitor "
【解决方案2】:

我不是这方面的专家,但这是我的两分钱:

This article 告诉我您可能需要以具有所需目标系统权限的人身份登录。

不过,我觉得这有点过分了。您是否尝试过在以计算机管理员身份运行命令提示符时从命令行编译脚本 - 以便您可以解锁所有权限(在 Windows Vista 和 Windows 7 上,通过右键单击开始菜单并点击“以管理员身份运行”)。

希望对你有帮助

【讨论】:

  • 感谢您的回复 :D 我尝试从 Admin cmd 提示符编译它,但我仍然没有运气。问题不在于它是如何编译的,服务本身工作得很好,只是正在发出的 wmi 调用似乎失败了。无论如何,我的帐户已经是管理员,并且该服务以 SYSTEM 而不是管理员身份运行,但是您永远不知道,此时我会尝试任何事情;)仅供参考,这是一项 XP 服务,我之前可能应该提到过。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多