将 Java 应用程序固定到 Windows 7 任务栏

发布于 2024-08-13 09:12:16 字数 2309 浏览 5 评论 0原文

我使用 Launch4j 作为 Windows 7 下 Java 应用程序的包装器,据我了解,它本质上派生了一个 javaw.exe 实例,而该实例又解释 Java 代码。因此,当尝试将我的应用程序固定到任务栏时,Windows 会固定 javaw.exe。如果没有所需的命令行,我的应用程序将无法运行。

将 Launch4j 应用程序固定到任务栏的结果

正如您所看到的,Windows 也没有意识到 Java 是主机应用程序:应用程序本身被描述为“Java(TM) Platform SE 二进制文件”。

我尝试更改注册表项 HKEY_CLASSES_ROOT\Applications\javaw.exe 以添加值 IsHostApp。这通过完全禁用我的应用程序的固定来改变行为;显然不是我想要的。

将 javaw.exe 指定为主机应用程序的结果

阅读Windows 如何解释单个应用程序的实例(以及 此问题中讨论的现象),我开始对将应用程序用户模型 ID (AppUserModelID) 嵌入到我的 Java 应用程序。

我相信我可以通过将唯一的 AppUserModelID 传递给 Windows 来解决此问题。有一个 shell32 方法,SetCurrentProcessExplicitAppUserModelID。按照 Gregory Pakosz 的建议,我实现了它,试图将我的应用程序识别为 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
}

这似乎没有效果,但函数返回时没有错误。诊断原因对我来说是个谜。有什么建议吗?

有效的实现

最终有效的实现是我的后续问题的答案 关于如何使用 JNA 传递 AppID

我将赏金授予了 Gregory Pakosz,他对 JNI 的出色回答让我走上了正轨。

作为参考,我相信使用此技术可以使用 中讨论的任何 API本文在 Java 应用程序中。

I use Launch4j as a wrapper for my Java application under Windows 7, which, to my understanding, in essence forks an instance of javaw.exe that in turn interprets the Java code. As a result, when attempting to pin my application to the task bar, Windows instead pins javaw.exe. Without the required command line, my application will then not run.

Result of pinning a Launch4j application to the taskbar

As you can see, Windows also does not realize that Java is the host application: the application itself is described as "Java(TM) Platform SE binary".

I have tried altering the registry key HKEY_CLASSES_ROOT\Applications\javaw.exe to add the value IsHostApp. This alters the behavior by disabling pinning of my application altogether; clearly not what I want.

Result of specifying javaw.exe as a host application

After reading about how Windows interprets instances of a single application (and a phenomenon discussed in this question), I became interested in embedding a Application User Model ID (AppUserModelID) into my Java application.

I believe that I can resolve this by passing a unique AppUserModelID to Windows. There is a shell32 method for this, SetCurrentProcessExplicitAppUserModelID. Following Gregory Pakosz suggestion, I implemented it in an attempt to have my application recognized as a separate instance of 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
}

This appears to have no effect, but the function returns without error. Diagnosing why is something of a mystery to me. Any suggestions?

Working implementation

The final implementation that worked is the answer to my follow-up question concerning how to pass the AppID using JNA.

I had awarded the bounty to Gregory Pakosz' brilliant answer for JNI that set me on the right track.

For reference, I believe using this technique opens the possibility of using any of the APIs discussed in this article in a Java application.

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

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

发布评论

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

评论(7

御守 2024-08-20 09:12:16

我没有 Windows 7,但这里有一些可能会让您开始:

在 Java 方面:

package com.stackoverflow.homework;

public class MyApplication
{
  static native boolean setAppUserModelID();

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

在本机方面,在`MyApplicationJNI.dll 库的源代码中:

JNIEXPORT jboolean JNICALL Java_com_stackoverflow_homework_MyApplication_setAppUserModelID(JNIEnv* env)
{
  LPCWSTR id = L"com.stackoverflow.homework.MyApplication";
  HRESULT hr = SetCurrentProcessExplicitAppUserModelID(id);

  return hr == S_OK;
}

您的问题明确要求 JNI 解决方案。但是,由于您的应用程序不需要任何其他本机方法,因此 jna 是另一种解决方案,可以节省您不必为了转发到 Windows API 而编写本机代码。如果您决定使用 jna,请注意 SetCurrentProcessExplicitAppUserModelID() 需要 UTF-16 字符串。

当它在您的沙箱中工作时,下一步是在您的应用程序中添加操作系统检测,因为 SetCurrentProcessExplicitAppUserModelID() 显然仅在 Windows 7 中可用:

  • 您可以通过检查 SetCurrentProcessExplicitAppUserModelID() 从 Java 端执行此操作code>System.getProperty("os.name"); 返回“Windows 7”
  • 如果您从我提供的小 JNI 片段构建,则可以通过使用 LoadLibrary 然后使用 GetProcAddress。如果 GetProcAddress 返回 NULL,则意味着该符号不存在于 shell32 中,因此它不是 Windows 7。

编辑:JNA 解决方案

参考资料:

I don't have Windows 7 but here is something that might get you started:

On the Java side:

package com.stackoverflow.homework;

public class MyApplication
{
  static native boolean setAppUserModelID();

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

And on the native side, in the source code of the `MyApplicationJNI.dll library:

JNIEXPORT jboolean JNICALL Java_com_stackoverflow_homework_MyApplication_setAppUserModelID(JNIEnv* env)
{
  LPCWSTR id = L"com.stackoverflow.homework.MyApplication";
  HRESULT hr = SetCurrentProcessExplicitAppUserModelID(id);

  return hr == S_OK;
}

Your question explicitly asked for a JNI solution. However, since your application doesn't need any other native method, jna is another solution which will save you from writing native code just for the sake of forwarding to the windows api. If you decide to go jna, pay attention to the fact that SetCurrentProcessExplicitAppUserModelID() is expecting a UTF-16 string.

When it works in your sandbox, the next step is to add operating system detection in your application as SetCurrentProcessExplicitAppUserModelID() is obviously only available in Windows 7:

  • you may do that from the Java side by checking that System.getProperty("os.name"); returns "Windows 7".
  • if you build from the little JNI snippet I gave, you can enhance it by dynamically loading the shell32.dll library using LoadLibrary then getting back the SetCurrentProcessExplicitAppUserModelID function pointer using GetProcAddress. If GetProcAddress returns NULL, it means the symbol is not present in shell32 hence it's not Windows 7.

EDIT: JNA Solution.

References:

も星光 2024-08-20 09:12:16

有一个 Java 库为 Java 提供了新的 Windows 7 功能。它被称为 J7Goodies,作者为 Strix 代码。使用它的应用程序可以正确固定到 Windows 7 任务栏。您还可以创建自己的跳转列表等。

There is a Java library providing the new Windows 7 features for Java. It's called J7Goodies by Strix Code. Applications using it can be properly pinned to the Windows 7 taskbar. You can also create your own jump lists, etc.

聽兲甴掵 2024-08-20 09:12:16

我已经使用 JNA 实现了对 SetCurrentProcessExplicitAppUserModelID 方法的访问,并且按照 MSDN 文档的建议使用时效果非常好。我从未像您在代码片段中那样使用 JNA api。我的实现遵循典型的 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 为子模块窗口设置不同的应用程序 ID),然后我使用 JNA 在运行时访问它。

I have implemented access to the SetCurrentProcessExplicitAppUserModelID method using JNA and it works quite well when used as the MSDN documentation suggests. I've never used the JNA api in the way you have in your code snippet. My implementation follows the typical JNA usage instead.

First the Shell32 interface definition:

interface Shell32 extends StdCallLibrary {

    int SetCurrentProcessExplicitAppUserModelID( WString appID );

}

Then using JNA to load Shell32 and call the function:

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

Many of the API's in the last article you mentioned make use of Windows COM which is quite difficult to use directly with JNA. I have had some success creating a custom DLL to call these API's (eg. using the SHGetPropertyStoreForWindow to set a different app ID for a submodule window) which I then use JNA to access at runtime.

下雨或天晴 2024-08-20 09:12:16

尝试使用JSmooth。我一直用这个。在 JSmooth 中,Skeleton 下有一个由 Windowed Wrapper 调用的选项

在exe进程中启动java应用程序

请参见此图像。

JSmooth
(来源:andrels.com< /a>)

也可以传递命令行参数。
我认为这可以为您提供解决方案。

马丁

Try to use JSmooth. I use always this one. In JSmooth is there an option under Skeleton by Windowed Wrapper called

Lauch java app in exe process

See on this image.

JSmooth
(source: andrels.com)

Also command line arguments can be passed.
I think this can be a solution for you.

Martijn

昵称有卵用 2024-08-20 09:12:16

SetCurrentProcessExplicitAppUserModelID (或 SetAppID()) 实际上会做你想做的事情。但是,修改安装程序以在快捷方式上设置 AppUserModel.ID 属性可能会更容易 - 引用 应用程序用户模型 ID 文档:

System.AppUserModel.ID 应用程序快捷方式文件的属性。快捷方式(作为 IShellLink、CLSID_ShellLink 或 .lnk 文件)通过 IPropertyStore 和整个 Shell 中使用的其他属性设置机制支持属性。这允许任务栏识别正确的固定快捷方式,并确保属于该进程的窗口与该任务栏按钮适当关联。
注意:创建快捷方式时,应将 System.AppUserModel.ID 属性应用于该快捷方式。使用 Microsoft Windows Installer (MSI) 安装应用程序时,MsiShortcutProperty 表允许在安装过程中创建快捷方式时将 AppUserModelID 应用于快捷方式。

SetCurrentProcessExplicitAppUserModelID (or SetAppID()) would in fact do what you're trying to do. However, it might be easier to modify your installer to set the AppUserModel.ID property on your shortcut - quoting from the Application User Model ID document mentioned above:

In the System.AppUserModel.ID property of the application's shortcut file. A shortcut (as an IShellLink, CLSID_ShellLink, or a .lnk file) supports properties through IPropertyStore and other property-setting mechanisms used throughout the Shell. This allows the taskbar to identify the proper shortcut to pin and ensures that windows belonging to the process are appropriately associated with that taskbar button.
Note: The System.AppUserModel.ID property should be applied to a shortcut when that shortcut is created. When using the Microsoft Windows Installer (MSI) to install the application, the MsiShortcutProperty table allows the AppUserModelID to be applied to the shortcut when it is created during installation.

私野 2024-08-20 09:12:16

最新的 jna-platform 库现在包含 SetCurrentProcessExplicitAppUserModelID 的 JNA 绑定:

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

The latest jna-platform library now includes JNA bindings for SetCurrentProcessExplicitAppUserModelID:

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

花心好男孩 2024-08-20 09:12:16

我修复了我的,没有任何 ID 设置。
Launch4J 中有一个选项,如果您正在使用它并且您说您这样做的话...

您可以将标头更改为 JNI Gui,然后使用 JRE 将其包装在 jar 中。
好处是它现在在进程中运行 .exe,而不是使用 jar 运行 javaw.exe。它可能是在幕后做的(不确定)。
我还注意到,它占用的 CPU 资源减少了大约 40-50%,这甚至更好!

并且固定工作正常并且所有窗口功能均已启用。

我希望它对某人有所帮助,因为我花了近 2 天的时间试图用我的未修饰的 javafx 应用程序解决这个问题。

I fixed mine without any ID settings.
There is an option in Launch4J if you are using it and you say you do then...

You can change the header to JNI Gui and then wrap it around the jar with the JRE.
The good thing is that it runs .exe in the process now instead on running javaw.exe with your jar. It probably does it under the hood (not sure).
Also I have noticed also that it takes around 40-50% less CPU resource which is even better!

And the pinning works fine and all that window features are enabled.

I hope it helps to someone as I spent nearly 2 days trying to solve that issue with my undecorated javafx app.

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