将 Java 应用程序固定到 Windows 7 任务栏
Posted
技术标签:
【中文标题】将 Java 应用程序固定到 Windows 7 任务栏【英文标题】:Pinning a Java application to the Windows 7 taskbar 【发布时间】:2010-12-22 12:27:43 【问题描述】:我在 Windows 7 下使用 Launch4j 作为我的 Java 应用程序的包装器,据我了解,它本质上派生了一个 javaw.exe
的实例,它反过来解释了 Java 代码。结果,当尝试将我的应用程序固定到任务栏时,Windows 改为固定javaw.exe
。如果没有所需的命令行,我的应用程序将无法运行。
如您所见,Windows 也没有意识到 Java 是宿主应用程序:应用程序本身被描述为“Java(TM) Platform SE 二进制文件”。
我已尝试更改注册表项 HKEY_CLASSES_ROOT\Applications\javaw.exe
以添加值 IsHostApp
。这通过完全禁用我的应用程序的固定来改变行为;显然不是我想要的。
在阅读了how Windows interprets instances of a single application(和a phenomenon discussed in this question)之后,我对将应用程序用户模型 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
这似乎没有效果,但函数返回没有错误。诊断为什么对我来说是个谜。有什么建议吗?
工作实施
最终的实现是the answer to my follow-up question,涉及如何使用 JNA 传递AppID
。
我已将赏金授予 Gregory Pakosz 对 JNI 的出色回答,这让我走上了正轨。
作为参考,我相信使用这种技术可以在 Java 应用程序中使用 in this article 讨论的任何 API。
【问题讨论】:
对于 JSmooth。如果需要,请使用 gimp 将您的 .ico 转换为 .png。 尝试使用String.getBytes("UTF-16");
方法
有人在 launch4j 中打开了原生支持的功能请求:sourceforge.net/tracker/… 帮助在下一个 3.1 版本中获得此功能!
您问题中链接的图像似乎已过期。您是否保存了可以重新上传和编辑的副本?
不,恐怕我不知道。他们描述了 (1) 右键单击应用程序并固定它会创建一个指向 javaw.exe
的不可执行链接,而不是我的应用程序,以及 (2) 在看到注册表项右键单击完全不允许固定后。
【参考方案1】:
SetCurrentProcessExplicitAppUserModelID(或 SetAppID())实际上会做你想做的事。但是,修改安装程序以在快捷方式上设置 AppUserModel.ID 属性可能更容易 - 引用上述Application User Model ID 文档:
在应用程序快捷方式文件的System.AppUserModel.ID 属性中。快捷方式(作为 IShellLink、CLSID_ShellLink 或 .lnk 文件)通过 IPropertyStore 和整个 Shell 中使用的其他属性设置机制支持属性。这允许任务栏识别正确的固定快捷方式,并确保属于该进程的窗口与该任务栏按钮适当关联。 注意:创建快捷方式时,应将 System.AppUserModel.ID 属性应用于快捷方式。当使用 Microsoft Windows Installer (MSI) 安装应用程序时,MsiShortcutProperty 表允许 AppUserModelID 在安装过程中创建快捷方式时应用到该快捷方式。
【讨论】:
这是一个很好的建议,谢谢。我实际上已经研究过了(我使用 Inno Setup,它声称在快捷方式中包含System.AppUserModel.ID
。经过一些实验后,我实际上无法重现结果。此外,即使这有效,用户最终会感到困惑手动创建快捷方式。我更愿意将 AppUserModelID
封装到我的 Java 应用程序中。这可以通过 JNI 实现吗?【参考方案2】:
尝试使用JSmooth。我总是用这个。在 JSmooth 中,Skeleton
下有一个选项,由 Windowed Wrapper
调用
在 exe 进程中启动 java 应用程序
查看这张图片。
(来源:andrels.com)
也可以传递命令行参数。 我认为这可以为您提供解决方案。
马丁
【讨论】:
我以前听说过这个包装器。我给它一个摆动,但我不能让它做任何事情,因为它经常挂起。每当我选择“骨架”选项卡时,我都无法选择另一个选项卡。如果我最后访问该选项卡,然后按编译,它仍然挂在“加载图标...”上。我使用的是 Windows 7,但使用 Vista 兼容模式也会发生同样的情况。在以前版本的 Windows 下,它根本不启动以兼容。有什么想法吗? 经过一番摆弄,我得到了一个编译好的版本。似乎选择一个图标会导致我之前评论中描述的问题。如果我在 Skeleton 选项卡下选择 在 exe 进程中启动应用程序,则固定按预期工作。这并不能真正回答我嵌入AppID
的问题,但它规避了这个问题。谢谢!
我只能为图标加载非 ICO 图像。无论如何,在固定应用程序时,RelaunchIconResource(请参阅msdn.microsoft.com/en-us/library/dd391573(VS.85).aspx)显示为 Window 的默认应用程序图标。使用 Resource Hacker,我可以看到这个图标包含在图标组 A2 下的 EXE 中。替换它会反映在固定的应用程序中,但这几乎不是解决方案。
关于上述使用单个 PNG 而不是 ICO 的建议:恐怕这不是我的项目的选择,因为依赖于操作系统的分发确实需要不同的分辨率应用程序图标.我很欣赏这个答案,但它并没有解决手头的问题,而且切换到不同的包装器是不可能的。【参考方案3】:
我没有 Windows 7,但这里有一些可以帮助您入门的东西:
在 Java 方面:
package com.***.homework;
public class MyApplication
static native boolean setAppUserModelID();
static
System.loadLibrary("MyApplicationJNI");
setAppUserModelID();
而在本机端,在`MyApplicationJNI.dll 库的源代码中:
JNIEXPORT jboolean JNICALL Java_com_***_homework_MyApplication_setAppUserModelID(JNIEnv* env)
LPCWSTR id = L"com.***.homework.MyApplication";
HRESULT hr = SetCurrentProcessExplicitAppUserModelID(id);
return hr == S_OK;
您的问题明确要求 JNI 解决方案。但是,由于您的应用程序不需要任何其他本机方法,jna 是另一种解决方案,它将使您不必为了转发到 windows api 而编写本机代码。如果您决定使用 jna,请注意 SetCurrentProcessExplicitAppUserModelID()
期待 UTF-16 字符串这一事实。
当它在您的沙盒中运行时,下一步是在您的应用程序中添加操作系统检测,因为 SetCurrentProcessExplicitAppUserModelID()
显然仅在 Windows 7 中可用:
System.getProperty("os.name");
是否返回 "Windows 7"
从 Java 端执行此操作。
如果您从我提供的小 JNI sn-p 构建,您可以通过使用 LoadLibrary
动态加载 shell32.dll
库然后使用 GetProcAddress
取回 SetCurrentProcessExplicitAppUserModelID
函数指针来增强它。如果GetProcAddress
返回NULL
,则表示shell32
中不存在该符号,因此它不是Windows 7。
编辑:JNA Solution.
参考资料:
The JNI book 获取更多 JNI 示例 Java Native Access (JNA)【讨论】:
这看起来很不错!我选择 JNI 并没有什么特别的原因。我有一些使用 JNI 的特定于 Mac 的方法,但我一定会考虑您的建议。 好的。不知道你是否熟悉JNI 回想起来,我只能在Mac上编译专门为Mac编写的JNI方法。我一直在研究 JNA,它看起来更直观。我还没有真正尝试过,但由于赏金即将到期,我将继续奖励积分。 我已经用我的 JNA 实现编辑了我的问题。这似乎没有效果;这与UTF-16有关吗?如果是这样,我如何在这种编码中传递一个字符串? 我之前关于字符串编码的问题得到了in this follow-up question的解答。【参考方案4】:我已经使用 JNA 实现了对 SetCurrentProcessExplicitAppUserModelID 方法的访问,并且按照 MSDN 文档的建议使用时效果很好。我从来没有像您在代码 sn-p 中那样使用 JNA api。我的实现遵循typical JNA usage。
首先是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 在运行时访问它。
【讨论】:
这非常符合 Gregory Pakosz 对我的后续问题 (***.com/questions/1907735/…) 的回答。我对你所说的“自定义 DLL”很好奇。我猜这是用 C# 制作的东西?你能提供更多信息吗? 不,我选择用 C 语言制作一个自定义 DLL,这很容易用 JNA 调用。到目前为止,我只在 C DLL 中实现了一个 SetWindowAppId 函数,该函数调用 SHGetPropertyStoreForWindow,然后能够操作生成的 COM 接口来设置窗口的 appID(正如各种 Microsoft 示例所示。)我还计划实现一些函数操纵我的应用程序的跳转列表,但我还没有到达那里。 JNA 非常方便地在其 Native 类中提供了用于检索 Java Window 的句柄/指针的 API,这使得这种方法成为可能。 @The_Fire:你能提供 SHGetPropertyStoreForWindow 代码吗? @The_Fire:好的,我自己实现了它——没那么难。 :)【参考方案5】:有一个 Java 库为 Java 提供了新的 Windows 7 功能。它被Strix Code 称为J7Goodies。使用它的应用程序可以正确固定到 Windows 7 任务栏。您还可以创建自己的跳转列表等。
【讨论】:
我不太喜欢使用专有库。尽管如此,它似乎包含了您可能希望在 Windows 7 中从 Java 集成的所有内容。看起来不错!【参考方案6】:最新的jna-platform
库现在包括SetCurrentProcessExplicitAppUserModelID
的JNA 绑定:
https://github.com/java-native-access/jna/pull/680
【讨论】:
【参考方案7】:我在没有任何 ID 设置的情况下修复了我的问题。 Launch4J 中有一个选项,如果你正在使用它并且你说你会这样做......
您可以将标头更改为 JNI Gui,然后使用 JRE 将其包裹在 jar 周围。 好消息是它现在在进程中运行 .exe,而不是在你的 jar 中运行 javaw.exe。它可能在引擎盖下完成(不确定)。 此外,我还注意到它减少了大约 40-50% 的 CPU 资源,这更好!
固定工作正常,所有窗口功能都已启用。
我希望它对某人有所帮助,因为我花了将近 2 天的时间试图用我未修饰的 javafx 应用程序解决这个问题。
【讨论】:
如果您需要 64 位版本的 javaw.exe,则无法使用此选项。有一个名为 Launch5J 的项目可能适用于 64 位版本,但它不适用于我。以上是关于将 Java 应用程序固定到 Windows 7 任务栏的主要内容,如果未能解决你的问题,请参考以下文章