views:

1299

answers:

4

Original question

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, SetAppID(). (Or SetCurrentProcessExplicitAppUserModelID?) Is it possible to call it via JNI? If so, would this even resolve the issue?

On a side note, I was curious if any of the APIs discussed in this article could be implemented for a Java application.

Edit after implementing JNA, as Gregory Pakosz suggested

I've now implemented the following 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.

+2  A: 

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:

In the System.AppUserModel.ID property of the application's shortcut file. A shortcut (as an IShellLink, CLSID_ShellLink, or a .lnk file) supports properties through IPropertyStore and other property-setting mechanisms used throughout the Shell. This allows the taskbar to identify the proper shortcut to pin and ensures that windows belonging to the process are appropriately associated with that taskbar button. Note: The System.AppUserModel.ID property should be applied to a shortcut when that shortcut is created. When using the Microsoft Windows Installer (MSI) to install the application, the MsiShortcutProperty table allows the AppUserModelID to be applied to the shortcut when it is created during installation.

Eric Brown
This is a good suggestion, thanks. I have actually looked into this (I use Inno Setup, which claims to include the `System.AppUserModel.ID` in shortcuts. After some experimentation I couldn't in fact reproduce the result. Furthermore, even if this worked, a user would end up confused when making a shortcut by hand. I would much rather prefer to encapsulate an `AppUserModelID` into my Java application. Would this be possible via JNI?
Paul Lammertsma
+3  A: 

Try to use JSmooth. I use always this one. In JSmooth is there an option under Skeleton by Windowed Wrapper called

Lauch java app in exe process

See on this image.

JSmooth

Also command line arguments can be passed.
I think this can be a solution for you.

Martijn

Martijn Courteaux
I've heard of this wrapper in the past. I've given it a swing, but I can't get it to do anything as it regularly hangs. Whenever I select the "Skeleton" tab, I cannot select another tab. If I visit that tab last, and press compile, it remains hanging on "Loading icon...". I'm using Windows 7, but the same happens using Vista compatibility mode. It doesn't start at all for compatibility under previous versions of Windows. Any ideas?
Paul Lammertsma
After some fiddling I got a version compiled. It seems selecting an icon causes the problems described in my previous comment. If I select **Launch the app in the exe process** under the **Skeleton** tab, pinning works as expected. This doesn't really answer my question of embedding an `AppID`, but it circumvents the problem. Thank you!
Paul Lammertsma
I can only load non-ICO images for the icon. Regardless, when pinning the application, the RelaunchIconResource (see http://msdn.microsoft.com/en-us/library/dd391573(VS.85).aspx) is shown as Window's default application icon. Using Resource Hacker, I can see that this icon is contained within the EXE under Icon Group A2. Replacing it is reflected in the pinned application, but this is hardly a solution.
Paul Lammertsma
Concerning the above suggestion to use a single PNG opposed to an ICO: this is not an option for my project, I'm afraid, as the O/S-dependent distribution really requires different resolution application icons. I appreciate this answer, but it doesn't address the question at hand and switching to a different wrapper is not a possibility.
Paul Lammertsma
+4  A: 

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 void native 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_howework_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:

Gregory Pakosz
This looks very good! There was no specific reason I was going for JNI. I have some Mac-specific methods using JNI, but I will definitely take your suggestion into consideration.
Paul Lammertsma
Ok. I didn't know whether you were familiar with JNI or not
Gregory Pakosz
In retrospect, I can only get the JNI methods specifically written for Mac compiled on a Mac. I've been looking into JNA, and it looks much more intuitive. I haven't actually tried it yet, but as the bounty is about to expire, I'm going to go ahead and award the points.
Paul Lammertsma
I've edited my question with my JNA implementation. This appears to have no effect; does this have something to do with UTF-16? If so, how do I pass a String in this encoding?
Paul Lammertsma
+2  A: 

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:

interface Shell32 extends StdCallLibrary {

    int SetCurrentProcessExplicitAppUserModelID( WString appID );

}

Then using JNA to load Shell32 and call the function:

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 );

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.

The_Fire
This is very much in line with Gregory Pakosz's answer to my follow-up question (http://stackoverflow.com/questions/1907735/using-jna-to-get-set-application-identifier). I'm very curious about the "custom DLL" you speak of. This is something made in C#, I presume? Can you give any more information?
Paul Lammertsma
No I chose to make a custom DLL in C which is very easy to call with JNA. So far I have only implemented a SetWindowAppId function in the C DLL which calls SHGetPropertyStoreForWindow and is then able manipulate the resulting COM interfaces to set the window's appID (exactly as the various Microsoft examples show.) I am also planning on implementing a few functions for manipulating my application's Jump Lists but I haven't got there yet. Quite conveniently JNA provides API in its Native class for retrieving a Java Window's handle/pointer which makes this approach possible.
The_Fire