代码之家  ›  专栏  ›  技术社区  ›  Paul Lammertsma

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

  •  35
  • Paul Lammertsma  · 技术社区  · 15 年前

    在Windows 7下,我使用Actuk4J作为Java应用程序的包装器,据我的理解,它本质上是一个实例。 javaw.exe 这又解释了Java代码。因此,当试图将应用程序固定到任务栏时,Windows会将其固定。 JavaW.EXE . 如果没有所需的命令行,我的应用程序将不会运行。

    Result of pinning a Launch4j application to the taskbar

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

    我试图更改注册表项 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(AppUsRealMeid ID)嵌入到Java应用程序中变得很感兴趣。

    我相信我可以通过传递一个 AppUserModelID 到Windows。有一个 shell32 方法, SetCurrentProcessExplicitAppUserModelID .根据GregoryPakosz的建议,我实现了它,试图将我的应用程序识别为 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 关于如何通过 AppID 使用JNA。

    我向格雷戈里·帕克斯(GregoryPakosz)奖励了他对JNI的精彩回答,这让我走上了正轨。

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

    7 回复  |  直到 6 年前
        1
  •  20
  •   Community CDub    8 年前

    我没有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()

    当它在沙盒中工作时,下一步是在应用程序中添加操作系统检测 setcurrentprocessexplicitappusermodelid()。 显然只在Windows 7中可用:

    • 您可以通过检查Java端来实现这一点。 System.getProperty("os.name"); 收益率 "Windows 7" .
    • 如果您是从我给出的JNI小片段构建的,那么您可以通过动态加载 shell32.dll 库使用 LoadLibrary 然后回来 SetCurrentProcessExplicitAppUserModelID 函数指针使用 GetProcAddress . 如果 获取进程地址 收益率 NULL ,表示符号不在 shell32 因此它不是Windows7。

    编辑: JNA Solution .

    参考文献:

        2
  •  5
  •   torn    14 年前

    有一个Java库为Java提供了新的Windows 7特性。它叫 J7Goodies 通过 Strix Code .使用它的应用程序可以正确地固定到Windows7任务栏。您还可以创建自己的跳转列表等。

        3
  •  4
  •   Community CDub    8 年前

    尝试使用 JSmooth . 我总是用这个。在jsmooth中,是否有以下选项 Skeleton 通过 Windowed Wrapper 打电话

    在EXE过程中使用Java应用程序

    请参见此图。

    JSmooth

    还可以传递命令行参数。
    我认为这是你的解决方案。

    马蒂恩

        4
  •  4
  •   The_Fire    15 年前

    我已经使用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在运行时访问它。

        5
  •  3
  •   Eric Brown    15 年前

    setcurrentProcessExplicitappUserModelID(或setAppID())实际上会执行您尝试执行的操作。但是,修改安装程序以在快捷方式上设置appusermodel.id属性可能会更容易—引用 Application User Model ID 上述文件:

    System.AppUserModel.ID 应用程序快捷方式文件的属性。快捷方式(作为ishellLink、clsid_shelllink或.lnk文件)通过ipropertyStore和在shell中使用的其他属性设置机制支持属性。这样,任务栏就可以识别正确的pin快捷方式,并确保属于进程的窗口与该任务栏按钮相关联。 注意:创建快捷方式时,System.AppUserModel.ID属性应应用于快捷方式。使用Microsoft Windows Installer(MSI)安装应用程序时, MsiShortcutProperty 表允许在安装过程中创建快捷方式时将AppUserModelID应用于该快捷方式。

        6
  •  1
  •   rednoah    9 年前

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

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

        7
  •  0
  •   LazerBanana    8 年前

    我在没有任何身份设置的情况下修复了我的。 在Launch4j中有一个选项,如果您使用它,并且您说您使用它,那么……

    您可以将头改为JNI GUI,然后用JRE将其包装在JAR中。 好的是它现在在进程中运行.exe,而不是用jar运行javaw.exe。可能是在引擎盖下面(不确定)。 另外,我还注意到它占用的CPU资源大约少40-50%,这甚至更好!

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

    我希望它能帮助一些人,因为我花了近2天的时间试图用我未修饰的JavaFX应用程序解决这个问题。