Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pinning a Java application to the Windows 7 taskbar

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.

Result of pinning a Launch4j application to the taskbar

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.

Result of specifying javaw.exe as a host application

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.

like image 236
Paul Lammertsma Avatar asked Dec 02 '09 17:12

Paul Lammertsma


People also ask

How do I pin an app to the taskbar in Windows 7?

Pinning Programs to the Windows 7 Taskbar – quickly find and open programs/menu items. Windows 7 allows you to pin favorite programs to the Taskbar for quicker-easy access. The icons for programs pinned to the Taskbar can be arranged or rearranged by clicking and dragging them in any order you wish.

How do I pin a program to the taskbar control panel?

Open Control Panel through the Start menu or a desktop icon – whatever method you use to launch it. Then once it's open, the Control Panel button will be displayed on the Taskbar. Right-click on it and select Pin this Program to Taskbar. That's it.

How do I permanently pin to taskbar?

First, launch the program as you normally would. At the bottom of your screen, the program icon appears on the taskbar. Right-click it and, from the menu, select Pin to taskbar. The icon is pinned permanently to the taskbar.

How do I pin Java to the taskbar in Windows 10?

Create a shortcut. Move the shortcut into C:\ProgramData\Microsoft\Windows\Start Menu\Programs. Then click on the start menu and drag and drop your newly added icon wherever you like.


1 Answers

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 using LoadLibrary then getting back the SetCurrentProcessExplicitAppUserModelID function pointer using GetProcAddress. If GetProcAddress returns NULL, it means the symbol is not present in shell32 hence it's not Windows 7.

EDIT: JNA Solution.

References:

  • The JNI book for more JNI examples
  • Java Native Access (JNA)
like image 133
Gregory Pakosz Avatar answered Sep 19 '22 21:09

Gregory Pakosz