【问题标题】:How to call a JNI DLL from C++如何从 C++ 调用 JNI DLL
【发布时间】:2009-08-12 04:29:05
【问题描述】:

我的任务是使用 C++ 与第三方公司的 dll 进行交互。

dll包自带:

  • dll 本身
  • 一个示例 Java 实现 - 由使用 SWIG 工具和 Java 源文件生成的 Java 包装器(库)组成
  • 说明所有公共数据类型、枚举和成员函数的文档。

我的另一位同事正在使用 Java(基于包中的示例)与 dll 交互,而我被要求使用 C++。 Java 示例看起来很简单......只需导入包装器并实例化文档中描述的任何类......

有关 dll 的更多信息:

  • 从文档中,它说 dll 是使用 C++ 编程的
  • 从 hexdump 中可以看出,它是使用 VC90(VS C++ 2008 对吗?)和来自 Dinkumware 的东西编译的。
  • 从depends.exe 输出中,函数似乎被包装在JNI 下。例如:_Java_mas_com_oa_rollings_as_apiJNI_Server_1disconnect@20

我的困境:

  • dll 公司没有更改 dll 中的任何内容,也没有提供任何其他信息。
  • 如何从 dll 中使用类中的成员函数?
  • 我做了一些简单的 LoadLibrary() 和 GetProcAddress 并设法获取公共成员函数的地址。
  • 但我不知道如何使用具有在 dll 中定义的数据类型参数的函数。例如:
    从文档中,成员函数定义为:

void Server::connect(const StringArray, const KeyValueMap) throw(std::invalid_argument,std::out_of_range)
typedef std::map Server::KeyValueMap typedef std::vector 服务器::StringArray

如何在 C++ 中调用该函数。我的编译器(VS 2005)中的 std::map 和 std::vector 具有不同的函数,列出了 dll 中的函数。例如,从depends.exe 输出:

  • std::map // KeyValueMap - del, empty, get, has_1key,set
  • std::vector // StringArray - 添加、容量、清除、获取、isEMPTY、保留、设置、大小

关于我应该如何解决这个问题的任何建议/策略?是否可以像 Java 示例那样简单地实例化该类?

【问题讨论】:

  • 这个东西是专有的吗,或者你能提供DLL给我们看吗?

标签: c++ class dll java-native-interface


【解决方案1】:

如果您尝试使用 VS 2005 来尝试与使用 VS2008 构建的 DLL 进行交互,那么除非您可以使用普通的 C 接口,否则您的尝试将注定失败。根据您的描述,情况并非如此; VS2005 和 VS2008 之间的运行时库不同,因此编译器之间的对象布局保持不变的可能性很小。您所指的“来自 Dinkumware 的东西”很可能是 Microsoft 使用 Dinkumware 的 C++ 标准库作为 ISTR。

在您上面的示例中,您还缺少一些重要信息 - 您描述的类型(Server::StringArray 和 Server::KeyValueMap)是标准库容器。好的,但是 what 的标准库容器?这些容器是模板,除非您知道这些模板实例化的确切类型,否则您会有点卡住。

这个 DLL 是否打算从 C++ 调用?它导出 JNI 接口的事实表明它可能不是一开始的。除了 _Java_... 格式的符号之外,它是否导出任何其他公共符号?

当然,如果没有其他方法并且您必须使用 C++ 而不是 Java,您可能需要考虑将 JVM 嵌入到您的 C++ 应用程序中并使用它来调用 C++ dll。这不是我所说的优雅解决方案,但它可能会奏效。

【讨论】:

  • oppss 对不起....我没有意识到我遗漏了那部分... typedef std::map<:string> Server::KeyValueMap typedef std::向量<:string> 服务器::StringArray
  • 该 dll 旨在与 Java 一起使用,正如 dll 公司所指出的那样……我的同事正在做 java 部分……但我的上司坚持要我让它与 C++ 一起使用。 . 没有 dll 公司的支持,我没有太多的选择可以在这里工作......
  • 如果函数实际上是导出的,您可能会根据上述警告(即,您必须使用相同的编译器版本)并自担风险来使用它们。如果它们不是,并且与 dll 通信的唯一方法是通过 Java API,那么与它们交互的唯一明智方法是在 C++ 中嵌入 JVM 来路由调用。恕我直言,后者是一个毫无意义的练习,但嘿,这只是我。
【解决方案2】:

我不太了解这里使用 C++ 标准库数据类型。 Java 代码如何提供std::map 参数?你传入的参数是否总是只是“不透明”的值,你会从之前调用库中得到的输出?只有这样,您才能使其在不同运行时的代码中运行。

无论如何...

当你创建一个 JNI 模块时,你运行javah.exe,它会生成一个带有如下声明的头文件:

JNIEXPORT void JNICALL Java_Native_HelloWorld(JNIEnv *, jobject);

你有这个模块的头文件吗?

如果我没记错的话,这些符号被导出为extern "C",所以如果你能得到正确的签名,你应该不会遇到名称修改或不兼容的内存分配器等问题。

方法签名末尾的“@20”表示该函数被声明为“stdcall”,并且在调用该函数时将 20 个字节放入堆栈。所有这些方法都应该以 JNIEnv*jobject 开头,我相信在 32 位环境中,它们总共有 8 个字节,因此您需要知道 12 个字节的参数才能生成正确的函数原型。

一旦弄清楚参数是什么,就可以生成如下内容:

typedef void (__stdcall *X)(JNIEnv *, jobject, jint i, jboolean b);

然后,您可以将 GetProcAddress 的结果转换为 X 并从您的 C++ 代码中调用它。

X x = (X)GetProcAddress(module, "name");
if (x) x(params...);

很遗憾,您所拥有的与我过去看到的不太一样。我习惯于处理来自 C/C++ 代码的 Java 数据类型,但看起来这个模块正在处理 Java 代码中的 C++ 数据类型,所以我不知道我的任何经验有多相关。希望这至少能有所帮助。

【讨论】:

  • 只有dll文件随包... 正如Timo所指出的,该dll并不是要被C++调用的...... dll的原始代码是在C++中的,但是在在这种情况下,它已与 JNI 集成并使用 SWIG 为其生成包装器,以便它可以被 Java 调用...(我没有 JNI 或 SWIG 经验,如果我错了,请纠正我)。 .
  • 我想这对我来说是一个很大的解决方法,因为我试图从 C++ 调用 dll(在 C++ 上具有 JNI)......你觉得呢?
  • 入口点名称看起来是自动生成的,所以我认为某处可能有一个匹配的生成头文件。除了我概述的方法之外,我不知道您还能做什么。
  • 另一种想法,您可以尝试在 DLL 上运行 winedump,尤其是“生成骨架代码”选项,也许它可以生成一些有用的东西:linux.die.net/man/1/winedump
  • 感谢 Codebender 提供的 winedump 提示...我目前没有任何可用的 Linux 发行版,但当我得到一个时,我一定会尝试使用 winedump...跨度>
猜你喜欢
  • 2019-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多