Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Franz Wong
Franz Wong

Posted on

Using Java Native Access (JNA) to call MacOS API

Introduction

Here is a simple example about how to use JNA with MacOS API. I am not going to explain this, because I am still learning, but I keep code minimal to make it easier to understand and get the basic idea about how to do it for other purposes.

Most of the code is fromIntellij-Community (Apache License).

Dependency

All we need isjna-platform.

<dependency>  <groupId>net.java.dev.jna</groupId>  <artifactId>jna-platform</artifactId>  <version>5.13.0</version></dependency>
Enter fullscreen modeExit fullscreen mode

Code

There are totally 4 source code files.ID is copied from IntelliJ without modification.MyFoundation andMyFoundationLibrary are trimmed fromFoundation andFoundationLibrary respectively.

Application.java

package org.example;import com.sun.jna.Pointer;import static org.example.MyFoundation.*;public class Application {    public static void main(String[] args) throws Exception {        ID processInfoCls = getObjcClass("NSProcessInfo");        Pointer processInfoSel = createSelector("processInfo");        Pointer processNameSel = createSelector("processName");        ID processInfo = invoke(processInfoCls, processInfoSel);        ID processNameNSString = invoke(processInfo, processNameSel);        String processName = toStringViaUTF8(processNameNSString);        System.out.println(processName);    }}
Enter fullscreen modeExit fullscreen mode

ID.java

package org.example;import com.sun.jna.NativeLong;public final class ID extends NativeLong {    public ID() {    }    public ID(long peer) {        super(peer);    }    public static final ID NIL = new ID(0L);    public boolean booleanValue() {        return intValue() != 0;    }}
Enter fullscreen modeExit fullscreen mode

MyFoundationLibrary.java

public interface MyFoundationLibrary extends Library {    int kCFStringEncodingUTF8 = 0x08000100;    ID objc_getClass(String className);    Pointer sel_registerName(String selectorName);    int CFStringGetLength(ID theString);    byte CFStringGetCString(ID theString, byte[] buffer, int bufferSize, int encoding);}
Enter fullscreen modeExit fullscreen mode

MyFoundation.java

package org.example;import com.sun.jna.*;import java.lang.reflect.Proxy;import java.util.Collections;public class MyFoundation {    private static final MyFoundationLibrary myFoundationLibrary;    private static final Function myObjcMsgSend;    static {        myFoundationLibrary = Native.load("Foundation", MyFoundationLibrary.class, Collections.singletonMap("jna.encoding", "UTF8"));        NativeLibrary nativeLibrary = ((Library.Handler) Proxy.getInvocationHandler(myFoundationLibrary)).getNativeLibrary();        myObjcMsgSend = nativeLibrary.getFunction("objc_msgSend");    }    public static ID getObjcClass(String className) {        return myFoundationLibrary.objc_getClass(className);    }    public static Pointer createSelector(String s) {        return myFoundationLibrary.sel_registerName(s);    }    private static Object [] prepInvoke(ID id, Pointer selector, Object[] args) {        Object[] invokArgs = new Object[args.length + 2];        invokArgs[0] = id;        invokArgs[1] = selector;        System.arraycopy(args, 0, invokArgs, 2, args.length);        return invokArgs;    }    public static ID invoke(final ID id, final Pointer selector, Object... args) {        // objc_msgSend is called with the calling convention of the target method        // on x86_64 this does not make a difference, but arm64 uses a different calling convention for varargs        // it is therefore important to not call objc_msgSend as a vararg function        return new ID(myObjcMsgSend.invokeLong(prepInvoke(id, selector, args)));    }    public static String toStringViaUTF8(ID cfString) {        if (ID.NIL.equals(cfString)) return null;        int lengthInChars = myFoundationLibrary.CFStringGetLength(cfString);        int potentialLengthInBytes = 3 * lengthInChars + 1; // UTF8 fully escaped 16 bit chars, plus nul        byte[] buffer = new byte[potentialLengthInBytes];        byte ok = myFoundationLibrary.CFStringGetCString(cfString, buffer, buffer.length, MyFoundationLibrary.kCFStringEncodingUTF8);        if (ok == 0) throw new RuntimeException("Could not convert string");        return Native.toString(buffer);    }}
Enter fullscreen modeExit fullscreen mode

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Location
    Hong Kong
  • Joined

More fromFranz Wong

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp