Закрепление приложения Java на панели задач Windows 7 - PullRequest
35 голосов
/ 02 декабря 2009

Я использую Launch4j в качестве оболочки для моего Java-приложения под Windows 7, которое, на мой взгляд, по сути разветвляет экземпляр javaw.exe, который, в свою очередь, интерпретирует код Java. В результате при попытке прикрепить мое приложение к панели задач Windows вместо этого выводит javaw.exe. Без обязательной командной строки мое приложение не будет работать.

Result of pinning a Launch4j application to the taskbar

Как вы можете видеть, Windows также не осознает, что Java является хост-приложением: само приложение описывается как "двоичная платформа Java (TM) Platform SE".

Я попытался изменить раздел реестра HKEY_CLASSES_ROOT\Applications\javaw.exe, чтобы добавить значение IsHostApp. Это меняет поведение, вообще отключая закрепление моего приложения; явно не то, что я хочу.

Result of specifying javaw.exe as a host application

Прочитав о том, как Windows интерпретирует экземпляры одного приложения явление, обсуждаемое в этом вопросе ), я заинтересовался внедрением идентификатора модели пользователя приложения (AppUserModelID) в мое Java-приложение.

Я считаю, что могу решить эту проблему, передав уникальный AppUserModelID в Windows. Для этого есть метод shell32, SetCurrentProcessExplicitAppUserModelID. Следуя предложению Грегори Пакоша, я реализовал его, чтобы мое приложение было признано отдельным экземпляром 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
}

Это, кажется, не имеет никакого эффекта, но функция возвращается без ошибки. Диагностика почему-то для меня загадка. Есть предложения?

Рабочая реализация

Последняя сработавшая реализация - это ответ на мой дополнительный вопрос о том, как передать AppID с использованием JNA.

Я вручил награду Григорию Пакошу за блестящий ответ за JNI, который поставил меня на правильный путь.

Для справки, я полагаю, использование этого метода открывает возможность использования любого из API, обсужденных в этой статье , в приложении Java.

Ответы [ 7 ]

20 голосов
/ 13 декабря 2009

У меня нет Windows 7, но есть кое-что, с чего можно начать:

На стороне Java:

package com.stackoverflow.homework;

public class MyApplication
{
  static native boolean setAppUserModelID();

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

А на родной стороне в исходном коде библиотеки `MyApplicationJNI.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;
}

Ваш вопрос явно задан для решения JNI. Однако, поскольку вашему приложению не нужен какой-либо другой нативный метод, jna - это еще одно решение, которое избавит вас от написания нативного кода только ради пересылки в windows api. Если вы решите пойти на jna, обратите внимание на тот факт, что SetCurrentProcessExplicitAppUserModelID() ожидает строку UTF-16.

Когда это работает в вашей песочнице, следующим шагом является добавление обнаружения операционной системы в ваше приложение, поскольку SetCurrentProcessExplicitAppUserModelID(), очевидно, доступно только в Windows 7:

  • вы можете сделать это со стороны Java, проверив, что System.getProperty("os.name"); возвращает "Windows 7".
  • если вы строите из небольшого фрагмента JNI, который я дал, вы можете улучшить его, динамически загрузив библиотеку shell32.dll, используя LoadLibrary, а затем вернув указатель функции SetCurrentProcessExplicitAppUserModelID, используя GetProcAddress. Если GetProcAddress возвращает NULL, это означает, что символ отсутствует в shell32, следовательно, это не Windows 7.

РЕДАКТИРОВАТЬ: JNA Solution .

Ссылки:

5 голосов
/ 02 декабря 2010

Существует библиотека Java, предоставляющая новые возможности Windows 7 для Java. Он называется J7Goodies Strix Code . Приложения, использующие его, могут быть правильно закреплены на панели задач Windows 7. Вы также можете создавать свои собственные списки переходов и т. Д.

4 голосов
/ 22 декабря 2009

Я реализовал доступ к методу SetCurrentProcessExplicitAppUserModelID, используя JNA, и он работает довольно хорошо, когда используется, как предполагает документация MSDN. Я никогда не использовал API JNA так, как вы используете в своем фрагменте кода. Моя реализация соответствует типичному использованию JNA .

Сначала определение интерфейса Shell32:

interface Shell32 extends StdCallLibrary {

    int SetCurrentProcessExplicitAppUserModelID( WString appID );

}

Затем с помощью JNA загрузите Shell32 и вызовите функцию:

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 );

Многие из API в последней упомянутой вами статье используют Windows COM, который довольно сложно использовать непосредственно с JNA. У меня был некоторый успех при создании пользовательской DLL для вызова этих API (например, с помощью SHGetPropertyStoreForWindow для установки другого идентификатора приложения для окна подмодуля), к которому я затем использую JNA для доступа во время выполнения.

4 голосов
/ 12 декабря 2009

Попробуйте использовать JSmooth . Я всегда использую это. В JSmooth есть опция под Skeleton на Windowed Wrapper под названием

Приложение Java в процессе exe

Смотрите на этом изображении.

JSmooth

Также можно передавать аргументы командной строки.
Я думаю, что это может быть решением для вас.

Мартейн

3 голосов
/ 11 декабря 2009

SetCurrentProcessExplicitAppUserModelID (или SetAppID ()) фактически сделает то, что вы пытаетесь сделать. Тем не менее, может быть проще изменить установщик так, чтобы он устанавливал свойство AppUserModel.ID на ярлык, цитируя упомянутый выше документ ИД модели приложения :

В свойстве System.AppUserModel.ID файла ярлыка приложения. Ярлык (как IShellLink, CLSID_ShellLink или файл .lnk) поддерживает свойства через IPropertyStore и другие механизмы установки свойств, используемые в командной консоли. Это позволяет панели задач определить правильный ярлык для закрепления и гарантирует, что окна, принадлежащие процессу, соответствующим образом связаны с этой кнопкой панели задач. Примечание. Свойство System.AppUserModel.ID следует применять к ярлыку при его создании. При использовании установщика Microsoft Windows (MSI) для установки приложения таблица MsiShortcutProperty позволяет применять AppUserModelID к ярлыку при его создании во время установки.

1 голос
/ 22 июля 2016

Последняя библиотека jna-platform теперь включает в себя привязки JNA для SetCurrentProcessExplicitAppUserModelID:

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

0 голосов
/ 19 января 2017

Я исправил мой без каких-либо настроек ID. В Launch4J есть опция, если вы ее используете, и вы говорите, что делаете, то ...

Вы можете изменить заголовок на JNI Gui, а затем обернуть его вокруг банки с JRE. Хорошо, что теперь он запускает .exe в процессе вместо запуска javaw.exe с вашим jar. Это, вероятно, делает это под капотом (не уверен). Также я заметил, что он потребляет примерно на 40-50% меньше ресурсов процессора, что даже лучше!

И закрепление работает нормально, и все функции этого окна включены.

Я надеюсь, что это кому-то поможет, поскольку я потратил почти 2 дня, пытаясь решить эту проблему с помощью моего недекорированного приложения javafx.

...