Functions

com.etdon.winj.function

One of the primary features of the library is the implementation of native function bindings. Those bindings allows you to easily build and call functions exported by native libraries.

Calling

In order to call a native function with (or without) an existing binding you should use a NativeCaller. The default constructor takes both a Linker as well as a SymbolLookupCache as its parameters. If you already created a WindowsAPI instance however, you can access a shared instance using its ServiceProvider available through the #getServiceProvider method:

try (final Arena arena = Arena.ofConfined()) {
    final WindowsAPI windowsAPI = WindowsAPI.of(arena);
    final ServiceProvider serviceProvider = windowsAPI.getServiceProvider();
    final NativeCaller nativeCaller = serviceProvider.get(NativeCaller.class);
}

The #call method of NativeCaller takes a NativeFunction as its only parameter. NativeFunction is the abstract parent of all native functions with an implemented binding. This is how you can use your obtained NativeCaller to - for example - call the GetForegroundWindow function of the user32 system library:

try (final Arena arena = Arena.ofConfined()) {
    final WindowsAPI windowsAPI = WindowsAPI.of(arena);
    final ServiceProvider serviceProvider = windowsAPI.getServiceProvider();
    final NativeCaller nativeCaller = serviceProvider.get(NativeCaller.class);
    nativeCaller.call(GetForegroundWindow.getInstance());
}

Bindings for functions with parameters always feature a fluent builder that allows for easy construction of complex calls while respecting optional parameters and other properties of the function:

final MemorySegment windowHandle = (MemorySegment) nativeCaller.call(
    OpenProcess.builder()
            .access(ProcessAccessRight.PROCESS_ALL_ACCESS)
            .inheritHandle(false)
            .processId(12345)
            .build()
);

As you can see in the provided example named constants exist for all relevant types and functions with implemented bindings. References to the classes that hold these constants can be found in the corresponding Javadoc comments.

In case the function you're trying to call doesn't have an implemented binding yet, you can use a GenericFunction instance instead. The way you call a GenericFunction doesn't differ from an implemented one, however, the building works slightly differently. Instead of having named builder methods for every component you'll have to provide the library name, function name, potential return type and all parameter instances (or their MemoryLayout in case they are null) instead:

GenericFunction.builder()
        .library(Library.KERNEL_32)
        .name("CloseHandle")
        .returnType(boolean.class)
        .parameter(handlePointer)
        .build();

Last updated