使用python(service中运行的)操作windows环境变量踩坑

前言

这两天在写一个python的插件,有一个奇葩的要求那就是需要使用python往系统环境变量中添加记录,然后需要立即生效(不允许重启服务器的情况下实现),恩想法是美好的,现实是残酷的。linux平台下没有任何问题(因为linux原生就支持环境变量立即生效,只需要修改.bashrc然后重启我的进程就OK了),但是windows就没有那么简单了,我玩了一个周末才踩完所有坑。

实现方法 (踩坑)

源码如下,这种方法是使用python操作windows注册表以达到修改环境变量的目的。

#python2 使用 _winreg
import  winreg as wg

key_test = wg.OpenKey(wg.HKEY_LOCAL_MACHINE,r"SYSTEM\CurrentControlSet\Control\Session Manager\Environment",0,wg.KEY_ALL_ACCESS)
path_str = wg.QueryValueEx(key_test,'path')
git_path='C:\\project\\PortableGit\\bin'

if path_str[0].find('PortableGit') < 0:
    path_str_new = path_str[0] + ';' + git_path
    wg.SetValueEx(key_test,'path','',path_str[1],path_str_new)
    wg.FlushKey(key_test)
    wg.CloseKey(key_test)

恩这种方法虽然能添加到环境变量(由于主进程是使用管理员身份运行的所以这里不用担心权限问题),但是最大的问题是这个环境变量不能直接生效(本来这个问题重启一下就OK了但是我做的东西是要分发到其他服务器上的让客户都一一重启服务器体验就完全没法形容了,如果不解决可能直接影响用户的安装量),网上都说使用广播消息的方式通知所有窗口,但是问题来了我这个程序是网页访问的,他根本就没有窗口,有且只有一个 service 这个方法我也试了很多次并不能解决我的问题。网上的广播方式我贴在下面兴许可以帮助到大家。

#修改注册表后,更新生效
def refresh():
  HWND_BROADCAST = 0xFFFF
  WM_SETTINGCHANGE = 0x1A
  SMTO_ABORTIFHUNG = 0x0002
  result = ctypes.c_long()
  SendMessageTimeoutW = ctypes.windll.user32.SendMessageTimeoutW
  SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, u'Environment', SMTO_ABORTIFHUNG, 10, ctypes.byref(result))
refresh()

解决思路

为了这个问题我折腾了2天(昨天+今天大多数时间都在折腾这个问题)最后都无果,具体表现就是python能添加成功但是就是无法立即生效(比如我的示例中的git,打款cmd执行git 就会提示“git 不是可执行程序或命令”) ,搞了很久最后灵机一动(我是谁?我为什么要打开系统cmd?)我能否在我的原有的service运行的程序中试一试能否使用git呢?抱着试一试的心态我执行了 os.system(“git”) ,但是最后结果令人心灰意冷,python返回的是 1 (也就是不成功),最开始我怀疑是不是os.system有问题(虽然这个问题基本上不会出现,但是码农嘛所有东西都是要怀疑一下的)试了一下 os.system(“dir”) ,这一次python返回了 0 也就是成功了。emmm 调用没有问题那就是系统环境变量还是没有生效咯?然后各种百度,找到一种解决方法,那就是使用os.environ修改系统变量(只针对当前程序有效)。

    os.environ['PATH']=os.environ.get('PATH')+";"+git_path

然而结果还是令人失望(但是又学到一种新的操作环境变量的方法)。最后还是抱着再折腾一下的心态各种折腾最开始的那种方案,最后功夫不负有心人 当我使用 os.system(“git --version”) 的时候成功返回了 0,尼玛坑爹呢。

ps

以上方法证明winreg 操作注册表的方法还有效的(虽然系统中没有立即生效但是在当前python web服务中环境变量已经生效),我这边的系统对整个系统是否生效其实并没有那么强的要求只要能在我的程序中执行我的exe程序命令就可以了,如果非要整个系统全部都生效那还是老老实实重启系统吧,以我折腾2天的经验告诉我除了重启别无他法。
目前 os.system(‘git pull’) 命令已经完全没有问题了。

意外发现

无意间发现python改变环境变量后,对其他service还是已经生效的,比如我当前service添加的环境变量,我service2中想要使用,那么只需要重启一下service2 服务后即可使用无需重启系统。

最后

虽然这个问题困扰了我2天时间,在我看来又涨了不少见识,学习了不少东西,生而二猿就应该折腾到最后一刻。

爱折腾的码农 敬上