为多台计算机添加单个python可执行文件到Windows系统PATH?

时间:2016-12-12 19:47:40

标签: python windows exe pyinstaller gpo

我已经创建了一个命令行程序,我想分发给一些工作人员。让他们全部安装python解释器是不切实际的。因此,我使用PyInstaller创建了一个.exe文件。然而,我开始意识到,大多数人甚至不知道如何导航到.exe所在的目录,以便调用它。 (到目前为止,我还没有弄清楚如何在点击时让程序运行。)有没有办法让程序在第一次运行时将它自己添加到用户sys PATH中,或者这样做需要安装人员?谢谢!

1 个答案:

答案 0 :(得分:1)

常见陷阱是阅读PATH env。使用os.environ('PATH')变量。这将是错误,因为此变量包含用户&系统路径混合在一起。这是PATH变量的一个特例。

您需要做的是从注册表(用户部分)获取PATH env变量,在需要时更新它,然后将其写回。

您可以使用winreg模块实现该功能,修改用户PATH环境变量(如果此特定用户不存在则创建)

  • 阅读用户PATH变量
  • 如果存在,则标记路径(否则,路径列表默认为空)
  • 计算当前模块的路径(使用os.path.dirname(__file__)
  • 检查是否已经在路径中,如果是,退出(我打印路径列表,以便你可以测试)
  • 创建/更新PATH用户环境。必要时带有更新路径列表的变量

代码:

import winreg,os

script_directory = os.path.dirname(__file__)

paths = []
key_type = winreg.REG_EXPAND_SZ  # default if PATH doesn't exist
try:
    keyQ = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Environment', 0, winreg.KEY_QUERY_VALUE)
    path_old, key_type = winreg.QueryValueEx(keyQ, "PATH")
    winreg.CloseKey(keyQ)
    paths = path_old.split(os.pathsep)
except WindowsError:
    pass

if script_directory in paths:
    # already set, do nothing
    print(paths)
else:
    # add the new path
    paths.append(script_directory)
    # change registry
    keyQ = winreg.OpenKey(winreg.HKEY_CURRENT_USER, 'Environment', 0, winreg.KEY_WRITE)
    winreg.SetValueEx(keyQ, 'PATH', 0, key_type, os.pathsep.join(paths))
    winreg.CloseKey(keyQ)

请注意,用户必须注销/登录才能使更改生效。另一种解决方案是在setx变量上调用PATH。系统调用,丑陋但立即生效。

# change registry with immediate effect
import subprocess
subprocess.call(["setx","PATH",os.pathsep.join(paths)])

或者,由于eryksun的礼貌,一些python代码将注册表更改传播到新进程。无需注销,无需丑陋setx,只需使用以下代码致电broadcast_change('Environment')

import ctypes

user32 = ctypes.WinDLL('user32', use_last_error=True)

HWND_BROADCAST = 0xFFFF
WM_SETTINGCHANGE = 0x001A
SMTO_ABORTIFHUNG = 0x0002
ERROR_TIMEOUT = 0x05B4

def broadcast_change(lparam):
    result = user32.SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE,
                0, ctypes.c_wchar_p(lparam), SMTO_ABORTIFHUNG, 1000, None)
    if not result:
        err = ctypes.get_last_error()
        if err != ERROR_TIMEOUT:
            raise ctypes.WinError(err)

(似乎我必须用最后一点重构我自己的一些代码:))

ENV。变量读取代码取自此处:How to return only user Path in environment variables without access to Registry?