将 Java 应用程序固定到 Windows 7 任务栏
我在 Windows 7 下使用 Launch4j 作为我的 Java 应用程序的包装器,据我了解,它实质上派生了一个 javaw.exe
的实例,该实例反过来解释了 Java 代码.因此,当尝试将我的应用程序固定到任务栏时,Windows 改为固定 javaw.exe
.如果没有所需的命令行,我的应用程序将无法运行.
如您所见,Windows 也没有意识到 Java 是宿主应用程序:应用程序本身被描述为Java(TM) Platform SE binary".
我已尝试更改注册表项 HKEY_CLASSES_ROOTApplicationsjavaw.exe
以添加值 IsHostApp
.这通过完全禁用我的应用程序的固定来改变行为;显然不是我想要的.
在阅读了Windows 如何解释单个实例之后应用程序(以及此问题中讨论的一种现象),我开始有兴趣将应用程序用户模型 ID (AppUserModelID) 嵌入到我的 Java 应用程序中.
我相信我可以通过将唯一的 AppUserModelID
传递给 Windows 来解决此问题.有一个 shell32
方法,SetCurrentProcessExplicitAppUserModelID
.按照 Gregory Pakosz 的建议,我实现了它,试图让我的应用程序被识别为 javaw.exe
的单独实例:
NativeLibrary 库;尝试 {lib = NativeLibrary.getInstance(shell32");} 捕捉(错误 e){Logger.out.error("无法加载 Shell32 库.");返回;}Object[] args = { Vendor.MyJavaApplication";};String functionName = "SetCurrentProcessExplicitAppUserModelID";尝试 {函数 function = lib.getFunction(functionName);int ret = function.invokeInt(args);如果(ret!= 0){Logger.out.error(function.getName() + "返回错误码"+ ret + ".");}} 捕捉(UnsatisfiedLinkError e){Logger.out.error(functionName + "未在"中找到"+ lib.getFile().getName() + ".");//不支持函数}
这似乎没有效果,但函数返回没有错误.诊断为什么对我来说是个谜.有什么建议吗?
工作实施
最终的实现是我后续问题的答案 关于如何使用 JNA 传递 AppID
.
我已将赏金授予 Gregory Pakosz 对 JNI 的出色回答,这让我走上了正轨.
作为参考,我相信使用这种技术可以使用所讨论的任何 API 在本文中 在 Java 应用程序中.
解决方案我没有 Windows 7,但这里有一些东西可以帮助你入门:
在 Java 方面:
包com.stackoverflow.homework;公共类 MyApplication{静态本机布尔 setAppUserModelID();静止的{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);返回 hr == S_OK;}
您的问题明确要求 JNI 解决方案.但是,由于您的应用程序不需要任何其他本机方法,jna 是另一种可以节省您不必为了转发到 windows api 而编写本机代码.如果您决定使用 jna,请注意 SetCurrentProcessExplicitAppUserModelID()
需要一个 UTF-16 字符串.
当它在您的沙箱中工作时,下一步是在您的应用程序中添加操作系统检测,因为 SetCurrentProcessExplicitAppUserModelID()
显然仅在 Windows 7 中可用:
- 您可以通过检查
System.getProperty("os.name");
返回"Windows 7"
来从 Java 端执行此操作. - 如果您从我提供的 JNI 小片段构建,您可以通过使用 shell32.dll 库来增强它-us/library/ms684175(VS.85).aspx" rel="nofollow noreferrer">
LoadLibrary
然后使用 取回SetCurrentProcessExplicitAppUserModelID
函数指针href="http://msdn.microsoft.com/en-us/library/ms683212(VS.85).aspx" rel="nofollow noreferrer">GetProcAddress
.如果GetProcAddress
返回NULL
,则表示shell32
中不存在该符号,因此它不是 Windows 7.
JNA 解决方案.p>
参考资料:
- JNI 书籍 了解更多 JNI 示例李>
- Java Native Access (JNA)
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_ROOTApplicationsjavaw.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.
解决方案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 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:
- The JNI book for more JNI examples
- Java Native Access (JNA)
相关文章