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

时间:2009-12-02 17:44:01

标签: java windows-7 java-native-interface launch4j taskbar

我使用Launch4j作为Windows 7下我的Java应用程序的包装器,根据我的理解,本质上要求javaw.exe的实例反过来解释Java代码。因此,在尝试将我的应用程序固定到任务栏时,Windows会引导javaw.exe。如果没有所需的命令行,我的应用程序将无法运行。

Result of pinning a Launch4j application to the taskbar

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

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

Result of specifying javaw.exe as a host application

在阅读了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
}

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

工作实施

有关如何使用JNA传递AppID的最终实现the answer to my follow-up question

我已经将Gregory Pakosz给予JNI的精彩答案奖励给我,使我走上正轨。

作为参考,我相信使用这种技术可以在Java应用程序中使用in this article中讨论的任何API。

7 个答案:

答案 0 :(得分:20)

我没有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中可用:

  • 您可以通过检查System.getProperty("os.name");返回"Windows 7"来从Java端执行此操作。
  • 如果您使用我提供的小JNI代码段构建,您可以使用LoadLibrary动态加载shell32.dll库然后使用{{3}获取SetCurrentProcessExplicitAppUserModelID函数指针来增强它}。如果GetProcAddress返回NULL,则表示该符号在shell32中不存在,因此它不是Windows 7。

编辑:GetProcAddress

参考文献:

答案 1 :(得分:5)

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

答案 2 :(得分:4)

尝试使用JSmooth。我总是用这个。在JSmooth中Skeleton Windowed Wrapper下的选项名称为

  

exe程序中的Lauch java应用程序

见此图片。

JSmooth

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

马亭

答案 3 :(得分:4)

我已经使用JNA实现了对SetCurrentProcessExplicitAppUserModelID方法的访问,并且当用作MSDN文档建议时它可以很好地工作。我从来没有像你的代码片段那样使用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进行访问。

答案 4 :(得分:3)

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在安装过程中创建时应用于快捷方式。

答案 5 :(得分:1)

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

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

答案 6 :(得分:0)

我在没有任何ID设置的情况下修复了我的。 Launch4J中有一个选项,如果你正在使用它,你说你那么...

您可以将标题更改为JNI Gui,然后使用JRE将其包装在jar中。 好处是它现在在运行中运行.exe而不是用你的jar运行javaw.exe。它可能是在引擎盖下进行的(不确定)。 另外我也注意到CPU资源减少了大约40-50%,甚至更好!

钉扎工作正常,所有窗口功能都已启用。

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