【问题标题】:How can I hide some parameters from C DLL function on JNA Wrapper side?如何从 JNA Wrapper 端的 C DLL 函数中隐藏一些参数?
【发布时间】:2021-03-02 14:57:21
【问题描述】:

我已经使用 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 Wrapper 内部完成,而不是从调用 java Wrapper 的代码中完成。 恐怕这不可能,是吗? 除非我创建另一个 C DLL(带有公共 int initFunction(int firstValue) 函数)调用第一个 C DLL(嵌入 initFunction(int firstValue, int secondValue, int thirdValue)。但我宁愿在 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);
    }
}

感谢您的帮助。

【问题讨论】:

  • 你能不能把完整版放在一个用户看不到的“内部”包中,并在公共 API 中添加单参数层?如果您使用模块,则可以强制执行强封装。
  • 嗨@Daniel Widdis,我知道内部包和java 模块,但我会看看。谢谢

标签: jna


【解决方案1】:

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

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());
  }
}

和上面的思路一样,默认的方法会隐藏丑陋的部分。这种情况下虽然不是通过托管INSTANCE访问函数,而是直接通过函数指针访问。

【讨论】:

  • 嗨,Matthias,我喜欢你的第二个回复,它接近我想要的(向我的 JNA Wrapper 的用户隐藏一些参数)。但是我的 JNA Wrapper 没有 main() 只有类型和方法定义以及 Native.load 指令的接口。在我这边,Native.load 是由一个 Sample.java 文件完成的,该文件通过 main() 调用 LibC 中定义的方法。因此,在我的架构中,我提供了一个 JNA Wrapper 作为扩展 jna.Library 的接口,并且 Sample.java 是我的 JNA Wrapper 的客户端。而且我不知道在 JNA Wrapper 中设置 Native.load 是否干净。谢谢
  • 抱歉,我不明白您的设置,您能否提供一个最小的示例来说明并反映您的设置?
  • 我与一个单独的团队合作。我为他们提供了 C DLL 和一个包含接口 IJNALibrary 的 jar 文件。由其他团队将 jar 文件 thanx 中定义的映射方法调用到我提供的 Sample.java 文件中。 Native.load 在 Sample.java 文件中完成。见上面我已经用 Sample.java 文件更新了我的初始帖子。谢谢。
  • 最后在与 Daniel Widdis 讨论之后,我将通过在我的库界面中添加 Native.load 指令而不是在 Sample.java 文件中设置它来执行您提出的第二个解决方案
猜你喜欢
  • 2011-05-23
  • 2016-08-23
  • 2019-08-28
  • 2010-10-25
  • 1970-01-01
  • 2011-11-30
  • 1970-01-01
  • 1970-01-01
  • 2014-06-08
相关资源
最近更新 更多