使用 JNA 获取/设置应用程序标识符

发布于 2024-08-15 02:47:21 字数 2754 浏览 2 评论 0原文

跟进我之前有关 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 的大小,(b)我不知道 GetCurrentProcessExplicitAppUserModelID 确实返回一个字节数组或字符串。

我知道 JSmooth 会在模拟这种效果的包装器中运行 GUI 进程。 Launch4j 声称可以做同样的事情,但似乎不起作用。我希望设置 AppUserModelID,无论 Java 包装器如何。

这里出了什么问题?

Following up on my previous question concerning the Windows 7 taskbar, I would like to diagnose why Windows isn't acknowledging that my application is independent of javaw.exe. I presently have the following JNA code to obtain the 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() + ".");
        }
    }
}

The output of the application is seemingly gibberish:

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

Being aware of the fact that the output may be UTF-16, in (3) I attempted to convert a byte array from UTF-16. In all honesty I don't know if my approach here is right as (a) I don't know the size of a PWSTR and (b) I don't know if GetCurrentProcessExplicitAppUserModelID is indeed returning a byte array or string.

I'm aware that JSmooth will run the GUI process in a wrapper which simulates this effect. Launch4j claims to do the same, but doesn't appear to work. I am looking to have the AppUserModelID set regardless of the Java wrapper.

What is going wrong here?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

沧笙踏歌 2024-08-22 02:47:22

以下是有关如何通过 JNA 调用 SetCurrentProcessExplicitAppUserModelID 的更简单示例:

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

}

Here's a more simple example on how to call SetCurrentProcessExplicitAppUserModelID via 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);

}
我不是你的备胎 2024-08-22 02:47:21

我之前没有看到你的问题,否则即使没有赏金我也会尝试一下。

这是我想出的。 请注意,正如代码本身所述,我没有使用 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

I didn't see your question before otherwise I would have given a try even without a bounty.

Here is what I came up with. Please note, as stated in the code itself, I didn't implement proper memory clean up with the CoTaskMemFree function (from Ole32.dll). So I suggest you take only the implementation for 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");
  }
}

Does it work for you?

At least here it correctly prints back:

com.stackoverflow.AppIdTest.AppIdTest

海之角 2024-08-22 02:47:21

如果您只需要设置 AppUserModelId 那么上面的 JNA 代码就足够了。但是,如果您想在 Java 应用程序中利用 Windows 7 的新功能,请查看 J7Goodies 的功能,一个提供 Windows 7 任务栏扩展的 Java 库。


编辑:来自 J7Goodies 程序员指南的更多信息

4.2。设置AppUserModelID

为了使用任何 Windows 7 功能,应用程序必须明确
设置其进程标识符 –
应用程序用户模型 ID (AppUserModelID)。它不能超过
128 个字符,不能包含空格。每个部分应该是
驼峰式,例如:

 公司名称.产品名称.子产品.版本信息

该标识符
必须在显示任何 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 程序,则为:-jar <应用程序 jar 的路径>
  • RelaunchIcon – 应用程序图标的路径

重要:必须始终设置 RelaunchCommandRelaunchDisplayName
一起。要设置这些属性,请使用简单的 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();

If you just need to set the AppUserModelId then the above JNA code is enough. However if you want to take advantage of the new Windows 7 features in you Java application then check out J7Goodies a Java library providing Windows 7 taskbar extensions.


EDIT: more info from J7Goodies Programmer's Guide

4.2. Setting AppUserModelID

In order to use any of the Windows 7 features an application must explicitly
set its process identifier –
Application User Model ID (AppUserModelID). It can have no more than
128 characters and cannot contain spaces. Each section should be
camel-cased, for example:

 CompanyName.ProductName.SubProduct.VersionInformation

This identifier
must be set before any GUI (window) is shown. You set it by calling:

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

4.3. Setting Window Properties

A Java application cannot be pinned to the Windows 7 taskbar unless its window properties are defined. The
properties consist of four fields:

  • AppUserModelID – the same as passed to AppUserModelId.setCurrentProcessId(String)
  • RelaunchDisplayName – application’s name
  • RelaunchCommand – the full command used to launch the application. In case of a Java program it will be: <path to javaw.exe> -jar <path to application jar>
  • RelaunchIcon – path to application’s icon

Important: RelaunchCommand and RelaunchDisplayName must always be set
together. To set these properties use the straightforward WindowProperties class.

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();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文