Использование JNA для получения / установки идентификатора приложения
В продолжение моего предыдущего вопроса, касающегося панели задач 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. Честно говоря, я не знаю, правильный ли мой подход, так как (а) я не знаю размер PWSTR
и (б) я не знаю, если GetCurrentProcessExplicitAppUserModelID
действительно возвращает байтовый массив или строку.
Я знаю, что JSmooth будет запускать процесс GUI в оболочке, которая имитирует этот эффект. Launch4j утверждает, что делает то же самое, но, похоже, не работает. Я ищу, чтобы иметь AppUserModelID
устанавливается независимо от оболочки Java.
Что здесь не так?
3 ответа
Я не видел ваш вопрос раньше, иначе я бы попробовал даже без награды.
Вот что я придумал. Обратите внимание, что, как указано в самом коде, я не реализовал надлежащую очистку памяти с помощью CoTaskMemFree
функция (от Ole32.dll
). Поэтому я предлагаю вам взять только реализацию для SetCurrentProcessExplicitAppUserModelID()
package com.stackru.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.stackru.AppIdTest.AppIdTest
Вот более простой пример того, как позвонить 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);
}
Если вам просто нужно установить 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
всегда должны быть установлены вместе. Чтобы установить эти свойства, используйте простой класс Window Properties.
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();