【问题标题】:Programmatically adding a directory to Windows PATH environment variable以编程方式将目录添加到 Windows PATH 环境变量
【发布时间】:2010-12-27 12:12:25
【问题描述】:

我正在编写一个 Win32 DLL,其中包含一个将目录添加到 Windows PATH 环境变量(将在安装程序中使用)的函数。

在 DLL 运行后查看 Regedit 或控制面板中的环境变量显示我的 DLL 已成功添加到 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\EnvironmentHKEY_CURRENT_USER\Environment 的路径。

但是当我启动一个新的命令提示符(在运行 DLL 之后)时,我添加的目录不会显示在 echo %PATH% 的输出中,并且我无法通过键入其名称来访问该目录中的可执行文件.

我认为我的程序在通知系统 PATH 已更改方面做得不好,或者它可能在更改完全生效之前通知他们。我读到了article by Microsoft,它说在更改环境变量后广播WM_SETTINGCHANGE 消息,我正在使用以下代码:

DWORD result2 = 0;
LRESULT result = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0,
    (LPARAM)"Environment", SMTO_ABORTIFHUNG, 5000, &result2);
if (result == 0){ /* ... Display error message to user ... */ }

我的调用顺序是:RegCreateKeyExRegSetValueExRegCloseKeySendMessageTimeout

如果我在控制面板的“环境变量”窗口中按“确定”,我的 DLL 对 PATH 所做的更改会显示在新创建的命令提示符中,因此控制面板正在做一些事情来传播 PATH变化;我想弄清楚它是什么,然后做同样的事情。

有人知道我应该怎么做吗?

我运行的是 64 位 Windows Vista,但我希望它能够在所有 Windows XP、Vista 和 Windows 7 操作系统上运行。

更新: 我上面发布的代码的问题是我没有在“环境”字符串上放置 L 前缀。虽然它没有在我能找到的 Microsoft 文档的任何地方明确说明,但 LPARAM 需要是指向 WCHAR 字符串(2 字节字符)的指针,而不是 CHAR 字符串,这是 Visual Studio 的编译器默认生成的当我写一个字符串文字时。我的问题的解决方案是将“环境”更改为 L“环境”。 (我以为在发布这个问题之前我已经尝试过了,但显然我没有正确尝试!)但是任何想要完成这项任务的 C++ 解决方案的人都应该看看 Dan Moulding 的答案。

【问题讨论】:

  • 您可能想查看一些开源安装程序的源代码,例如NSIS 或 Inno 设置。他们正确地执行此通知。
  • 一个小小的快速提示,您可以在命令提示符下显示 PATH 内容,只需键入“path”并按 Enter。每次测试可节省 7 次击键(按 shift 键计算 9 次)!
  • 啊,你必须构建一个 Unicode 应用程序,所以 windows.h 引入了 Unicode 版本的 API (SendMessageTimeoutW),这需要 LPCWSTR 来代替 LPARAM LPCSTR。我认为如果您想同时支持“ANSI”和 Unicode 构建,那么正确的做法是使用 LPCTSTR(即通过 _T() 宏)。我的图书馆可能会使用更新来使其“识别 Unicode”。它目前仅支持使用“ANSI”设置环境变量,如果您的路径中需要包含西里尔字符的目录,这可能会出现问题。
  • 我怀疑 SendMessageTimeout[W] 是否会将 LPARAM 从一种字符串类型转换为另一种,因为据我所知 lparam 只是一个可以指向任何东西的通用指针。因此,当您生成字符串时,您必须确保它是 Unicode 格式,然后再将其提供给 SendMessageTimeOut[W]。我正在使用 Visual Studio 2008,包括 windows.h,并且没有对 Unicode 做任何特别的事情。在我的程序中,像“Environment”这样的文字字符串默认是 ANSI(这导致了我的问题)。您使用的是什么编译器,默认情况下文字字符串是什么编码?
  • 对,这个函数有两个版本:SendMessageTimeoutASendMessageTimeoutW。 “A”版本的 LPARAM 预计为 LPCSTR,而“W”版本的 LPARAM 预计为 LPCWSTR。如果您只通过SendMessageTimeout 调用该函数,那么您确实应该将LPARAM 包含在_T() 宏中,即:_T("Environment")。然后,如果 windows.h 将SendMessageTimeout 扩展为SendMessageTimeoutW,它也会将_T("Environment") 扩展为L"Environment"。否则(对于非 Unicode)它们分别扩展为 SendMessageTimeoutA"Environment"

标签: c++ winapi registry environment-variables environment


【解决方案1】:

我有一个程序调用你的相同的 Win32 API 来更新环境,它工作正常。

需要注意的一点是如何打开命令提示符。

如果您这样做打开命令提示符:

Start -> Run -> cmd.exe

那么提示中的环境显示设置了新变量。

但是,我的键盘上还有一个可编程功能键,我已将其设置为运行cmd.exe 进程。如果我通过该功能键打开命令提示符,然后键入env,它不会显示已设置的变量。

我不确定为什么它的工作方式不同,但它一定与cmd.exe 进程的启动方式有关(尽管两者都在我的用户名下运行,而不是SYSTEM)。

你是如何打开命令提示符的?

【讨论】:

  • 管理您的可编程功能键的程序正在使用自己的环境副本启动 CMD.EXE,如果您在更改路径后没有重新启动它,则该环境尚未更新。
  • 有趣。我通过单击开始菜单中的“命令提示符”来启动命令提示符。这是 Windows 附带的快捷方式,位于附件菜单中,我将其“固定”到开始菜单。目标是 %SystemRoot%\system32\cmd.exe
  • @Todd - 是的,听起来不错。好分析! @David - 如果您从 Start->Run 运行 cmd.exe 有什么不同吗?
  • 在我的系统上,从开始菜单快捷方式运行它与使用开始->运行没有区别。这是有道理的,因为主“shell”(explorer.exe)由于 WM_SETTINGCHANGE 广播而更新了它的环境。它产生的任何进程(无论是通过 Start->Run 还是其他方式)都应该继承该更新的环境。
  • @LeopardSkinPillBoxHat,不知道有没有区别;我已经解决了这个问题,现在它工作正常。感谢您的建议。
【解决方案2】:

事实证明,太阳底下真的没有什么新鲜事。这之前已经做过,至少一次。由我。我创建了一个与您描述的完全相同的 DLL(用于从 NSIS 安装程序修改路径)。它被Visual Leak Detector 安装程序使用。

该 DLL 称为 editenv.dll。 The source 可在 github 上找到。我刚刚测试了安装程序,它更新了 system PATH 环境变量,没问题。根据您所写的内容,我认为没有任何明显错误的地方。我也没有看到任何明显的缺失。但可能值得一看 editenv.dll 源代码(您最感兴趣的是EnvVar.cpp 中的EnvVar::set(),可能还有editenv.cpp 中的pathAdd()pathRemove() C API)。

【讨论】:

  • 感谢丹!即使我无法通过查看您的代码找到我的具体解决方案,但您的回答将对这个问题的未来读者非常有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-17
  • 2019-07-28
  • 1970-01-01
  • 2019-08-28
  • 2014-10-23
  • 2012-09-14
相关资源
最近更新 更多