快速总结:
我的 IKVM 编译的 JAR-to-DLL 库与 .NET 项目中的一些方法和类一起使用,但一个特定的库抛出了一个异常,这似乎表明我的 IKVM.Runtime.JNI 正在尝试将 64 位地址存储到 32 位变量,当我希望整个系统为 32 位时。这会导致抛出 IllegalArgumentException。
问题
我有一个需要集成到 .NET 项目中的多个 JAR 文件形式的 JAVA API。JAR 文件根据 CLASSPATH 中的目录搜索 API 的许可证和配置文件。JAR 文件还使用 JNI 调用对 java.library.path 中包含的 DLL 进行调用。
.NET 项目有一个 x86 目标。JNI 包含的 DLL 有 win64 和 win32 两个版本,所以我使用 win32 目录中的那些。
这一切都发生在 VS2010 中,使用 .NET Framework 4。操作系统是 64 位 Windows 7。
计划
好的,所以计划是使用 IKVM 编译器将 JAR 文件编译为 DLL。然后,将这些 DLL 与 IKVM 库一起包含在 .NET 项目中,并能够以这种方式与 JAVA API 交互。
汇编
首先,我编译 log4j jar:
~>ikvmc -target:library log4j-1.2.14.jar
IKVM.NET Compiler version 7.3.4830.0
Copyright (C) 2002-2013 Jeroen Frijters
http://www.ikvm.net/
note IKVMC0002: Output file is "log4j-1.2.14.dll"
warning IKVMC0100: Class "javax.jms.MessageListener" not found
...
大约有 20 个左右的警告处理 javax.jms.* 和 javax.mail.*,但它似乎编译得很好。
然后,我把剩下的都拉进去了。
~>ikvmc -platform:x86 -target:library -classloader:ikvm.runtime.ClassPathAssemblyClassLoader -r:log4j-1.2.14 { commons-io-1.4.jar } { commons-lang3-3.1.jar } { EngineAPI_PC-1.0.13.jar } { EngineAPI_PC-api-1.0.13.jar } { guice_no_aop-3.0.jar } { guice-assistedinject-3.0.jar } { inject-330.jar } { IVectorsMultiSpeaker4GResources-1.0.1.jar } { IVectorsResources-1.0.6.jar } { slf4j-api-1.7.2.jar } { slf4j-log4j12-1.7.2.jar }
IKVM.NET Compiler version 7.3.4830.0
Copyright (C) 2002-2013 Jeroen Frijters
http://www.ikvm.net/
warning IKVMC0126: Found assembly "log4j-1.2.14" using legacy search rule, pleas
e append '.dll' to the reference
note IKVMC0002: Output file is "commons-io-1.4.dll"
note IKVMC0002: Output file is "commons-lang3-3.1.dll"
note IKVMC0002: Output file is "EngineAPI_PC-1.0.13.dll"
note IKVMC0002: Output file is "EngineAPI_PC-api-1.0.13.dll"
note IKVMC0002: Output file is "guice_no_aop-3.0.dll"
note IKVMC0002: Output file is "guice-assistedinject-3.0.dll"
note IKVMC0002: Output file is "inject-330.dll"
note IKVMC0002: Output file is "IVectorsMultiSpeaker4GResources-1.0.1.dll"
note IKVMC0002: Output file is "IVectorsResources-1.0.6.dll"
note IKVMC0002: Output file is "slf4j-api-1.7.2.dll"
note IKVMC0002: Output file is "slf4j-log4j12-1.7.2.dll"
warning IKVMC0112: Emitted java.lang.IllegalAccessError in "es.agnitio.core3.Voi
ceSampleImpl.getIdentifiableData()Ljava.util.List;"
("Try to access class es.agnitio.core3.a from class es.agnitio.core3.VoiceSa
mpleImpl")
(in EngineAPI_PC-1.0.13.dll)
warning IKVMC0112: Emitted java.lang.IllegalAccessError in "es.agnitio.core3.Voi
ceSampleImpl.getIdentifiableData()Ljava.util.List;"
("Try to access method es.agnitio.core3.a.<init>(Les.agnitio.core3.VoiceSamp
leImpl;Les.agnitio.core3.IdentifiableData;)V from class es.agnitio.core3.VoiceSa
mpleImpl")
(in EngineAPI_PC-1.0.13.dll)
如您所见,我看到 -platform:x86 以确保 32 位 DLL。使用 -classloader:... 是以后能够编辑 CLASSPATH 所必需的,包括包含配置和许可证文件的目录。我引用了 log4j DLL 并列出了剩余的 JAR。在此之后,我有每个文件的 DLL。
设置项目
在 .NET 项目中,我参考:
- IKVM/bin 目录下的所有 DLL,除了 ikvm-native-*.dll
- IKVM/bin-x86 中的 JVM.DLL
- 上一步中编译的 DLL。
我将 ikvm-native-win32-x86.dll 作为要包含在项目构建中的链接添加到项目中。我没有将任何 IKVM EXE 添加到项目中。我还应该注意,尽管能够进行 JNI 调用,但项目调试输出从未表明它使用 JVM.DLL 或 ikvm-native-win32-x86.dll。
我的项目有一个 App.config,其中设置了 ikvm:java.library.path 和 ikvm:java.class.path:
<appSettings>
<add key="ikvm:java.library.path" value="C:\path\to\DLLs\for\JNI;" />
<add key="ikvm:java.class.path" value="C:\path\to\config;C:\path\to\license;" />
</appSettings>
App.config 还具有所有 IKVM 库的程序集绑定。
编译和运行项目
我包括 java.io 和 es.agnitio.* 命名空间。我能够使用 JAVA 项目中的方法和类并成功编译。该库还成功地找到了配置、许可证和 JNI DLL 文件。
当我运行应用程序时,在加载 JAVA 库时,对 JAVA 函数的第一次调用会暂停一段时间。此时,我得到以下输出:
[17:42:38.76691 ] loadLibrary: C:\path\to\DLLs\for\JNI\predj.dll, class loader: ikvm.runtime.ClassPathAssemblyClassLoader@3EDD7A7
[17:42:38.80491 ] Library loaded: C:\path\to\DLLs\for\JNI\predj.dll, handle = 0xF800000
对于所有 JNI 库,我看到本地方法已成功链接。在此之后,几个 Agnitio JAVA API 类和方法按预期工作,我能够执行 JAVA 文件 IO。但是,一个特定的方法给出了这个例外:
*** exception in native code ***
java.lang.IllegalArgumentException: Can not set long field es.agnitio.data.Nativ
eMemoryJNI.ptrAddress to es.agnitio.core.ArrayForNativeCode
System.Collections.ListDictionaryInternal
Can not set long field es.agnitio.data.NativeMemoryJNI.ptrAddress to es.agnitio.
core.ArrayForNativeCode
at __<Setter>(IReflectionException , Object , Int64 , Object )
at IKVM.NativeCode.sun.reflect.ReflectionFactory.FieldAccessorImplBase.FieldA
ccessor`1.lazySet(Object obj, T value)
at IKVM.NativeCode.sun.reflect.ReflectionFactory.FieldAccessorImplBase.FieldA
ccessor`1.lazySet(Object obj, T value, FieldAccessor`1 acc)
at IKVM.NativeCode.sun.reflect.ReflectionFactory.FieldAccessorImplBase.LongFi
eld.setLong(Object obj, Int64 value)
at IKVM.Runtime.JNIEnv.SetLongField(JNIEnv* pEnv, IntPtr obj, IntPtr fieldID,
Int64 val)
at es.agnitio.ivectors.IVectorsNative.updateStreaming4GSessionS(ArrayForNativ
eCode afnc1, ArrayForNativeCode afnc2, ArrayForNativeCode afnc3)
at es.agnitio.ivectors.h.b(FeaturesNoJNA fnjna)
at es.agnitio.core3.internal.d.a(FrontEndResult fer, Int32 i1, Int32 i2)
at es.agnitio.core3.internal.d.a(Int32 i1, Int32 i2, List l)
at es.agnitio.core3.internal.b.extractVoiceSamples(Int32 i, List l)
at es.agnitio.core3.internal.b.extractVoiceSamples(Int32 i)
at es.agnitio.core3.internal.b.extractVoiceSample(List l)
at es.agnitio.modeling.ModelFactoryAbstract.a(List , List , List )
at es.agnitio.modeling.ModelFactoryAbstract.trainModelFromAudio(AudioStandard
as, List l)
at es.agnitio.modeling.ModelFactoryAbstract.trainModelFromAudio(AudioStandard
as)
at BS3Test.Program.Main(String[] args) in C:\path\to\source\Program.cs:line 38
我的想法
看起来 Agnitio 库将某个对象的地址存储在 32 位变量中,但 IKVM 库方法将其转换为 64 位地址,因此出现 IllegalArgumentException。网上很多文档都说 ikvm-native-*.dll 确定 JNI 调用是作为 32 位还是 64 位进行的,但似乎没有涉及到我的 JVM.dll 或 ikvm-native-win32-x86.dll在这个过程中。有关如何解决此问题(或集成此 JAVA 库的更好方法)的任何想法?
编辑:我最初的印象是不正确的。ikvm-native-win32-x86.dll 和 JVM.dll 以 32 位版本成功包含在项目中。现在,我对错误消息所指示的内容有些茫然。