Fijar una aplicación Java a la barra de tareas de Windows 7


Utilizo Launch4j como un wrapper para mi aplicación Java bajo Windows 7, que, a mi entender, en esencia bifurca una instancia de javaw.exe que a su vez interpreta el código Java. Como resultado, al intentar anclar mi aplicación a la barra de tareas, Windows fija javaw.exe. Sin la línea de comandos requerida, mi aplicación no se ejecutará.

Resultado de fijar una aplicación Launch4j a la barra de tareas

Como puede ver, Windows tampoco se da cuenta de que Java es la aplicación host: la aplicación en sí se describe como "Java (TM) Platform SE binary".

He intentado alterar la clave del registro HKEY_CLASSES_ROOT\Applications\javaw.exe para agregar el valor IsHostApp. Esto altera el comportamiento al deshabilitar la fijación de mi aplicación por completo; claramente no es lo que quiero.

Resultado de especificar javaw.exe como aplicación host

Después de leer sobre cómo Windows interpreta las instancias de una sola aplicación (y un fenómeno discutido en esta pregunta ), me interesé en incrustar un ID de Modelo de Usuario de aplicación (AppUserModelID) en mi Java aplicación.

Creo que puedo resolver esto pasando un único AppUserModelID a Windows. Hay un método shell32 para esto, SetCurrentProcessExplicitAppUserModelID. Siguiendo la sugerencia de Gregory Pakosz, la implementé en un intento de que mi aplicación fuera reconocida como una instancia separada de javaw.exe:

NativeLibrary lib;
try {
    lib = NativeLibrary.getInstance("shell32");
} catch (Error e) {
    Logger.out.error("Could not load Shell32 library.");
    return;
}
Object[] args = { "Vendor.MyJavaApplication" };
String functionName = "SetCurrentProcessExplicitAppUserModelID";
try {
    Function function = lib.getFunction(functionName);
    int ret = function.invokeInt(args);
    if (ret != 0) {
        Logger.out.error(function.getName() + " returned error code "
                + ret + ".");
    }
} catch (UnsatisfiedLinkError e) {
    Logger.out.error(functionName + " was not found in "
            + lib.getFile().getName() + ".");
    // Function not supported
}

Esto parece no tener ningún efecto, pero la función retorna sin error. Diagnosticar por qué es algo misterioso para mí. Alguna sugerencia?

Aplicación de trabajo

El la implementación final que funcionó es la respuesta a mi pregunta de seguimiento sobre cómo pasar el AppID usando JNA.

Había otorgado la recompensa a la brillante respuesta de Gregory Pakosz para el JNI que me puso en el camino correcto.

Como referencia, creo que el uso de esta técnica abre la posibilidad de utilizar cualquiera de las API discutidas en este artículo en una aplicación Java.

Author: Community, 2009-12-02

7 answers

No tengo Windows 7, pero aquí hay algo que podría ayudarte a comenzar:

En el lado de Java:

package com.stackoverflow.homework;

public class MyApplication
{
  static native boolean setAppUserModelID();

  static
  {
    System.loadLibrary("MyApplicationJNI");
    setAppUserModelID();
  }
}

Y en el lado nativo, en el código fuente de la `MyApplicationJNI.biblioteca dll:

JNIEXPORT jboolean JNICALL Java_com_stackoverflow_homework_MyApplication_setAppUserModelID(JNIEnv* env)
{
  LPCWSTR id = L"com.stackoverflow.homework.MyApplication";
  HRESULT hr = SetCurrentProcessExplicitAppUserModelID(id);

  return hr == S_OK;
}

Su pregunta pedía explícitamente una solución del JNI. Sin embargo, dado que su aplicación no necesita ningún otro método nativo, jna es otra solución que le ahorrará escribir código nativo solo por el bien de reenviar a la api de Windows. Si decida ir jna, preste atención al hecho de que SetCurrentProcessExplicitAppUserModelID() está esperando una cadena UTF-16.

Cuando funciona en su sandbox, el siguiente paso es agregar la detección del sistema operativo en su aplicación, ya que SetCurrentProcessExplicitAppUserModelID() obviamente solo está disponible en Windows 7:

  • puede hacerlo desde el lado de Java comprobando que System.getProperty("os.name"); devuelve "Windows 7".
  • si compilas desde el pequeño fragmento de código JNI que te di, puedes mejorarlo cargando dinámicamente la biblioteca shell32.dll usando LoadLibrary entonces recuperando el puntero de la función SetCurrentProcessExplicitAppUserModelID usando GetProcAddress. Si GetProcAddress devuelve NULL, significa que el símbolo no está presente en shell32 por lo tanto no es Windows 7.

EDITAR: Solución JNA.

Referencias:

 20
Author: Gregory Pakosz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:08

Hay una biblioteca Java que proporciona las nuevas características de Windows 7 para Java. Se llama J7Goodies por Código Strix. Las aplicaciones que lo utilizan se pueden fijar correctamente a la barra de tareas de Windows 7. También puede crear sus propias listas de salto, etc.

 5
Author: torn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-02 14:42:43

Intenta usar JSmooth. Siempre uso este. En JSmooth hay una opción bajo Skeleton por Windowed Wrapper llamada

Lauch java app in exe process

Ver en esta imagen.

También se pueden pasar argumentos de línea de comandos.
Creo que esto puede ser una solución para ti.

Martijn

 4
Author: Martijn Courteaux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-08 14:18:14

He implementado el acceso al método SetCurrentProcessExplicitAppUserModelID usando JNA y funciona bastante bien cuando se usa como sugiere la documentación de MSDN. Nunca he usado la api de JNA de la manera que lo has hecho en tu fragmento de código. Mi implementación sigue el uso típico de JNA en su lugar.

Primero la definición de la interfaz de Shell32:

interface Shell32 extends StdCallLibrary {

    int SetCurrentProcessExplicitAppUserModelID( WString appID );

}

Luego usando JNA para cargar Shell32 y llamar a la función:

final Map<String, Object> WIN32API_OPTIONS = new HashMap<String, Object>() {
    {
       put(Library.OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
       put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
    }
};
Shell32 shell32 = (Shell32) Native.loadLibrary("shell32", Shell32.class,
           WIN32API_OPTIONS);
WString wAppId = new WString( "Vendor.MyJavaApplication" );
shell32.SetCurrentProcessExplicitAppUserModelID( wAppId );

Muchas de las API en el último artículo que mencionaste hacer uso de Windows COM que es bastante difícil de usar directamente con JNA. He tenido cierto éxito creando una DLL personalizada para llamar a estas API (por ejemplo. usando el SHGetPropertyStoreForWindow para establecer un ID de aplicación diferente para una ventana de submódulo) que luego uso JNA para acceder en tiempo de ejecución.

 4
Author: The_Fire,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-22 18:32:14

SetCurrentProcessExplicitAppUserModelID (o SetAppID()) de hecho haría lo que estás tratando de hacer. Sin embargo, podría ser más fácil modificar su instalador para establecer el AppUserModel.ID propiedad en su atajo-citando del documento Application User Model ID mencionado anteriormente:

En el System.AppUserModel.ID propiedad del archivo de acceso directo de la aplicación. Un atajo (como IShellLink, CLSID_ShellLink, o a .archivo lnk) soporta propiedades a través de IPropertyStore y otros mecanismos de configuración de propiedades utilizados en todo el Shell. Esto permite que la barra de tareas identifique el atajo adecuado para pin y asegura que las ventanas que pertenecen al proceso estén asociadas apropiadamente con ese botón de la barra de tareas. Nota: El System.AppUserModel.ID la propiedad debe aplicarse a un acceso directo cuando se crea ese acceso directo. Cuando se utiliza Microsoft Windows Installer (MSI) para instalar la aplicación, la tabla MsiShortcutProperty permite el AppUserModelID se aplicará al acceso directo cuando se cree durante la instalación.

 3
Author: Eric Brown,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-11 19:24:28

La última biblioteca jna-platform ahora incluye enlaces JNA para SetCurrentProcessExplicitAppUserModelID:

Https://github.com/java-native-access/jna/pull/680

 1
Author: rednoah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-22 12:33:13

Arreglé el mío sin ninguna configuración de ID. Hay una opción en Launch4J si lo está usando y dice que lo hace entonces...

Puede cambiar el encabezado a JNI Gui y luego envolverlo alrededor del jar con el JRE. Lo bueno es que funciona .exe en el proceso ahora en lugar de ejecutar javaw.exe con tu tarro. Probablemente lo hace bajo el capó (no estoy seguro). También he notado también que se necesita alrededor de 40-50% menos de recursos de CPU que es aún mejor!

Y la fijación funciona bien y todas las características de esa ventana están habilitadas.

Espero que ayude a alguien, ya que pasé casi 2 días tratando de resolver ese problema con mi aplicación javafx sin decorar.

 0
Author: LazerBanana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-19 11:25:33