我正在使用JNA访问本机库。这是我第一次使用JNA,我也没有使用c / c ++的经验。我能够使应用程序工作,并使用System.out.println()
返回并显示正确的结果。这是我的代码的最后一行。该值显示在控制台中,然后Java崩溃,例如。将显示对话框Java Platform SE binary has stopped working
,其中包含关闭或调试选项。
然后控制台会显示:
Java Result: -1073741819
Windows事件查看器说0xc0000005根据现有答案是我
编辑5 2018年8月21日:
这仍然没有解决,现在我再次调查。解决的是循环时MemoryError的问题。因为我现在真的需要一个解决方案,所以我也停止混淆dll。 AFAIK供应商将其停业。他们不回复问题,网站已过时。
我的“文档”来自here
这里是当前的java代码:
public interface CLogP extends StdCallLibrary {
CLogP INSTANCE = (CLogP) Native.loadLibrary("BB-CLOGP", CLogP.class);
NativeLong calcLogP(String smiles, FloatByReference logP, NativeLongByReference numContrb, HANDLEByReference contrib);
}
public static void main(String[] args) {
//contrib can also be a PointerByReference, the behaviour is the same
HANDLEByReference contrib = new HANDLEByReference();
FloatByReference cLogP = new FloatByReference();
NativeLongByReference numContrib = new NativeLongByReference();
NativeLong err = CLogP.INSTANCE.calcLogP("c1ccccc1", cLogP, numContrib, contrib);
System.out.println(err.intValue());
System.out.println(cLogP.getValue());
System.out.println(numContrib.getValue());
//needs to be done after ever call to calcLogP
//then no issue running in a loop
Kernel32.INSTANCE.GlobalFree(contrib.getValue().getPointer());
// Tried to free other variables, does not have any effect
//Kernel32.INSTANCE.GlobalFree(cLogP.getPointer());
//Kernel32.INSTANCE.GlobalFree(numContrib.getPointer());
}
我有点迷茫。上面的内容适用于dll的过时版本,但不适用于最新版本。出了什么问题?拆解它会有什么帮助吗?
旧代码和信息,现在只是部分相关:
我的代码:
public static void main(String[] args) {
FloatByReference result = new FloatByReference();
//EDIT: changed to NativeLongByReference as per suggested answer
NativeLongByReference numContrib = new NativeLongByReference();
// This is a struct that needs to be passed by reference
MyDll.PContribution.ByReference contrib = new MyDll.PContribution.ByReference();
NativeLong err = MyDll.INSTANCE.calcResult("myValue", result, numContrib, contrib);
// I only care about result and not the other out-parameters
System.out.println(result.getValue());
//crash here
}
c中函数的定义:
typedef long (CALCRESULT)(const char*, float*, long*, HANDLE*);
出了什么问题?在应用程序终止之前,我是否需要执行一些清理工作?
编辑:
我可以在循环中运行方法调用并且它可以工作。只有在它终止时它才会崩溃。
EDIT2:
根据评论此处代码为MyDll:
public interface MyDll extends Library {
public static class PContribution extends Structure {
public static class ByReference extends PContribution implements Structure.ByReference {
public byte[] Class = new byte[10];
public byte[] Type = new byte[6];
public byte[] Description = new byte[40];
public byte[] Comment = new byte[10];
public float Value;
}
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "Class", "Type", "Description", "Comment", "Value" });
}
}
MyDll INSTANCE = (MyDll) Native.loadLibrary("MyDll", MyDll.class);
NativeLong calcResult(String smi, FloatByReference result, NativeLongByReference numContrb, PContribution contrib);
}
struct
:
typedef struct {
char Class[10];
char Type[6];
char Description[40];
char Comment[10];
float Value;
} PContribution;
编辑3:
该死。我发现可用的文档是针对旧版本的dll。使用旧的DLL,一切正常。所以现在我需要从供应商那里获得新版本的文档。
编辑4:
它适用于旧的dll,但应用程序在65533次迭代(调用)后一直崩溃。每个调用都使用完全相同的参数。
java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokeInt(Native Method)
at com.sun.jna.Function.invoke(Function.java:390)
at com.sun.jna.Function.invoke(Function.java:323)
at com.sun.jna.Library$Handler.invoke(Library.java:236)
at com.sun.proxy.$Proxy0.calcLogP(Unknown Source)
练习的重点是能够快速拨打数千个电话。
答案 0 :(得分:1)
您获得的Java结果是错误消息。 -1073741819是0xc0000005,这是STATUS_ACCESS_VIOLATION的代码:您正在访问您没有权限的内存。
当您的应用程序关闭时发生这一事实表明它与对象终结相关联,这在程序终止时发生,也可能在触发垃圾收集时发生。 Java / JNA可能正在尝试释放C尚未分配的内存。
当你遇到这种错误时,你应该调查:
类型映射
您的代码与c函数定义的比较显示两个不匹配。
CALCRESULT函数有一个指向C中long
的指针。这是一个32位还是64位长是operating system dependent而Java是long
总是64位。您应该将原始long
值映射到JNA的NativeLong类型。虽然在这种情况下这可能不会导致崩溃,但是当值在数组或结构中时,知道这种区别是非常重要的,因为它会抛弃字节偏移。作为指向原生long
的指针,您的变量numContrib
应为NativeLongByReference
CALCRESULT函数在C中有一个指向HANDLE
类型的指针,但是你传递了一个结构(通过引用)。这里有几点说明:
Pointer
,使用指针创建一个新的结构。可能是同样的事情,但它对我来说更清楚发生了什么。 )HANDLE
的指针。 Handle是对内部维护的表的引用,该表可以访问其他系统资源等。当您使用它们时,应该释放句柄,或者占用这些资源。 JNA模拟HANDLE
类型;在这种情况下,你有一个指针,所以适当的类型可能是HANDLEByReference。 内存分配不匹配
当API为您提供指向HANDLE
类型(JNA HANDLEByReference
)的指针时,不清楚您使用结构的原因。当您定义结构的new
实例(在您的情况下为PContribution
)时,Java会分配必要的内存,JNA会将该Java内存映射到本地内存。但是,C函数给出一个指向HANDLE
类型的指针,它占用的内存比你定义的结构少。完全有可能当Java在Java端释放结构内存然后尝试在C端释放相同的内存时,它会遇到问题,因为它告诉C到释放更多内存(用于结构)而不是C分配(用于HANDLE)。如果没有来自您的API的更清晰的文档,关于如何从此HANDLE到您在其所在位置列出的结构,则无法进一步回答这个问题。
发布资源
由于API使用HANDLE
类型,因此可能涉及除内存之外的其他资源,并且您有责任发布这些引用。请参阅CloseHandle()功能。是否必须使用此函数自行关闭句柄,或者API是否将使用自己的方法(实现CloseHandle()
内部函数)为您关闭它应该在API中明确记录(在本例中为CALCRESULT函数) 。如果没有要检查的API,我可以提供更多帮助,但请仔细阅读文档,看看是否需要明确释放/释放您在代码中创建的任何结构,以及如何。