Following up on my previous question concerning the Windows 7 taskbar, I would like to diagnose why Windows isn't acknowledging that my application is independent of javaw.exe. I presently have the following JNA code to obtain the AppUserModelID:
public class AppIdTest {
    public static void main(String[] args) {
        NativeLibrary lib;
        try {
            lib = NativeLibrary.getInstance("shell32");
        } catch (Error e) {
            System.err.println("Could not load Shell32 library.");
            return;
        }
        Object[] functionArgs = new Object[1];
        String functionName = null;
        Function function;
        try {
            functionArgs[0] = new String("Vendor.MyJavaApplication")
                    .getBytes("UTF-16");
            functionName = "GetCurrentProcessExplicitAppUserModelID";
            function = lib.getFunction(functionName);
            // Output the current AppId
            System.out.println("1: " + function.getString(0));
            functionName = "SetCurrentProcessExplicitAppUserModelID";
            function = lib.getFunction(functionName);
            // Set the new AppId
            int ret = function.invokeInt(functionArgs);
            if (ret != 0) {
                Logger.out.error(function.getName() + " returned error code "
                        + ret + ".");
            }
            functionName = "GetCurrentProcessExplicitAppUserModelID";
            function = lib.getFunction(functionName);
            // Output the current AppId
            System.out.println("2: " + function.getString(0));
            // Output the current AppID, converted from UTF-16
            System.out.println("3: "
                    + new String(function.getByteArray(0, 255), "UTF-16"));
        } catch (UnsupportedEncodingException e) {
            System.err.println("System does not support UTF-16 encoding.");
        } catch (UnsatisfiedLinkError e) {
            System.err.println(functionName + " was not found in "
                    + lib.getFile().getName() + ".");
        }
    }
}
The output of the application is seemingly gibberish:
1: ‹ÿU‹ìƒìL¡¬Ÿv3ʼnEüSV‹uƒ&
2: ‹ÿU‹ìƒìL¡¬Ÿv3ʼnEüSV‹uƒ&
3: ????????????????P???????????
Being aware of the fact that the output may be UTF-16, in (3) I attemp开发者_高级运维ted to convert a byte array from UTF-16. In all honesty I don't know if my approach here is right as (a) I don't know the size of a PWSTR and (b) I don't know if GetCurrentProcessExplicitAppUserModelID is indeed returning a byte array or string.
I'm aware that JSmooth will run the GUI process in a wrapper which simulates this effect. Launch4j claims to do the same, but doesn't appear to work. I am looking to have the AppUserModelID set regardless of the Java wrapper.
What is going wrong here?
I didn't see your question before otherwise I would have given a try even without a bounty.
Here is what I came up with. Please note, as stated in the code itself, I didn't implement proper memory clean up with the CoTaskMemFree function (from Ole32.dll). So I suggest you take only the implementation for SetCurrentProcessExplicitAppUserModelID()
package com.stackoverflow.AppIdTest;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.WString;
import com.sun.jna.ptr.PointerByReference;
public class AppIdTest
{
  public static void main(String[] args) throws Exception
  {
    setCurrentProcessExplicitAppUserModelID(AppIdTest.class.getName());
    System.out.println(getCurrentProcessExplicitAppUserModelID());
  }
  // DO NOT DO THIS, IT'S JUST FOR TESTING PURPOSE AS I'M NOT FREEING THE MEMORY
  // AS REQUESTED BY THE DOCUMENTATION:
  //
  // http://msdn.microsoft.com/en-us/library/dd378419%28VS.85%29.aspx
  //
  // "The caller is responsible for freeing this string with CoTaskMemFree when
  // it is no longer needed"
  public static String getCurrentProcessExplicitAppUserModelID()
  {
    final PointerByReference r = new PointerByReference();
    if (GetCurrentProcessExplicitAppUserModelID(r).longValue() == 0)
    {
      final Pointer p = r.getValue();
      return p.getString(0, true); // here we leak native memory by lazyness
    }      
    return "N/A";
  }
  public static void setCurrentProcessExplicitAppUserModelID(final String appID)
  {
    if (SetCurrentProcessExplicitAppUserModelID(new WString(appID)).longValue() != 0)
      throw new RuntimeException("unable to set current process explicit AppUserModelID to: " + appID);
  }
  private static native NativeLong GetCurrentProcessExplicitAppUserModelID(PointerByReference appID);
  private static native NativeLong SetCurrentProcessExplicitAppUserModelID(WString appID);
  static
  {
    Native.register("shell32");
  }
}
Does it work for you?
At least here it correctly prints back:
com.stackoverflow.AppIdTest.AppIdTest
If you just need to set the AppUserModelId then the above JNA code is enough. However if you want to take advantage of the new Windows 7 features in you Java application then check out J7Goodies a Java library providing Windows 7 taskbar extensions.
EDIT: more info from J7Goodies Programmer's Guide
4.2. Setting AppUserModelID
In order to use any of the Windows 7 features an application must explicitly set its process identifier – Application User Model ID (
AppUserModelID). It can have no more than 128 characters and cannot contain spaces. Each section should be camel-cased, for example:CompanyName.ProductName.SubProduct.VersionInformationThis identifier must be set before any GUI (window) is shown. You set it by calling:
// Remember to set AppUserModelID before creating any UI
AppUserModelId.setCurrentProcessId("StrixCode.J7Goodies.Appname");
4.3. Setting Window Properties
A Java application cannot be pinned to the Windows 7 taskbar unless its window properties are defined. The properties consist of four fields:
- AppUserModelID – the same as passed to
AppUserModelId.setCurrentProcessId(String)- RelaunchDisplayName – application’s name
- RelaunchCommand – the full command used to launch the application. In case of a Java program it will be:
<path to javaw.exe> -jar <path to application jar>- RelaunchIcon – path to application’s icon
Important:
RelaunchCommandandRelaunchDisplayNamemust always be set together. To set these properties use the straightforward WindowProperties class.
WindowProperties props = new WindowProperties(myFrame);
props.setRelaunchCommand("<full path to javaw.exe –arguments>");
props.setRelaunchDisplayName("My Java Application");
props.setRelaunchIcon("<full path to an .ico or .exe file>");
props.setAppUserModelID("StrixCode.J7Goodies.Appname");
props.save();
Here's a more simple example on how to call SetCurrentProcessExplicitAppUserModelID via JNA:
import com.sun.jna.*;
import com.sun.jna.win32.*;
interface Shell32 extends StdCallLibrary {
    Shell32 INSTANCE = (Shell32) Native.loadLibrary("shell32", Shell32.class, W32APIOptions.DEFAULT_OPTIONS);
    NativeLong SetCurrentProcessExplicitAppUserModelID(WString appID);
}
 
         
                                         
                                         
                                         
                                        ![Interactive visualization of a graph in python [closed]](https://www.devze.com/res/2023/04-10/09/92d32fe8c0d22fb96bd6f6e8b7d1f457.gif) 
                                         
                                         
                                         
                                         加载中,请稍侯......
 加载中,请稍侯......
      
精彩评论