【问题标题】:Program uses outdated (not current) env variable value程序使用过时的(不是当前的)环境变量值
【发布时间】:2012-03-09 13:34:54
【问题描述】:

我有一个内部使用 java 的 C++ 程序(通过我的 C++ dll 包装内部使用 jvm.dll 的 WebLogic jsmc.dll)。

当我在运行我的程序之前设置 CLASSPATH 时,所有的 JAR 库都被找到并且程序可以正常运行。 当我在运行程序之前设置 CLASSPATH 时,找到 JAR,这当然是意料之中的。

现在,当我在运行我的程序之前设置了 CLASSPATH,但是 clear 程序代码中的这个 CLASSPATH 环境变量 加载我使用 java 的 dll 之前,很奇怪发生:仍然找到所有 JAR,并且程序运行起来好像一切正​​常。我已经通过几种方式验证了 CLASSPATH 确实从 env 变量中删除(例如,通过使用 ProcessExplorer 或通过打印其值)。

问题:

你能向我解释一下这种行为吗?我想知道为什么 java 会忽略我设置的 CLASSPATH,但是 怎么可能 java 看到旧的 CLASSPATH 值,而不是当前的值?我强调,java 不可能以某种方式存储旧的 CLASSPATH 值,因为在旧值可用时未加载 java。

如何让 java 尊重进程环境变量的变化?

详情:

上面的问题只是我为探索我的真正问题所做的简化。我正在尝试从程序内部设置 CLASSPATH 并避免在外部设置它。但是java使用的是外部设置的CLASSPATH,不是我在程序里面设置的。

我使用 Windows API(GetEnvironmentVariableA、SetEnvironmentVariableA)读取和设置环境变量值。我已经验证了程序进程环境变量在以这种方式设置后确实发生了变化。在调用任何 java 方法之前,我什至从使用 java 的 dll 中打印了 CLASSPATH 值。我使用 ProcessMonitor 检查了 jvm.dll 确实在 CLASSPATH 被删除之后加载。我还试图排除从父进程读取 CLASSPATH 的可能性。现在我很确定在加载 jvm.dll 时,CLASSPATH 已经从进程环境中删除了。

我尝试了 Visual C++ 2010 测试程序和 HP LoadRunner C 编译器 (mmdrv.exe) vuser 脚本,结果相同。 LoadRunner 是我需要解决这个问题的主要原因。

【问题讨论】:

  • 您是否仔细检查过正在使用的 CLASSPATH 只能来自父进程(例如,命令外壳),而不是来自例如存储在注册表中的默认环境变量值?
  • 我设法让代码在 Visual Studio 中运行。将“jmsc.dll”标记为延迟加载就足够了(属性 -> 链接器 -> 输入 -> 延迟加载的 Dlls =“jmsc.dll”)。不幸的是,相同的代码在 LoadRunner 中的工作方式不同 - 即使 dll 被延迟加载,加载的 dll 仍然使用原始的 CLASSPATH,而不是修改后的。 :-((( 可能在 lr_load_dll() 的实现中有些怪癖。
  • 一种可能的解释是 jmsc.dll 中包含或引用的 C 运行时可能会在启动时制作自己的环境变量副本。我不确定它们会如何在 jvm.dll 中结束:也许它使用相同的运行时,或者 jmsc.dll 出于某种原因明确地将 CLASSPATH 传递给 jvm.dll。您需要使用调试器查看它才能确定。
  • 我已经测试过了。 1)jvm.dll 必须在创建 JVM 实例时显式分配类路径,请参见下面 Peter Cetinski 的回答和以下 cmets。 2)有人向我建议 MCVCRT.DLL 保留自己的环境副本。所以我把SetEnvironmentVariableA()改成了_putenv(),没有效果。 _putenv 在 msvcrt 中实现。
  • 但也许您的程序没有使用与 jsmc.dll 相同的运行时实例?

标签: java windows visual-c++ classpath environment-variables


【解决方案1】:

问题是由 C 运行时以某种方式缓存环境变量引起的。当我尝试使用系统函数 SetEnvironmentVariableA() 修改 CLASSPATH 时,jmsc.dll 从 C 运行时缓存中读取了 CLASSPATH。 C 运行时尝试将其缓存与进程环境中的实际值同步,但显然不是很成功。为了更改 CLASSPATH,我有必要用 C 运行时对 _putenv() 的调用替换对 SetEnvironmentVariableA() 的系统调用。

但是还有另一个问题。我的代码使用了各种版本的 C 运行时,每个版本都有自己的环境缓存。我的 VC 代码与 msvcr100.dll 相关联,而 jmsc.dll(实例化 Java VM)使用 msvcrt.dll。解决方案是将我的代码也链接到 msvcrt.dll,以便我的代码使用来自 jmsc.dll 读取的同一 C 运行时的 _putenv() 设置 CLASSPATH。

感谢 Harry Johnston 提供重要提示,感谢 Peter Cetinski 提供宝贵信息。

【讨论】:

    【解决方案2】:

    从 C++ 调用新的 JVM 进程时,不应依赖 CLASSPATH 环境变量。 JNI 接口提供了一种在启动时指定 JVM 的类路径的机制。

    http://java.sun.com/docs/books/jni/html/invoke.html#28719

    【讨论】:

    • 好主意:)。但是,我没有显式创建 JVM,因此无法设置 JavaVMInitArgs 的参数。我只是使用创建自己的 JVM 的 jmsc.dll 库(JMS 的 Weblogic C-API)。如果我正确理解了您的链接,如果我使用 JNI_CreateJavaVM() 创建了一个 JVM,那将是一个新的 JVM 实例,与 jmsc.dll 创建的不同。
    • 你是对的,这将是一个不同的过程。我确信 jmsc.dll 只是调用 JNI_CreateJavaVM。
    • 好吧,我至少应该测试一下在创建一个新的 JVM 并在它之前的运行时清除类路径时会发生什么。
    • 结果:JNI_CreateJavaVM() 仅在当前目录中搜索类文件,除非它的类路径是通过 JavaVMOption 结构明确设置的(参见上面的链接)。 IE。 CLASSPATH 环境变量没有影响。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-23
    相关资源
    最近更新 更多