【问题标题】:Using JNA to get/set application identifier使用 JNA 获取/设置应用程序标识符
【发布时间】:2010-12-26 20:04:16
【问题描述】:

跟进my previous question concerning the Windows 7 taskbar,我想诊断一下为什么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 包装器如何

这里出了什么问题?

【问题讨论】:

  • 如果将格式设为 UTF-8,输出会是什么?
  • ??U???L???v3?E?SV?u?&...(实际上更长,因为字节数组的长度为 255)。如果我返回字节值,它会显示负值:-117 -1 85 -117 -20 -125 -20 76 -95 -84 -97 12 118 51 -59 -119 69 -4 83 86 -117 117 8 -125 38 ...。它给我的印象是该函数返回的不是字符串。

标签: java windows-7 jna utf-16


【解决方案1】:

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

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

【讨论】:

  • 太棒了!它就像一个魅力!我喜欢它如何在没有任何进一步修改的情况下为固定应用程序显示我的应用程序名称和图标。非常感谢!
  • 我很高兴能帮上忙。在这之前我从未使用过 JNA,这就是我对你的问题感到好奇的原因。但是,直到今天我才能使用 Windows 7 计算机
  • 再一次,不要在实际应用程序中读回该值,否则您需要增强实现以在将 appID 转换回 Java 字符串后正确清理内存
  • @GregoryPakosz 我已经成功复制了 this 示例代码的结果,但是我不确定我应该看到什么。我创建了一个简单的应用程序,它执行示例代码并显示JFrame。然后在任务栏上显示一个图标,但右键单击它不会让我选择将图标“固定”到任务栏(我只有一个“关闭 blabla”选项)。我在这里想念什么?或者 this 根本不尝试回答 OP 的 related question
  • @KohányiRóbert @kayahr 您似乎遇到的行为是a mode called "IsHostApp"。也许javaw 在您的系统上注册为 IsHostApp,或者可能是 Swing(因为您似乎正在使用它)以这种方式设置自己以防止固定问题。您可能会在this related question 中找到更多信息。
【解决方案2】:

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


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

4.2。设置 AppUserModelID

为了使用任何 Windows 7 功能,应用程序必须明确 设置它的进程标识符—— 应用程序用户模型 ID (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 – 应用程序图标的路径

重要提示:必须始终设置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();

【讨论】:

    【解决方案3】:

    这里有一个更简单的例子,说明如何通过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);
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-19
      • 2013-10-26
      • 2011-04-13
      • 1970-01-01
      • 2011-11-14
      • 1970-01-01
      相关资源
      最近更新 更多