El programa usa un valor de variable env desactualizado (no actual)

Tengo un programa de C++ que usa Java internamente (a través de mi dll de C++ que envuelve WebLogic jsmc.dll que usa internamente jvm.dll).

Cuando configuro CLASSPATH antes de ejecutar mi programa, se encuentran todas las bibliotecas JAR y el programa funciona correctamente. Cuando lo hago no establecer el CLASSPATH antes de ejecutar mi programa, los JAR son no encontrado, que por supuesto se espera.

Ahora, cuando configuro CLASSPATH antes de ejecutar mi programa, pero limpiar esta variable de entorno CLASSPATH dentro del código del programa antes cargando mi dll que usa java, sucede algo extraño: todavía se encuentran todos los JAR y el programa funciona como si todo estuviera bien. He verificado de varias maneras que CLASSPATH realmente se elimina de las variables env (por ejemplo, usando ProcessExplorer o imprimiendo su valor).

PREGUNTA:

¿Puedes explicarme este comportamiento? estoy no preguntándome por qué Java ignora el CLASSPATH que configuré, pero como es posible que java ve el antiguo valor CLASSPATH, no el actual? Hago hincapié en que no es posible que java almacene el valor antiguo de CLASSPATH de alguna manera porque java no se cargó en el momento en que el valor anterior estaba disponible.

¿Cómo puedo hacer que java respete los cambios en las variables env del proceso?

DETALLES:

El problema anterior es solo una simplificación que hice para explorar mi problema real. Estoy tratando de configurar CLASSPATH desde dentro del programa y evitar tenerlo configurado externamente. Pero Java usa el CLASSPATH configurado externamente, no el que configuré dentro del programa.

Leo y configuro los valores de las variables env usando la API de Windows (GetEnvironmentVariableA, SetEnvironmentVariableA). He verificado que las variables de entorno del proceso del programa realmente cambian después de configurarlas de esta manera. Incluso imprimí el valor CLASSPATH del dll que usa java, antes de llamar a cualquier método java. Verifiqué usando ProcessMonitor que jvm.dll está realmente cargado después de se elimina el CLASSPATH. También traté de excluir la posibilidad de que CLASSPATH se lea desde el proceso principal. Ahora estoy bastante seguro de que en el momento en que se carga jvm.dll, CLASSPATH ya se eliminó del entorno del proceso.

Probé un programa de prueba de Visual C++ 2010 y la secuencia de comandos Vuser del compilador C de HP LoadRunner (mmdrv.exe), con el mismo resultado. LoadRunner es la razón principal por la que necesito resolver este problema.

preguntado el 09 de marzo de 12 a las 13:03

¿Ha verificado dos veces que el CLASSPATH que se usa solo puede provenir del proceso principal (por ejemplo, el shell de comandos) y no de, por ejemplo, los valores de variables de entorno predeterminados almacenados en el registro? -

Logré que el código funcionara desde Visual Studio. Fue suficiente marcar "jmsc.dll" como cargado de forma diferida (Propiedades -> Enlazador -> Entrada -> Dlls cargados con retraso = "jmsc.dll"). Desafortunadamente, el mismo código no funciona de la misma manera desde LoadRunner: aunque el dll se carga con pereza, el dll cargado todavía usa el CLASSPATH original, no el modificado. :-((( Probablemente algunas peculiaridades en la implementación de lr_load_dll(). -

Una posible explicación es que el tiempo de ejecución de C contenido en jmsc.dll o al que hace referencia podría estar haciendo su propia copia de las variables de entorno al inicio. No estoy seguro de cómo terminarían en jvm.dll: tal vez usa el mismo tiempo de ejecución, o tal vez jmsc.dll está pasando explícitamente CLASSPATH a jvm.dll por alguna razón. Tendrías que mirarlo con un depurador para estar seguro. -

Ya he probado esto. 1) A jvm.dll se le debe asignar el classpath explícitamente al crear la instancia de JVM, consulte la respuesta de Peter Cetinski a continuación y los siguientes comentarios. 2) Alguien me sugirió que MCVCRT.DLL guarda su propia copia del entorno. Así que cambié SetEnvironmentVariableA() a _putenv(), sin ningún efecto. _putenv está implementado en msvcrt. -

¿Pero quizás su programa no está usando la misma instancia del tiempo de ejecución que jsmc.dll? -

2 Respuestas

El problema se debió a que el tiempo de ejecución de C de alguna manera almacena en caché las variables de entorno. Mientras intentaba modificar CLASSPATH usando la función del sistema SetEnvironmentVariableA(), jmsc.dll leyó CLASSPATH del caché de tiempo de ejecución de C. El tiempo de ejecución de C intenta sincronizar su caché con los valores reales en el entorno del proceso, pero evidentemente no tiene mucho éxito. Fue necesario para mí reemplazar la llamada del sistema a SetEnvironmentVariableA() con la llamada a _putenv() desde el tiempo de ejecución de C para cambiar CLASSPATH.

Pero había otro problema. Mi código usó varias versiones de C-runtime, cada una con su propio entorno de caché. Mi código de VC se vinculó con msvcr100.dll, mientras que jmsc.dll (que crea una instancia de Java VM) usa msvcrt.dll. La solución fue vincular mi código a msvcrt.dll también, de modo que mi código establezca CLASSPATH usando _putenv() desde el mismo tiempo de ejecución de C que lee jmsc.dll.

Gracias a Harry Johnston para la pista crucial, y Pedro Cetinski para información valiosa.

respondido 14 mar '12, 09:03

No debe confiar en la variable de entorno CLASSPATH al invocar un nuevo proceso JVM desde C++. La interfaz JNI proporciona un mecanismo para especificar el classpath de la JVM al inicio.

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

respondido 09 mar '12, 14:03

Buena idea :). Sin embargo, no estoy creando la JVM explícitamente, por lo que no puedo configurar los parámetros de JavaVMInitArgs. Solo uso la biblioteca jmsc.dll (la C-API de Weblogic para JMS) que crea su propia JVM. Si entiendo su enlace correctamente, si creé una JVM usando JNI_CreateJavaVM(), sería una nueva instancia de JVM, diferente de la que crea jmsc.dll. - xarx

Tienes razón, sería un proceso diferente. Estoy seguro de que jmsc.dll simplemente está llamando a JNI_CreateJavaVM. - Pedro Cetinski

Bueno, al menos probaré lo que sucede al crear una nueva JVM y borrar el classpath en tiempo de ejecución inmediatamente antes. - xarx

Resultado: JNI_CreateJavaVM() busca archivos de clase solo en el directorio actual, a menos que su classpath se establezca explícitamente a través de la estructura JavaVMOption (consulte el enlace anterior). Es decir, la variable de entorno CLASSPATH no tiene influencia. - xarx

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.