如何在 JNA Wrapper 端隐藏 C DLL 函数的一些参数?

How can I hide some parameters from C DLL function on JNA Wrapper side?

我已经使用 JNA 成功包装了一个 C DLL 库。 因为我不是 C 开发部分的所有者,所以我想隐藏 我在 java 端包装的 C 函数的一些参数。

更准确地说,我的 java 代码如下:

public interface IJNALibrary extends Library {

    // INIT FUNCTION
    public int initFunction(int firstValue, int secondValue, int thirdValue);
}

在 C 端,我在 *.h 文件中有:

extern "C" CSAMPLE_API int initFunction (
    unsigned            firstValue,
    unsigned            secondValue,
    unsigned            thirdValue);

我的目的是直接将 secondValue 和 thirdValue 参数设置为 1,从而对 java API 用户隐藏这些参数。 我不希望用户知道他可以更改这些参数的值。 事实上,我想要类似的东西:

public interface IJNALibrary extends Library {

    // INIT FUNCTION
    public int initFunction(int firstValue);
}

并且 initFunction(int firstValue) 从 C DLL 部分调用 initFunction(int firstValue, int secondValue, int thirdValue)。 但这必须在 java 包装器内部完成,而不是从调用 java 包装器的代码中完成。 恐怕这不可能,是吗? 除非我创建另一个调用第一个 C DLL(嵌入 initFunction(int firstValue, int secondValue, int thirdValue) 的 C DLL(具有 public int initFunction(int firstValue) 函数)。但我宁愿在java 端,以便不管理 2 个 C DLL。

另请参阅下面的 Sample.java 文件,该文件调用 IJNALibrary 接口中定义的映射方法。

public class Sample {

    static IJNALibrary IJNAFunctions;
    
    public static void main(String[] args) throws IOException {
    
        System.setProperty("jna.library.path", "./librayPath");
        
        // LOADING  LIBRARY
        IJNAFunctions = (IJNALibrary) Native.load("c", IJNALibrary.class);

        int firstValue = 1;
        int secondValue = 2;
        int thirdValue = 3;

        int initReturn = IJNAFunctions.initFunction(firstValue, secondValue, thirdValue);
    }
}

感谢您的帮助。

这取决于您要存档的内容。如果你想让用户更容易调用 init,这是一个选项(使用 libc 中的 gethostname 进行演示),它使用 Java8 功能,允许向接口添加默认方法:

public class TestDefaultMethod {
  public static interface LibC extends Library {
    LibC INSTANCE = Native.load("c", LibC.class);

    // Original binding of method
    int gethostname(byte[] name, int len);

    // Helper method to make it easier to call gethostname
    default String gethostname() {
        byte[] result = new byte[255];
        LibC.INSTANCE.gethostname(result, result.length);
        return Native.toString(result);
    }
  }

  public static void main(String[] args) {
    // Usage
    System.out.println(LibC.INSTANCE.gethostname());
  }
}

Java 开发人员通常不会将数组赋值给函数,函数会填充它们,java 开发人员永远不会在单独的参数中传递数组的长度。这些是函数的 C 性质的产物。在包装函数中分配一个数组,完成本机调用,然后打开数组。所有丑陋的 C 特性都隐藏在默认方法中。

如果您根本不想公开 java 上的方法(请注意,如果您的用户可以访问 JNA 库,他们可以绕过您的保护!),您可以使用函数指针直接:

public class TestDefaultMethod {

  public static interface LibC extends Library {
    NativeLibrary libc = NativeLibrary.getInstance("c");
    LibC INSTANCE = Native.load("c", LibC.class);

    default String gethostname() {
        byte[] result = new byte[255];
        libc.getFunction("gethostname").invokeInt(new Object[] {result, result.length});
        return Native.toString(result);
    }
  }

  public static void main(String[] args) {
    System.out.println(LibC.INSTANCE.gethostname());
  }
}

同上思路,默认方法会把丑的部分隐藏起来。在这种情况下,虽然函数不是通过托管实例访问的,而是直接通过函数指针访问的。