Feedback / query on jextract for Windows 10

Duncan Gittins duncan.gittins at gmail.com
Sat Jan 23 18:34:25 UTC 2021


I've been using Panama on Windows 10 to eliminate my C# apps which I 
would normally call via ProcessBuilder. I'm very impressed so far and 
looking forward to newer versions: everything I have written in Windows 
C# is now replaced with foreign memory / linker calls to native code 
using the latest Java 16 EA.  I have examples for various Win32 API and 
COM objects, HWND objects and screensaver. However I'm struggling to get 
the code running with output from jextract build. Perhaps I overlooked 
something very obvious in how to setup?

One example app is attached - this calls SystemParametersInfoA to set 
the Windows desktop background. There are two implementations of 
setImage: one with hardwired MethodHandle works fine, but the jextract 
version gives UnsupportedOperationException for layout unrelated to 
SystemParametersInfoA. If I edit the generated code to comment out all 
lines unrelated to SystemParametersInfoA then both versions of setImage 
work.

The jextract command on Windows 10 (with fresh Vis Studio installation) 
expands shlobj_core.h because I my app also uses IShellLink.

      set "WINKIT=c:\Program Files (x86)\Windows 
Kits\10\Include\10.0.18362.0"
      jextract --source -t duncan.win32.shobjidl_core -d 
shlobj_core.h/java     "%WINKIT%/um/shlobj_core.h"

Another issue is that this single windows header file gives rise to 11MB 
jar and is unusable inside Eclipse (on my slow PC), and is slow startup 
at runtime. Have you considered changing the generated code to use lazy 
Supplier<MethodHandle> instead of MethodHandle to ensure only required 
handles are created on-demand?

Kind regards

Duncan


-------------- next part --------------
package duncan.sample;

import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import duncan.win32.shobjidl_core.shlobj_core_h;
//import duncan.win32.shobjidl_core.shlobj_core_h$19;
import jdk.incubator.foreign.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;

/**

 %JAVAHOME%\bin\java -Dforeign.restricted=permit --add-modules jdk.incubator.foreign -Dfile.encoding=UTF-8 -cp C:\dev\jars\samplepanama.jar;C:\dev\jars\jextract-shlobj_core.h.jar duncan.sample.Screen "C:\Temp\sm all\1.jpg"

	All Windows native calls fail with same error, when initialising the same layout (not related to the actual calls being made)
	shlobj_core_h$constants$16.java:1844 is:
		static final VarHandle _NT_TIB$ArbitraryUserPointer$VH_ = MemoryHandles.asAddressVarHandle(_NT_TIB$struct$LAYOUT_.varHandle(long.class, MemoryLayout.PathElement.groupElement("ArbitraryUserPointer")));

	Note - no runtime issues if commenting out all lines unrelated to SystemParametersInfoA

Exception in thread "main" java.lang.AssertionError: java.lang.ExceptionInInitializerError
        at duncan.win32.shobjidl_core.shlobj_core_h$19.SystemParametersInfoA(shlobj_core_h$19.java:552)
        at duncan.sample.Screen.setImage(Screen.java:49)
        at duncan.sample.Screen.main(Screen.java:64)
Caused by: java.lang.ExceptionInInitializerError
        at duncan.win32.shobjidl_core.shlobj_core_h$19.SystemParametersInfoA(shlobj_core_h$19.java:550)
        ... 2 more
Caused by: java.lang.UnsupportedOperationException: Invalid alignment requirements for layout b64(ArbitraryUserPointer)[abi/kind=POINTER,layout/name=ArbitraryUserPointer]
        at jdk.incubator.foreign/jdk.internal.foreign.LayoutPath.checkAlignment(LayoutPath.java:273)
        at jdk.incubator.foreign/jdk.internal.foreign.LayoutPath.dereferenceHandle(LayoutPath.java:159)
        at jdk.incubator.foreign/jdk.incubator.foreign.MemoryLayout.lambda$varHandle$2(MemoryLayout.java:488)
        at jdk.incubator.foreign/jdk.incubator.foreign.MemoryLayout.computePathOp(MemoryLayout.java:534)
        at jdk.incubator.foreign/jdk.incubator.foreign.MemoryLayout.varHandle(MemoryLayout.java:488)
        at duncan.win32.shobjidl_core.shlobj_core_h$constants$16.<clinit>(shlobj_core_h$constants$16.java:1844)
		
 */
public class Screen
{
	private static void Win32_checkThat(boolean condition, Supplier<String> error)
    {
        if (!condition)
        {
            String message = error.get();
            System.err.println(message);
            throw new RuntimeException(message);
        }
    }
	private static final int SPI_SETDESKWALLPAPER  = 0x0014; //shlobj_core_h.SPI_SETDESKWALLPAPER();
	private static final int SPIF_UPDATEINIFILE    = 0x01;   // shlobj_core_h.SPIF_UPDATEINIFILE();
	private static final int SPIF_SENDCHANGE       = 0x02;   // shlobj_core_h.SPIF_SENDCHANGE();
	
    private static void setImage(String path) throws Throwable
    {
        final long t0 = System.nanoTime();
        try (NativeScope scope = NativeScope.unboundedScope())
        {

            MemorySegment img = CLinker.toCString(path, /*StandardCharsets.UTF_16LE,*/ scope);
            // int status = shlobj_core_h$19.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, img.address(), SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
            int status = shlobj_core_h.SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, img.address(), SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
            Win32_checkThat(status != 0, () -> "setImage status="+status+" path="+path);
        }

        final long now = System.nanoTime();
        System.out.println("setImage ms="+TimeUnit.NANOSECONDS.toMillis(now - t0)+" "+path);
    }

    private static void setImageWORKS(String path) throws Throwable
	{
        final long t0 = System.nanoTime();
		LibraryLookup user32 = LibraryLookup.ofLibrary("user32");
		MethodHandle spi = CLinker.getInstance().downcallHandle(user32.lookup("SystemParametersInfoA").get()
			// BOOL SystemParametersInfoA         (UINT uiAction,  UINT uiParam,   PVOID pvParam,       UINT fWinIni);
			, MethodType.methodType(int.class,     int.class,      int.class,      MemoryAddress.class, int.class)
			, FunctionDescriptor.of(CLinker.C_LONG,CLinker.C_LONG, CLinker.C_LONG, CLinker.C_POINTER,   CLinker.C_LONG));

		try (NativeScope scope = NativeScope.unboundedScope()) {
			MemorySegment img = CLinker.toCString(path, scope);
			int status = (int)spi.invokeExact(SPI_SETDESKWALLPAPER, 0, img.address(), SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
            Win32_checkThat(status != 0, () -> "setImageWORKS status="+status+" path="+path);
		}
        final long now = System.nanoTime();
        System.out.println("setImageWORKS ms="+TimeUnit.NANOSECONDS.toMillis(now - t0)+" "+path);
	}
	
    public static void main(String[] args) throws Throwable
    {
        if (args.length == 0)
            throw new IllegalArgumentException("Usage: "+Screen.class.getClass().getName()+" {image}");

        for(String arg : args)
        {
			String p = Path.of(arg).toAbsolutePath().toString();
            setImageWORKS(p);
            setImage(p);
        }
    }
}


More information about the panama-dev mailing list