如何从C ++调用JNI DLL

时间:2009-08-12 04:29:06

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

我有一项任务是使用C ++与第三方公司的dll连接。

dll包附带:

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

当我被要求使用C ++时,我的另一位同事正在使用Java(基于包中的示例)与dll进行交互。 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 Server :: StringArray

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

  • std :: map // KeyValueMap - del,empty,get,has_1key,set
  • std :: vector // StringArray - add,capacity,clear,get,isEMPTY,reserve,set,size

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

2 个答案:

答案 0 :(得分:1)

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

使用上面的示例,您还缺少几个重要的信息 - 您描述的类型(Server :: StringArray和Server :: KeyValueMap)是标准库容器。好的,但是什么的标准库容器?这些容器是模板,除非你知道这些模板已经实例化的确切类型,否则你有点卡住了。

这个DLL是否打算从C ++中调用?它导出JNI接口这一事实表明它可能不是第一位的。它是否导出除了_Java _...?

格式之外的任何其他公共符号

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

答案 1 :(得分:1)

我不太明白这里使用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 ++数据类型,所以我不知道我的经验是多么相关。希望这至少是一些帮助。