Использование JNA для получения / установки идентификатора приложения - PullRequest
21 голосов
/ 15 декабря 2009

В ответ на мой предыдущий вопрос о панели задач Windows 7 , я хотел бы выяснить, почему Windows не признает, что мое приложение не зависит от javaw.exe. В настоящее время у меня есть следующий код JNA для получения AppUserModelID:

public class AppIdTest {

    public static void main(String[] args) {
        NativeLibrary lib;
        try {
            lib = NativeLibrary.getInstance("shell32");
        } catch (Error e) {
            System.err.println("Could not load Shell32 library.");
            return;
        }
        Object[] functionArgs = new Object[1];
        String functionName = null;
        Function function;
        try {
            functionArgs[0] = new String("Vendor.MyJavaApplication")
                    .getBytes("UTF-16");
            functionName = "GetCurrentProcessExplicitAppUserModelID";
            function = lib.getFunction(functionName);
            // Output the current AppId
            System.out.println("1: " + function.getString(0));
            functionName = "SetCurrentProcessExplicitAppUserModelID";
            function = lib.getFunction(functionName);
            // Set the new AppId
            int ret = function.invokeInt(functionArgs);
            if (ret != 0) {
                Logger.out.error(function.getName() + " returned error code "
                        + ret + ".");
            }
            functionName = "GetCurrentProcessExplicitAppUserModelID";
            function = lib.getFunction(functionName);
            // Output the current AppId
            System.out.println("2: " + function.getString(0));
            // Output the current AppID, converted from UTF-16
            System.out.println("3: "
                    + new String(function.getByteArray(0, 255), "UTF-16"));
        } catch (UnsupportedEncodingException e) {
            System.err.println("System does not support UTF-16 encoding.");
        } catch (UnsatisfiedLinkError e) {
            System.err.println(functionName + " was not found in "
                    + lib.getFile().getName() + ".");
        }
    }
}

Выходные данные приложения кажутся бессмысленными:

1: ‹ÿU‹ìƒìL¡¬Ÿv3ʼnEüSV‹uƒ&
2: ‹ÿU‹ìƒìL¡¬Ÿv3ʼnEüSV‹uƒ&
3: ????????????????P???????????

Зная, что выводом может быть UTF-16, в (3) я попытался преобразовать массив байтов из UTF-16. Честно говоря, я не знаю, правильный ли мой подход, так как (а) я не знаю размер a PWSTR и (б) я не знаю, является ли GetCurrentProcessExplicitAppUserModelID действительно возвращает байтовый массив или строку.

Мне известно, что JSmooth будет запускать процесс графического интерфейса в оболочке, которая имитирует этот эффект. Launch4j утверждает, что делает то же самое, но, похоже, не работает. Я ищу AppUserModelID set независимо от оболочки Java .

Что здесь не так?

Ответы [ 3 ]

19 голосов
/ 18 декабря 2009

Я не видел вашего вопроса раньше, иначе я бы попробовал даже без награды.

Вот то, что я придумал. Обратите внимание, как указано в самом коде, я не реализовал надлежащую очистку памяти с помощью функции CoTaskMemFree (из Ole32.dll). Поэтому я предлагаю вам взять только реализацию для SetCurrentProcessExplicitAppUserModelID()

package com.stackoverflow.AppIdTest;

import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.ptr.PointerByReference;

public class AppIdTest
{

  public static void main(String[] args) throws Exception
  {
    setCurrentProcessExplicitAppUserModelID(AppIdTest.class.getName());

    System.out.println(getCurrentProcessExplicitAppUserModelID());
  }

  // DO NOT DO THIS, IT'S JUST FOR TESTING PURPOSE AS I'M NOT FREEING THE MEMORY
  // AS REQUESTED BY THE DOCUMENTATION:
  //
  // http://msdn.microsoft.com/en-us/library/dd378419%28VS.85%29.aspx
  //
  // "The caller is responsible for freeing this string with CoTaskMemFree when
  // it is no longer needed"
  public static String getCurrentProcessExplicitAppUserModelID()
  {
    final PointerByReference r = new PointerByReference();

    if (GetCurrentProcessExplicitAppUserModelID(r).longValue() == 0)
    {
      final Pointer p = r.getValue();


      return p.getString(0, true); // here we leak native memory by lazyness
    }      
    return "N/A";
  }

  public static void setCurrentProcessExplicitAppUserModelID(final String appID)
  {
    if (SetCurrentProcessExplicitAppUserModelID(new WString(appID)).longValue() != 0)
      throw new RuntimeException("unable to set current process explicit AppUserModelID to: " + appID);
  }

  private static native NativeLong GetCurrentProcessExplicitAppUserModelID(PointerByReference appID);
  private static native NativeLong SetCurrentProcessExplicitAppUserModelID(WString appID);


  static
  {
    Native.register("shell32");
  }
}

Это у вас работает?

По крайней мере, здесь он правильно печатает обратно:

com.stackoverflow.AppIdTest.AppIdTest

3 голосов
/ 04 августа 2016

Вот более простой пример того, как позвонить SetCurrentProcessExplicitAppUserModelID через JNA:

import com.sun.jna.*;
import com.sun.jna.win32.*;

interface Shell32 extends StdCallLibrary {

    Shell32 INSTANCE = (Shell32) Native.loadLibrary("shell32", Shell32.class, W32APIOptions.DEFAULT_OPTIONS);

    NativeLong SetCurrentProcessExplicitAppUserModelID(WString appID);

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

Если вам просто нужно установить AppUserModelId, то приведенного выше кода JNA достаточно. Однако, если вы хотите воспользоваться преимуществами новых функций Windows 7 в своем приложении Java, посмотрите J7Goodies библиотеку Java, предоставляющую расширения панели задач Windows 7.


РЕДАКТИРОВАТЬ : больше информации от J7Goodies Руководство программиста

4,2. Настройка AppUserModelID

Чтобы использовать любую из функций Windows 7, приложение должно явно установить идентификатор процесса - Идентификатор модели пользователя приложения (AppUserModelID). Может иметь не более 128 символов и не может содержать пробелы. Каждый раздел должен быть в верблюжьей шкуре, например:

 CompanyName.ProductName.SubProduct.VersionInformation

Этот идентификатор должен быть установлен до отображения любого GUI (окна). Вы устанавливаете это по телефону:

// Remember to set AppUserModelID before creating any UI
AppUserModelId.setCurrentProcessId("StrixCode.J7Goodies.Appname");

4,3. Настройка свойств окна

Приложение Java не может быть закреплено на панели задач Windows 7, если не определены его свойства окна. свойства состоят из четырех полей:

  • AppUserModelID - то же, что передано в AppUserModelId.setCurrentProcessId(String)
  • RelaunchDisplayName - имя приложения
  • RelaunchCommand - полная команда, используемая для запуска приложения. В случае Java-программы это будет: <path to javaw.exe> -jar <path to application jar>
  • RelaunchIcon - путь к значку приложения

Важно : RelaunchCommand и RelaunchDisplayName всегда должны быть установлены все вместе. Чтобы установить эти свойства, используйте простой класс WindowProperties.

WindowProperties props = new WindowProperties(myFrame);
props.setRelaunchCommand("<full path to javaw.exe –arguments>");
props.setRelaunchDisplayName("My Java Application");
props.setRelaunchIcon("<full path to an .ico or .exe file>");
props.setAppUserModelID("StrixCode.J7Goodies.Appname");
props.save();
...