首页 > 解决方案 > 从 Windows 服务内部设置环境变量

问题描述

我想从窗口服务内部设置一个环境变量。我的第一次尝试是使用 os.environ['flag']='1'。这适用于 cmd shell,但是当我将它放在服务主体中时,它什么也不做。

这是一个简化的代码,它创建了一个只是尝试更改此变量的值的 Windows 服务。它尝试将“1”写入名为 test_flag 的环境变量,休眠 60 秒,然后将其写回“0”。这样做的想法是让服务内部的过程以某种方式开始运行并监视 flag_test 是否在特定时间间隔内返回“0”(某种超时)。如果不是程序挂起,我将需要重新启动服务。我知道这并不漂亮,但我的想法已经用完了(真正的问题是 asyncio 任务挂起而没有抛出任何异常)。

我也尝试使用 win32api.SetEnvironmentVariable ,它也不起作用。

import threading
import win32api
import win32service
import win32serviceutil
import win32event
import os
from time import sleep

class InterruptedException(Exception):
    pass

class WorkerThread(threading.Thread):
    def __init__(self,controller):
        self._controller = controller
        self._stop = threading.Event()
        super(WorkerThread, self).__init__()
        
    def stop(self):
        self._stop.set()

    def stopped(self):
        return self._stop.isSet()
        
    def run(self):
        try:
            # Insert the code you want to run as a service here
            # rather than do "execfile(.../.../blah)" simply do:
            # You can have your code throw InterruptedException if your code needs to exit
            # Also check often if self.stopped and then cleanly exit

            os.environ['test_flag']='1'
         
             # initializing classes
            sleep(60)

            os.environ['test_flag']='0'

        # if code in another module is not yours or cannot check often if it should stop then use multiprocessing which will spawn separate processes that you can terminate then from here when you need to stop and return
        # in that case simply block here on self._stop.wait()
        except InterruptedException:
            # We are forcefully quitting 
            pass
        except Exception:
            pass
            # Oh oh, did not anticipate this, better report to Windows or log it
        finally:
           # Close/release any connections, handles, files etc.
           # OK, we can stop now
           win32event.SetEvent(self._controller)

class envvar_service(win32serviceutil.ServiceFramework):
    _svc_name_ = "py_varenv_test"
    _svc_display_name_ = "ENVVAR test"
    _svc_description_ = "Service test use of os.environ"

    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)     
        self.hWaitDone = win32event.CreateEvent(None, 0, 0, None)        
        
        self.worker = WorkerThread(self.hWaitDone)                


    def SvcStop(self):          
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)

    def SvcDoRun(self):
        import servicemanager      
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, '')) 
        
        self.timeout = 600000     # 60 seconds / 1 minute

        self.worker = WorkerThread(self.hWaitDone)
        self.worker.start()
        self.worker.setDaemon=True

        while True:
            # Wait for service stop signal
            rc = win32event.WaitForMultipleObjects([self.hWaitStop, self.hWaitDone],False,win32event.INFINITE)
            # Check to see if self.hWaitStop happened as part of Windows Service Management
            if rc == 0:
                # Stop signal encountered                    
                servicemanager.LogInfoMsg(self._svc_name_ + " - STOPPED!")  #For Event Log
                break
    
            if rc == 1:
                # Wait until worker has fully finished
                self.worker = WorkerThread(self.hWaitDone)
                self.worker.setDaemon=True
                self.worker.start()

def ctrlHandler(ctrlType):
   return True
   
if __name__ == '__main__':   
   win32api.SetConsoleCtrlHandler(ctrlHandler, True)   
   win32serviceutil.HandleCommandLine(envvar_service)

标签: pythonwindowsserviceenvironment-variables

解决方案


我认为您无法从服务中更改环境,因为这是另一个过程。您应该使用一些进程间通信来告诉主进程设置环境。

我认为最简单的方法是使用multiprocessing.Queue.

您创建一个队列并将队列作为参数提供给服务。

服务将数据放入队列 ( queue.put(data))

主进程中的一个线程从队列 ( data = queue.read()) 中读取并相应地设置环境变量。

如果您想等待正在设置的变量,您可以使用 multiprocessing.Event

请参阅多处理和 threading.Thread 文档。

https://docs.python.org/3/library/multiprocessing.html https://docs.python.org/3/library/threading.html

您必须使用多处理模块中的事件和队列类,而不是线程模块!


推荐阅读