将 Java 应用程序固定到 Windows 7 任务栏
我使用 Launch4j 作为 Windows 7 下 Java 应用程序的包装器,据我了解,它本质上派生了一个 javaw.exe
实例,而该实例又解释 Java 代码。因此,当尝试将我的应用程序固定到任务栏时,Windows 会固定 javaw.exe
。如果没有所需的命令行,我的应用程序将无法运行。
正如您所看到的,Windows 也没有意识到 Java 是主机应用程序:应用程序本身被描述为“Java(TM) Platform SE 二进制文件”。
我尝试更改注册表项 HKEY_CLASSES_ROOT\Applications\javaw.exe
以添加值 IsHostApp
。这通过完全禁用我的应用程序的固定来改变行为;显然不是我想要的。
阅读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.
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.
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我没有 Windows 7,但这里有一些可能会让您开始:
在 Java 方面:
在本机方面,在`MyApplicationJNI.dll 库的源代码中:
您的问题明确要求 JNI 解决方案。但是,由于您的应用程序不需要任何其他本机方法,因此 jna 是另一种解决方案,可以节省您不必为了转发到 Windows API 而编写本机代码。如果您决定使用 jna,请注意
SetCurrentProcessExplicitAppUserModelID()
需要 UTF-16 字符串。当它在您的沙箱中工作时,下一步是在您的应用程序中添加操作系统检测,因为
SetCurrentProcessExplicitAppUserModelID()
显然仅在 Windows 7 中可用:SetCurrentProcessExplicitAppUserModelID()
从 Java 端执行此操作code>System.getProperty("os.name"); 返回“Windows 7”
。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:
And on the native side, in the source code of the `MyApplicationJNI.dll library:
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:System.getProperty("os.name");
returns"Windows 7"
.shell32.dll
library usingLoadLibrary
then getting back theSetCurrentProcessExplicitAppUserModelID
function pointer usingGetProcAddress
. IfGetProcAddress
returnsNULL
, it means the symbol is not present inshell32
hence it's not Windows 7.EDIT: JNA Solution.
References:
有一个 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.
我已经使用 JNA 实现了对 SetCurrentProcessExplicitAppUserModelID 方法的访问,并且按照 MSDN 文档的建议使用时效果非常好。我从未像您在代码片段中那样使用 JNA api。我的实现遵循典型的 JNA 用法。
首先是Shell32接口定义:
然后使用JNA加载Shell32并调用函数:
上一篇文章中提到的许多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:
Then using JNA to load Shell32 and call the function:
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.
尝试使用JSmooth。我一直用这个。在 JSmooth 中,
Skeleton
下有一个由Windowed Wrapper
调用的选项请参见此图像。
(来源:andrels.com< /a>)
也可以传递命令行参数。
我认为这可以为您提供解决方案。
马丁
Try to use JSmooth. I use always this one. In JSmooth is there an option under
Skeleton
byWindowed Wrapper
calledSee on this image.
(source: andrels.com)
Also command line arguments can be passed.
I think this can be a solution for you.
Martijn
SetCurrentProcessExplicitAppUserModelID (或 SetAppID()) 实际上会做你想做的事情。但是,修改安装程序以在快捷方式上设置 AppUserModel.ID 属性可能会更容易 - 引用 应用程序用户模型 ID 文档:
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:
最新的
jna-platform
库现在包含SetCurrentProcessExplicitAppUserModelID
的 JNA 绑定:https://github.com/java-native-access/jna/pull/680
The latest
jna-platform
library now includes JNA bindings forSetCurrentProcessExplicitAppUserModelID
:https://github.com/java-native-access/jna/pull/680
我修复了我的,没有任何 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.