Закрепление приложения Java на панели задач Windows 7

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

Результат закрепления приложения Launch4j на панели задач

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

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

Результат определения javaw.exe

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

Я считаю, что я могу решить эту проблему, передав уникальный AppUserModelID в Windows. E сть 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 ответов

Решение

У меня нет Windows 7, но вот что может помочь вам начать:

На стороне Java:

package com.stackru.homework;

public class MyApplication
{
  static native boolean setAppUserModelID();

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

И на родной стороне, в исходном коде библиотеки `MyApplicationJNI.dll:

JNIEXPORT jboolean JNICALL Java_com_stackru_homework_MyApplication_setAppUserModelID(JNIEnv* env)
{
  LPCWSTR id = L"com.stackru.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.

Рекомендации:

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

Я реализовал доступ к методу 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 для доступа во время выполнения.

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

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

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

JSmooth

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

Мартейн

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

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

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

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

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

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

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

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

Другие вопросы по тегам