Java DLL问题和无效内存访问
这就是我的情况。我的任务是将部分程序从用DelphiIDE编写的pascal移植到Java程序,至少我相信是这样。我没有参与正在移植的程序的创建,所以除了观看它作为最终产品所做的工作之外,我几乎不知道它的内部工作。尽管如此,我已经开始将其传输过来,并在他们的.dll调用中遇到了障碍 它们的.dll调用如下所示:Java DLL问题和无效内存访问,java,delphi,dll,jna,Java,Delphi,Dll,Jna,这就是我的情况。我的任务是将部分程序从用DelphiIDE编写的pascal移植到Java程序,至少我相信是这样。我没有参与正在移植的程序的创建,所以除了观看它作为最终产品所做的工作之外,我几乎不知道它的内部工作。尽管如此,我已经开始将其传输过来,并在他们的.dll调用中遇到了障碍 它们的.dll调用如下所示: unit impdll; interface uses Classes, Sysutils; type TChar80 = array[0..80] of char;
unit impdll;
interface
uses
Classes, Sysutils;
type
TChar80 = array[0..80] of char;
procedure GetElev (var a,b:double; var Elevate,ErrCode:LongInt); stdcall;
procedure ClxGen (var errCode:LongInt); stdcall;
procedure RunGen (var ErrCode:Integer;var XFileName:Tchar80;XLen: integer); stdcall;
implementation
procedure GetElev (var a,b:double; var Elevate,ErrCode:LongInt); stdcall; external 'Getelev.dll' name 'GETELEV';
procedure ClxGen (var errCode:LongInt); stdcall; external 'ClxGen.dll' name 'CLXGEN';
procedure RunGen (var ErrCode:Integer;var XFileName:Tchar80;XLen: integer); stdcall; external 'Rungen.dll' name '_rungen@12';
end.
我不知道他们的.dll做什么,甚至不知道它们作为参数输入或输出,也不知道它们是用什么代码编写的。尽管它们的名称的性质以及知道程序的这一部分是做什么的,但我可以猜到,三个.dll文件中的第一个文件基于lat-long位置a和b获得高程,并将其返回到double,将高程返回到longint。
在getelev.dll文件上使用“dumpbin.exe/EXPORTS”后,它将此作为唯一的函数名,根据它们的代码和对.dll的调用,这是有意义的:
ordinal hint RVA name
1 0 0000100 GETELEV
通过研究,我发现名称后面有一个@#“
,意味着有一定数量的字节作为参数传递,所以我假设这意味着它们没有参数。我的推理错了吗
还有,这里有没有确定返回类型的方法?我假设CLXGEN
没有返回类型,仅仅是因为使用的是过程而不是函数,或者过程通常是在接口中的函数上使用的
无论如何,通过使用JNA,我能够部分地将.dll库转换成Java。我的代码如下所示:
public class MainProgram {
public interface LibraryCalls extends Library {
LibraryCalls INSTANCE = (LibraryCalls) Native.loadLibrary("getelev",LibraryCalls.class);
public void GETELEV(double a, double b, int Elevate, int ErrCode);
}
public static void main(String[] args)
{
System.loadLibrary("getelev");
LibraryCalls newCall = LibraryCalls.INSTANCE;
newCall.GETELEV(24.726348,-81.051194,0,0);
}
}
这就是我的麻烦开始的地方。我得到一个无效的内存访问,如下所示:
Exception in thread "main" java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokeVoid(Native Method)
at com.sun.jna.Function.invoke(Function.java:408)
at com.sun.jna.Function.invoke(Function.java:354)
at com.sun.jna.Library$Handler.invoke(Library.java:244)
at com.sun.proxy.$Proxy0.GETELEV(Unknown Source)
at markSimTest.MainProgram.main(MainProgram.java:22)
在研究了这种无效的内存访问之后,我发现很多人都在谈论映射,而映射是否正确可能会影响这一点,所以我想知道这是否是我的问题?如果不是这样的话,那么JNA可能不适用于.dll类型。这只是因为它是用未知语言编写的,如果是这样的话,我猜它是一个pascal.dll,如果可能的话。正如您所知,我对.dll没有太多的知识
同样在同一个主题中,我很难找到如果.dll是一个void函数且不带参数,它将如何返回lat、long或elevation。这个过程是否做了一些特殊的事情来从.dll中获取这些数字
任何帮助,即使是部分帮助,都将不胜感激
解决方案编辑:
我现在看到的是一个相当简单的解决方案,但当时我看不到这样的解决方案,因为我在这个问题上没有足够的知识
以下是最新的Java代码:
public interface LibraryCalls extends StdCallLibrary{
LibraryCalls INSTANCE = (LibraryCalls) Native.loadLibrary("getelev",LibraryCalls.class);
public void GETELEV(Pointer a, Pointer b, Pointer Elevate, Pointer ErrCode);
}
public static void main(String[] args)
{
Pointer lat = new Memory(Native.getNativeSize(Double.TYPE));
lat.setDouble(0, 34.769823);
Pointer lon = new Memory(Native.getNativeSize(Double.TYPE));
lon.setDouble(0, -87.654147);
Pointer elevate = new Memory(Native.getNativeSize(Integer.TYPE));
elevate.setInt(0, 0);
Pointer errCode = new Memory(Native.getNativeSize(Integer.TYPE));
errCode.setInt(0, 0);
LibraryCalls newCall = LibraryCalls.INSTANCE;
newCall.GETELEV(lat,lon,elevate,ErrCode);
}
GetElev
接受4个参数,它们都是指针var
用于指示参数是通过引用传递的。所以a
、b
、Elevate
和ErrCode
都是输出。没有@
的事实并不意味着很多。声明显示GetElev
接受四个参数。在C中,声明是:void\u stdcall GetElev(double*a,double*b,int*elevate,int*errcode)代码>。其他声明类似。正如@JimRhodes所说:var
意味着参数通过引用传递。似乎只有XLen
通过值传递。对不起,我不知道如何将它翻译成Java。DLL似乎将函数导出为全大写:GETELEV
,等等。这很容易翻译。阅读Delphi文档。这不是秘密。GetElev有4个参数。他们在那里。天气晴朗。不要费心尝试从导入名称中反转它。阅读代码。您需要使用StdCallLibrary
标记界面来指示您的DLL调用约定(“stdcall”)