mega - Fijar una aplicación Java a la barra de tareas de Windows 7
launch4j 3.12 win32 (7)
Uso Launch4j como envoltorio para mi aplicación Java bajo Windows 7, que, a mi entender, en esencia forma una instancia de javaw.exe
que a su vez interpreta el código Java. Como resultado, cuando intento fijar mi aplicación a la barra de tareas, Windows en su lugar establece javaw.exe
. Sin la línea de comando requerida, mi aplicación no se ejecutará.
Como puede ver, Windows tampoco se da cuenta de que Java es la aplicación de host: la aplicación en sí misma se describe como "Java (TM) Platform SE binary".
He intentado modificar la clave de 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
Después de leer acerca de cómo Windows interpreta las instancias de una sola aplicación (y un fenómeno discutido en esta pregunta ), me interesó incorporar un ID de modelo de usuario de aplicación (AppUserModelID) en mi aplicación Java.
Creo que puedo resolver esto pasando un AppUserModelID
único a Windows. Hay un método de shell32
para esto, SetCurrentProcessExplicitAppUserModelID
. Siguiendo la sugerencia de Gregory Pakosz, lo implementé en un intento de que mi aplicación se reconociera 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 regresa sin error. Diagnosticar por qué es algo así como un misterio para mí. ¿Alguna sugerencia?
Implementación de trabajo
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 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.
Arreglé el mío sin ninguna configuración de ID. Hay una opción en Launch4J si la está usando y usted dice que sí ...
Puede cambiar el encabezado a JNI Gui y luego envolverlo alrededor del jar con JRE. Lo bueno es que ahora ejecuta .exe en el proceso al ejecutar javaw.exe con tu jar. Probablemente lo haga bajo el capó (no estoy seguro). También me he dado cuenta de que se necesita alrededor de 40-50% menos de recursos de CPU, ¡lo que es aún mejor!
Y la fijación funciona bien y todas las características de la 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.
Hay una biblioteca Java que proporciona las nuevas características de Windows 7 para Java. Se llama J7Goodies por Strix Code . Las aplicaciones que lo utilizan se pueden fijar correctamente a la barra de tareas de Windows 7. También puedes crear tus propias listas de salto, etc.
Implementé el acceso al método SetCurrentProcessExplicitAppUserModelID utilizando JNA y funciona bastante bien cuando se usa como lo sugiere la documentación de MSDN. Nunca utilicé la API de JNA de la forma que tienes en tu fragmento de código. Mi implementación sigue el uso típico de JNA .
Primero, la definición de la interfaz Shell32:
interface Shell32 extends StdCallLibrary {
int SetCurrentProcessExplicitAppUserModelID( WString appID );
}
Luego, usa 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 del último artículo que mencionas usan Windows COM, que es bastante difícil de usar directamente con JNA. He tenido cierto éxito al crear una DLL personalizada para llamar a estas API (por ejemplo, usando SHGetPropertyStoreForWindow para establecer una ID de aplicación diferente para una ventana de submódulo) a la que luego uso JNA para acceder en tiempo de ejecución.
Intenta usar JSmooth . Yo uso siempre este. En JSmooth hay una opción en Skeleton
by Windowed Wrapper
llamada
La aplicación java Lauch en el proceso exe
Ver en esta imagen.
También se pueden pasar argumentos de línea de comando.
Creo que esta puede ser una solución para ti.
Martijn
La última jna-platform
ahora incluye enlaces JNA para SetCurrentProcessExplicitAppUserModelID
:
No tengo Windows 7 pero aquí hay algo que podría ayudarte a comenzar:
En el lado de Java:
package com..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 biblioteca `MyApplicationJNI.dll:
JNIEXPORT jboolean JNICALL Java_com__homework_MyApplication_setAppUserModelID(JNIEnv* env)
{
LPCWSTR id = L"com..homework.MyApplication";
HRESULT hr = SetCurrentProcessExplicitAppUserModelID(id);
return hr == S_OK;
}
Su pregunta solicitó explícitamente una solución 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 reenviar a la API de Windows. Si decide ir a jna, preste atención al hecho de que SetCurrentProcessExplicitAppUserModelID()
espera una cadena UTF-16.
Cuando funciona en su entorno limitado, 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 compila a partir del pequeño fragmento de JNI que proporcioné, puede mejorarlo cargando dinámicamente la biblioteca de
shell32.dll
utilizandoLoadLibrary
y obteniendo el puntero de funciónSetCurrentProcessExplicitAppUserModelID
utilizandoGetProcAddress
. SiGetProcAddress
devuelveNULL
, significa que el símbolo no está presente enshell32
por lo tanto, no es Windows 7.
EDITAR: Solución JNA .
Referencias
- El libro de JNI para más ejemplos de JNI
- jna
SetCurrentProcessExplicitAppUserModelID (o SetAppID ()) de hecho haría lo que está intentando hacer. Sin embargo, podría ser más fácil modificar su instalador para establecer la propiedad AppUserModel.ID en su acceso directo, citando el documento ID de modelo de usuario de la aplicación mencionado anteriormente:
En la propiedad System.AppUserModel.ID del archivo de acceso directo de la aplicación. Un acceso directo (como un enlace IShellLink, CLSID_ShellLink o .lnk) admite propiedades a través de IPropertyStore y otros mecanismos de establecimiento de propiedades utilizados en todo el Shell. Esto permite que la barra de tareas identifique el atajo correcto para fijar y garantiza que las ventanas que pertenecen al proceso estén asociadas de manera apropiada con ese botón de la barra de tareas. Nota: La propiedad System.AppUserModel.ID se debe aplicar a un acceso directo cuando se crea ese acceso directo. Cuando se utiliza el instalador de Microsoft Windows (MSI) para instalar la aplicación, la tabla MsiShortcutProperty permite que AppUserModelID se aplique al acceso directo cuando se crea durante la instalación.