2021-03-30 19:59发布
工作中需要用java调用dll,并获取信息
代码如下:
1 function JavaCall(InS, OutS: PChar): Integer; stdcall; 2 var 3 s: string; 4 begin 5 ShowMessage('您传入的内容:'+InS); 6 s := 'DLL返回内容'; 7 OutS := PChar(s); 8 Result := 999; 9 end;
public interface CLibrary extends Library { CLibrary INSTANCE = (CLibrary) Native.loadLibrary("testdll", CLibrary.class); // 引入库文件 public int JavaCall(String s1, byte[] s2); } public static void main(String[] args) { byte[] aaa = new byte[100]; System.out.println("java call dll result = "+CLibrary.INSTANCE.JavaCall("ttttt", aaa)); System.out.println("aaa="+new String(aaa)); }
和许多解释执行的语言一样,Java提供了调用原生函数的机制,以加强Java平台的能力。Java™ Native Interface (JNI)就是Java调用原生函数的机制。
事实上,很多Java核心代码内部就是使用JNI实现的。这些Java功能实际上是通过原生函数提供的。
但是,使用JNI对Java开发者来说简直是一场噩梦。
如果你已经有了原生函数,使用JNI,你必须使用C语言再编写一个动态链接库,这个动态链接库的唯一功能就是使用Java能够理解的C代码来调用目标原生函数。
这个没什么实际用途的动态链接库的编写过程令人沮丧。同时编写Java和C代码使开发难度大大增加。
因此,在Java开发社区中,人们一直都视JNI为禁地,轻易不愿涉足。
缺少原生函数的协助使Java的使用范围大大缩小。
反观.NET阵营,其P/Invoke技术调用原生函数非常方便,不需要编写一行C代码,只需要写Annotation就可以快速调用原生函数。因此,与硬件有关的很多开发领域都被.NET所占据。
JNA(Java NativeAccess)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。
JNA项目地址:https://jna.dev.java.net/
JNA使Java调用原生函数就像.NET上的P/Invoke一样方便、快捷。
JNA的功能和P/Invoke类似,但编写方法与P/Invoke截然不同。JNA没有使用Annotation,而是通过编写一般的Java代码来实现。
P/Invoke是.NET平台的机制。而JNA是Java平台上的一个开源类库,和其他类库没有什么区别。只需要在classpath下加入jna.jar包,就可以使用JNA。
JNA使Java平台可以方便地调用原生函数,这大大扩展了Java平台的整合能力。
JNI 是Java调用原生函数唯一的机制。JNA也是建立在JNI技术之上的。它简化了Java调用原生函数的过程。
JNA提供了一个动态的C语言编写的转发器,可以自动实现Java和C的数据类型映射。你不再需要编写那个烦人的C动态链接库。
当然,这也意味着,使用JNA技术比使用JNI技术调用动态链接库会有些微的性能损失。可能速度会降低几倍。但对于绝大部分项目来说,影响不大。
让我们先看一个JNA调用原生函数的例子。
假设我们有一个动态链接库,发布了这样一个C函数:
void say(wchar_t* pValue){
std::wcout.imbue(std::locale("chs"));
std::wcout<}它需要传入一个Unicode编码的字符数组。然后在控制台上打印出一段中文字符。 为了调用这个原生函数,使用JNA,我们需要编写这样的Java代码: publicinterface TestDll1 extends Library { TestDll1 INSTANCE =(TestDll1)Native.loadLibrary("TestDll1", TestDll1.class); public void say(WString value);}这里,如果动态链接库是以stdcall方式输出函数,那么就继承StdCallLibrary。然后就可以像普通的Java程序那样调用这个接口:public static void main(String[] args) { TestDll1.INSTANCE.say(newWString("Hello World!")); System.out.println("Java输出。"); }执行,可以看到控制台下如下输出:原生函数说:Hello World!Java输出。调用原生函数的模式JNA不使用native关键字。JNI使用native关键字,使用一个个Java方法来代表外部的原生函数。而JNA使用一个Java接口来代表一个动态链接库发布的所有函数。对于不需要的原生函数,你可以不在Java接口中声明Java方法原型。如果使用JNI,你需要使用System.loadLibrary方法,把我们专为JNI编写的动态链接库载入进来。这个动态链接库实际上是我们真正需要的动态链接库的代理。上例中使用JNA类库的Native类的loadLibrary方法 ,是直接把我们需要的动态链接库载入进来。使用JNA,我们不需要编写作为代理的动态链接库,不需要编写一行原生代码。 上面的JNA代码使用了单例,接口的静态变量返回的是接口的唯一实例,这个Java对象是JNA通过反射动态创建的。通过这个对象,我们可以调用动态链接库发布的函数。和原生代码的类型映射 跨平台、跨语言调用的最大难点,就是不同语言之间数据类型不一致造成的问题。绝大部分跨平台调用的失败,都是这个问题造成的。JNA使用的数据类型是Java的数据类型。而原生函数中使用的数据类型是原生函数的编程语言使用的数据类型。可能是C,Delphi,汇编等语言的数据类型。因此,不一致是在所难免的。JNA提供了Java和原生代码的类型映射。和操作系统数据类型的对应表Java 类型C 类型原生表现booleanint32位整数 (可定制)bytechar8位整数charwchar_t平台依赖shortshort16位整数intint32位整数longlong long, __int6464位整数floatfloat32位浮点数doubledouble64位浮点数BufferPointerpointer平台依赖(32或 64位指针)[] (基本类型的数组)pointerarray32或 64位指针(参数/返回值)邻接内存(结构体成员)支持常见的数据类型的映射Java 类型C 类型原生表现Stringchar*/0结束的数组 (native encoding or jna.encoding)WStringwchar_t*/0结束的数组(unicode)String[]char**/0结束的数组的数组WString[]wchar_t**/0结束的宽字符数组的数组Structurestruct*struct指向结构体的指针 (参数或返回值) (或者明确指定是结构体指针)结构体(结构体的成员) (或者明确指定是结构体)Unionunion等同于结构体Structure[]struct[]结构体的数组,邻接内存Callback (*fp)()Java函数指针或原生函数指针NativeMappedvaries依赖于定义NativeLonglong平台依赖(32或64位整数)PointerTypepointer和 Pointer相同:尽量使用基本、简单的数据类型;尽量少跨平台、跨语言传递数据!如果有复杂的数据类型需要在Java和原生函数中传递,那么我们就必须在Java中模拟大量复杂的原生类型。这将大大增加实现的难度,甚至无法实现。如果在Java和原生函数间存在大量的数据传递,那么一方面,性能会有很大的损失。更为重要的是,Java调用原生函数时,会把数据固定在内存中,这样原生函数才可以访问这些Java数据。这些数据,JVM的GC不能管理,会造成内存碎片。如果在你需要调用的动态链接库中,有复杂的数据类型和庞大的跨平台数据传递。那么你应该另外写一些原生函数,把需要传递的数据类型简化,把需要传递的数据量简化。模拟结构体 在原生代码中,结构体是经常使用的复杂数据类型。这里我们研究一下怎样使用JNA模拟结构体。使用JNA调用使用Struct的C函数假设我们现在有这样一个C语言结构体struct UserStruct{ long id; wchar_t* name; int age;};使用上述结构体的函数#define MYLIBAPI extern "C" __declspec( dllexport ) MYLIBAPI void sayUser(UserStruct* pUserStruct);对应的Java程序中,在例1的接口中添加下列代码: public static classUserStruct extendsStructure{ publicNativeLong id; publicWString name; public int age;public static class ByReferenceextends UserStruct implements Structure.ByReference { }public static class ByValue extends UserStruct implements Structure.ByValue { } } public void sayUser(UserStruct.ByReference struct);Java中的调用代码:UserStruct userStruct=new UserStruct (); userStruct.id=newNativeLong(100); userStruct.age=30; userStruct.name=newWString("奥巴马"); TestDll1.INSTANCE.sayUser(userStruct);说明现在,我们就在Java中实现了对C语言的结构体的模拟。这里,我们继承了Structure类,用这个类来模拟C语言的结构体。必须注意,Structure子类中的公共字段的顺序,必须与C语言中的结构的顺序一致。否则会报错!因为,Java调用动态链接库中的C函数,实际上就是一段内存作为函数的参数传递给C函数。动态链接库以为这个参数就是C语言传过来的参数。同时,C语言的结构体是一个严格的规范,它定义了内存的次序。因此,JNA中模拟的结构体的变量顺序绝对不能错。如果一个Struct有2个int变量。 Int a, int b 如果JNA中的次序和C中的次序相反,那么不会报错,但是数据将会被传递到错误的字段中去。 Structure类代表了一个原生结构体。当Structure对象作为一个函数的参数或者返回值传递时,它代表结构体指针。当它被用在另一个结构体内部作为一个字段时,它代表结构体本身。 另外,Structure类有两个内部接口Structure.ByReference和Structure.ByValue。这两个接口仅仅是标记,如果一个类实现Structure.ByReference接口,就表示这个类代表结构体指针。如果一个类实现Structure.ByValue接口,就表示这个类代表结构体本身。 使用这两个接口的实现类,可以明确定义我们的Structure实例表示的是结构体的指针还是结构体本身。 上面的例子中,由于Structure实例作为函数的参数使用,因此是结构体指针。所以这里直接使用了UserStruct userStruct=new UserStruct ();也可以使用UserStruct userStruct=new UserStruct.ByReference ();明确指出userStruct对象是结构体指针而不是结构体本身。模拟复杂结构体 C语言最主要的数据类型就是结构体。结构体可以内部可以嵌套结构体,这使它可以模拟任何类型的对象。 JNA也可以模拟这类复杂的结构体。结构体内部可以包含结构体对象的数组structCompanyStruct{ long id; wchar_t* name; UserStruct users[100]; int count;};JNA中可以这样模拟:public static classCompanyStruct extendsStructure{ public NativeLong id; public WString name; public UserStruct.ByValue[] users=new UserStruct.ByValue[100]; public int count;} 这里,必须给users字段赋值,否则不会分配100个UserStruct结构体的内存,这样JNA中的内存大小和原生代码中结构体的内存大小不一致,调用就会失败。结构体内部可以包含结构体对象的指针的数组structCompanyStruct2{ long id; wchar_t* name; UserStruct* users[100]; int count;};JNA中可以这样模拟:public static classCompanyStruct2 extendsStructure{ public NativeLong id; public WString name; public UserStruct.ByReference[] users=newUserStruct.ByReference[100]; public int count;}测试代码:CompanyStruct2.ByReference companyStruct2=new CompanyStruct2.ByReference(); companyStruct2.id=new NativeLong(2); companyStruct2.name=new WString("Yahoo"); companyStruct2.count=10; UserStruct.ByReference pUserStruct=new UserStruct.ByReference(); pUserStruct.id=new NativeLong(90); pUserStruct.age=99; pUserStruct.name=new WString("杨致远");// pUserStruct.write(); for(inti=0;icount;i++){ companyStruct2.users[i]=pUserStruct; } TestDll1.INSTANCE.sayCompany2(companyStruct2);执行测试代码,报错了。这是怎么回事?考察JNI技术,我们发现Java调用原生函数时,会把传递给原生函数的Java数据固定在内存中,这样原生函数才可以访问这些Java数据。对于没有固定住的Java对象,GC可以删除它,也可以移动它在内存中的位置,以使堆上的内存连续。如果 原生函数访问没有被固定住的Java对象,就会导致调用失败。固定住哪些java对象,是JVM根据原生函数调用自动判断的。而上面的CompanyStruct2结构体中的一个字段是UserStruct对象指针的数组,因此,JVM在执行时只是固定住了CompanyStruct2对象的内存,而没有固定住users字段引用的UserStruct数组。因此,造成了错误。我们需要把users字段引用的UserStruct数组的所有成员也全部固定住,禁止GC移动或者删除。如果我们执行了pUserStruct.write();这段代码,那么就可以成功执行上述代码。Structure类的write()方法会把结构体的所有字段固定住,使原生函数可以访问。代码 JNI技术是双向的,既可以从Java代码中调用原生函数,也可以从原生函数中直接创建Java虚拟机,并调用Java代码。 但是,这样做要写大量C代码,对于广大Java程序员来说是很头疼的。 使用JNA,我们就可以不写一行C代码,照样实现原生代码调用Java代码!JNA可以模拟函数指针,通过函数指针,就可以实现在原生代码中调用Java函数。让我们先看一个模拟函数指针的JNA例子: 通过回调函数实现原生代码调用Java代码intgetValue(int (*fp)(intleft,int right),intleft,int right){ returnfp(left,right);} C函数中通过函数指针调用外部传入的函数,执行任务。JNA中这样模拟函数指针:public static interfaceFp extendsCallback { int invoke(int left,int right);} C函数用如下Java方法声明代表:public int getValue(Fp fp,int left,int right); 现在,我们有了代表函数指针int(*fp)(int left,intright)的接口Fp,但是还没有Fp的实现类。public static classFpAdd implementsFp{ @Override public intinvoke(intleft, intright) { return left+right; }}回调函数说明 原生函数可以通过函数指针实现函数回调,调用外部函数来执行任务。这就是策略模式。 JNA可以方便地模拟函数指针,把Java函数作为函数指针传递给原生函数,实现在原生代码中调用Java代码。 模拟指针 JNA可以模拟原生代码中的指针。Java和原生代码的类型映射表中的指针映射是这样的:Java 类型C 类型原生表现BufferPointerpointer平台依赖(32或 64位指针)[] (基本类型的数组)pointerarray32或 64位指针(参数/返回值)邻接内存(结构体成员)PointerTypepointer和 Pointer相同 原生代码中的数组,可以使用JNA中对应类型的数组来表示。 原生代码中的指针,可以使用Pointer类型,或者PointerType类型及它们的子类型来模拟。Pointer代表原生代码中的指针。其属性peer就是原生代码中指针的地址。我们不可以直接创建Pointer对象,但可以用它表示原生函数中的任何指针。Pointer类有2个子类:Function, Memory。 Function类代表原生函数的指针,可以通过invoke(Class,Object[],Map)这一系列的方法调用原生函数。 Memory类代表的是堆中的一段内存,它也是我们可以创建的Pointer子类。创建一个Memory类的实例,就是在原生代码的内存区中分配一块指定大小的内存。这块内存会在GC释放这个Java对象时被释放。Memory类在指针模拟中会被经常用到。 PointerType类代表的是一个类型安全的指针。ByReference类是PointerType类的子类。ByReference类代表指向堆内存的指针。ByReference类非常简单。public abstract class ByReference extends PointerType { protected ByReference(int dataSize) { setPointer(new Memory(dataSize)); }} ByReference类有很多子类,这些类都非常有用。 ByteByReference, DoubleByReference,FloatByReference, IntByReference, LongByReference, NativeLongByReference,PointerByReference, ShortByReference, W32API.HANDLEByReference,X11.AtomByReference, X11.WindowByReference ByteByReference等类故名思议,就是指向原生代码中的字节数据的指针。 PointerByReference类表示指向指针的指针。 在JNA中模拟指针,最常用到的就是Pointer类和PointerByReference类。Pointer类代表指向任何东西的指针,PointerByReference类表示指向指针的指针。Pointer类更加通用,事实上PointerByReference类内部也持有Pointer类的实例。 PointerByReference类可以嵌套使用,它所指向的指针,本身可能也是指向指针的指针。PointerByReference类的源代码:public class PointerByReference extends ByReference { public PointerByReference() { this(null); } public PointerByReference(Pointervalue) { super(Pointer.SIZE); setValue(value); } public void setValue(Pointer value) { getPointer().setPointer(0,value); } public Pointer getValue() { returngetPointer().getPointer(0); }} 可以看到,PointerByReference类的构造器做了如下工作:1, 首先在堆中分配一个指针大小的内存,并用一个Pointer对象代表。PointerByReference类的实例持有这个Pointer对象。2, 然后,这个堆上新创建的指针的值被设置为传入的参数的地址,也就是指向传入的Pointer对象。这样,新创建的Pointer对象就是指针的指针。 使用PointerByReference模拟指向指针的指针假设我们有一个结构体UserStruct的实例userStruct,现在又有了一个指向userStruct对象的指针pUser。 为了得到UserStruct**指针在Java中的对等体,我们可以执行如下代码:PointerByReferenceppUser=new PointerByReference(pUser);这会在堆中创建一个指针pointer,然后把pUser指针的地址复制到pointer对象中,这样pointer也就是指向pUser的指针。Pointer对象就是代表UserStruct**类型的指针。可以使用ppUser.getPointer()方法返回pointer对象。我们在Java和原生代码的类型映射表中曾经指出,PointerType和Pointer类型相同,都可以表示指针。PointerByReference类是PointerType类的子类,因此,ppUser对象也可以代表UserStruct**类型的指针。 模拟指针 下面,给大家展示一个完整的例子,展示如何使用Pointer和PointerByReference类型模拟各类原生指针。C代码:void sayUser(UserStruct* pUserStruct){ std::wcout.imbue(std::locale("chs")); std::wcout<id< std::wcout<name< std::wcout<age<}void sayUser2(UserStruct** ppUserStruct){ //UserStruct** ppUserStruct=*pppUserStruct; UserStruct* pUserStruct=*ppUserStruct; sayUser(pUserStruct);}void sayUser3(UserStruct*** pppUserStruct){ //UserStruct**ppUserStruct=*pppUserStruct; UserStruct** ppUserStruct=*pppUserStruct; sayUser2(ppUserStruct);}然后发布这3个函数。JNA中模拟:在接口中添加方法:public voidsayUser(UserStruct.ByReference struct);public void sayUser2(PointerByReference ppUserStruct);public void sayUser3(PointerpppUserStruct);JNA中调用:UserStruct pUserStruct2=new UserStruct(); pUserStruct2.id=new NativeLong(90); pUserStruct2.age=99; pUserStruct2.name=new WString("乔布斯"); pUserStruct2.write(); PointerpPointer=pUserStruct2.getPointer(); PointerByReferenceppUserStruct=new PointerByReference(pPointer); System.out.println("使用ppUserStruct!!!!"); TestDll1.INSTANCE.sayUser2(ppUserStruct); System.out.println("使用pppUserStruct!!!!"); PointerByReference pppUserStruct=newPointerByReference(ppUserStruct.getPointer()); TestDll1.INSTANCE.sayUser3(pppUserStruct.getPointer()); 可以看到,我们能够使用Pointer或者PointerByReference来表示指向指针的指针。sayUser3中,我们使用了PointerByReference类的getPointer()方法返回了代表UserStruct***类型的指针。 事实上,如果public void sayUser3(Pointer pppUserStruct);定义成public void sayUser3(PointerByReference pppUserStruct);也是可以的,只是调用时提供的参数变为pppUserStruct对象本身即可。 通过使用Pointer和PointerByReference类,我们可以模拟任何原生代码的指针。类详解 setPointer()方法相当于pTr2=&ptr1; setLong()方法相当于ptr2=&long; getPointer(0)相当于 (void*) *ptr2;取指针指向的值,返回的还是指针。 getLong(0)相当于 (long)*ptr2;取指针指向的值,返回的是long类型的数据。 JNA打破了Java和原生代码原本泾渭分明的界限,实现了Java和原生代码的强强联合,在各自擅长的领域分工合作,快速解决问题。Java可以方便地利用原生代码的优势:执行速度快,可以直接操作硬件,机器码不容易被破解等。原生代码可以通过回调Java函数,利用Java的优势:开发效率高,自动内存管理,跨平台,类库丰富,网络功能强大,支持多种脚本语言等。JNA为Java开发者打开了一扇通向广袤的原生代码世界的大门。
}
它需要传入一个Unicode编码的字符数组。然后在控制台上打印出一段中文字符。
为了调用这个原生函数,使用JNA,我们需要编写这样的Java代码:
publicinterface TestDll1 extends Library {
TestDll1 INSTANCE =(TestDll1)Native.loadLibrary("TestDll1", TestDll1.class);
public void say(WString value);
这里,如果动态链接库是以stdcall方式输出函数,那么就继承StdCallLibrary。
然后就可以像普通的Java程序那样调用这个接口:
public static void main(String[] args) {
TestDll1.INSTANCE.say(newWString("Hello World!"));
System.out.println("Java输出。");
执行,可以看到控制台下如下输出:
原生函数说:Hello World!
Java输出。
JNA不使用native关键字。
JNI使用native关键字,使用一个个Java方法来代表外部的原生函数。
而JNA使用一个Java接口来代表一个动态链接库发布的所有函数。
对于不需要的原生函数,你可以不在Java接口中声明Java方法原型。
如果使用JNI,你需要使用System.loadLibrary方法,把我们专为JNI编写的动态链接库载入进来。这个动态链接库实际上是我们真正需要的动态链接库的代理。
上例中使用JNA类库的Native类的loadLibrary方法 ,是直接把我们需要的动态链接库载入进来。使用JNA,我们不需要编写作为代理的动态链接库,不需要编写一行原生代码。
上面的JNA代码使用了单例,接口的静态变量返回的是接口的唯一实例,这个Java对象是JNA通过反射动态创建的。通过这个对象,我们可以调用动态链接库发布的函数。
跨平台、跨语言调用的最大难点,就是不同语言之间数据类型不一致造成的问题。绝大部分跨平台调用的失败,都是这个问题造成的。
JNA使用的数据类型是Java的数据类型。而原生函数中使用的数据类型是原生函数的编程语言使用的数据类型。可能是C,Delphi,汇编等语言的数据类型。因此,不一致是在所难免的。
JNA提供了Java和原生代码的类型映射。
Java 类型
C 类型
原生表现
boolean
int
32位整数 (可定制)
byte
char
8位整数
wchar_t
平台依赖
short
16位整数
32位整数
long
long long, __int64
64位整数
float
32位浮点数
double
64位浮点数
BufferPointer
Buffer
Pointer
pointer
平台依赖(32或 64位指针)
[] (基本类型的数组)
pointerarray
32或 64位指针(参数/返回值)邻接内存(结构体成员)
String
char*
/0结束的数组 (native encoding or jna.encoding)
jna.encoding
WString
wchar_t*
/0结束的数组(unicode)
String[]
char**
/0结束的数组的数组
WString[]
wchar_t**
/0结束的宽字符数组的数组
Structure
struct*struct
指向结构体的指针 (参数或返回值) (或者明确指定是结构体指针)结构体(结构体的成员) (或者明确指定是结构体)
Union
union
等同于结构体
Structure[]
struct[]
结构体的数组,邻接内存
Callback
(*fp)()
Java函数指针或原生函数指针
NativeMapped
varies
依赖于定义
NativeLong
平台依赖(32或64位整数)
PointerType
和 Pointer相同
相同
尽量使用基本、简单的数据类型;
尽量少跨平台、跨语言传递数据!
如果有复杂的数据类型需要在Java和原生函数中传递,那么我们就必须在Java中模拟大量复杂的原生类型。这将大大增加实现的难度,甚至无法实现。
如果在Java和原生函数间存在大量的数据传递,那么一方面,性能会有很大的损失。更为重要的是,Java调用原生函数时,会把数据固定在内存中,这样原生函数才可以访问这些Java数据。这些数据,JVM的GC不能管理,会造成内存碎片。
如果在你需要调用的动态链接库中,有复杂的数据类型和庞大的跨平台数据传递。那么你应该另外写一些原生函数,把需要传递的数据类型简化,把需要传递的数据量简化。
在原生代码中,结构体是经常使用的复杂数据类型。这里我们研究一下怎样使用JNA模拟结构体。
假设我们现在有这样一个C语言结构体
struct UserStruct{
long id;
wchar_t* name;
int age;
};
使用上述结构体的函数
#define MYLIBAPI extern "C" __declspec( dllexport )
MYLIBAPI void sayUser(UserStruct* pUserStruct);
对应的Java程序中,在例1的接口中添加下列代码:
public static classUserStruct extendsStructure{
publicNativeLong id;
publicWString name;
public int age;
public static class ByReferenceextends UserStruct implements Structure.ByReference { }
public static class ByValue extends UserStruct implements Structure.ByValue
{ }
public void sayUser(UserStruct.ByReference struct);
Java中的调用代码:
UserStruct userStruct=new UserStruct ();
userStruct.id=newNativeLong(100);
userStruct.age=30;
userStruct.name=newWString("奥巴马"); TestDll1.INSTANCE.sayUser(userStruct);
现在,我们就在Java中实现了对C语言的结构体的模拟。
这里,我们继承了Structure类,用这个类来模拟C语言的结构体。
必须注意,Structure子类中的公共字段的顺序,必须与C语言中的结构的顺序一致。否则会报错!
因为,Java调用动态链接库中的C函数,实际上就是一段内存作为函数的参数传递给C函数。
动态链接库以为这个参数就是C语言传过来的参数。
同时,C语言的结构体是一个严格的规范,它定义了内存的次序。因此,JNA中模拟的结构体的变量顺序绝对不能错。
如果一个Struct有2个int变量。 Int a, int b
如果JNA中的次序和C中的次序相反,那么不会报错,但是数据将会被传递到错误的字段中去。
Structure类代表了一个原生结构体。当Structure对象作为一个函数的参数或者返回值传递时,它代表结构体指针。当它被用在另一个结构体内部作为一个字段时,它代表结构体本身。
另外,Structure类有两个内部接口Structure.ByReference和Structure.ByValue。这两个接口仅仅是标记,如果一个类实现Structure.ByReference接口,就表示这个类代表结构体指针。如果一个类实现Structure.ByValue接口,就表示这个类代表结构体本身。
使用这两个接口的实现类,可以明确定义我们的Structure实例表示的是结构体的指针还是结构体本身。
上面的例子中,由于Structure实例作为函数的参数使用,因此是结构体指针。所以这里直接使用了UserStruct userStruct=new UserStruct ();
也可以使用UserStruct userStruct=new UserStruct.ByReference ();
明确指出userStruct对象是结构体指针而不是结构体本身。
C语言最主要的数据类型就是结构体。结构体可以内部可以嵌套结构体,这使它可以模拟任何类型的对象。
JNA也可以模拟这类复杂的结构体。
structCompanyStruct{
UserStruct users[100];
int count;
JNA中可以这样模拟:
public static classCompanyStruct extendsStructure{
public NativeLong id;
public WString name;
public UserStruct.ByValue[] users=new UserStruct.ByValue[100];
public int count;
这里,必须给users字段赋值,否则不会分配100个UserStruct结构体的内存,这样JNA中的内存大小和原生代码中结构体的内存大小不一致,调用就会失败。
structCompanyStruct2{
UserStruct* users[100];
public static classCompanyStruct2 extendsStructure{
public UserStruct.ByReference[] users=newUserStruct.ByReference[100];
测试代码:
CompanyStruct2.ByReference companyStruct2=new CompanyStruct2.ByReference();
companyStruct2.id=new NativeLong(2);
companyStruct2.name=new WString("Yahoo");
companyStruct2.count=10;
UserStruct.ByReference pUserStruct=new UserStruct.ByReference();
pUserStruct.id=new NativeLong(90);
pUserStruct.age=99;
pUserStruct.name=new WString("杨致远");
// pUserStruct.write();
for(inti=0;icount;i++){
companyStruct2.users[i]=pUserStruct;
TestDll1.INSTANCE.sayCompany2(companyStruct2);
执行测试代码,报错了。这是怎么回事?
考察JNI技术,我们发现Java调用原生函数时,会把传递给原生函数的Java数据固定在内存中,这样原生函数才可以访问这些Java数据。对于没有固定住的Java对象,GC可以删除它,也可以移动它在内存中的位置,以使堆上的内存连续。如果 原生函数访问没有被固定住的Java对象,就会导致调用失败。
固定住哪些java对象,是JVM根据原生函数调用自动判断的。而上面的CompanyStruct2结构体中的一个字段是UserStruct对象指针的数组,因此,JVM在执行时只是固定住了CompanyStruct2对象的内存,而没有固定住users字段引用的UserStruct数组。因此,造成了错误。
我们需要把users字段引用的UserStruct数组的所有成员也全部固定住,禁止GC移动或者删除。
如果我们执行了pUserStruct.write();这段代码,那么就可以成功执行上述代码。
Structure类的write()方法会把结构体的所有字段固定住,使原生函数可以访问。
JNI技术是双向的,既可以从Java代码中调用原生函数,也可以从原生函数中直接创建Java虚拟机,并调用Java代码。
但是,这样做要写大量C代码,对于广大Java程序员来说是很头疼的。
使用JNA,我们就可以不写一行C代码,照样实现原生代码调用Java代码!
JNA可以模拟函数指针,通过函数指针,就可以实现在原生代码中调用Java函数。
让我们先看一个模拟函数指针的JNA例子:
intgetValue(int (*fp)(intleft,int right),intleft,int right){
returnfp(left,right);
C函数中通过函数指针调用外部传入的函数,执行任务。
JNA中这样模拟函数指针:
public static interfaceFp extendsCallback {
int invoke(int left,int right);
C函数用如下Java方法声明代表:
public int getValue(Fp fp,int left,int right);
现在,我们有了代表函数指针int(*fp)(int left,intright)的接口Fp,但是还没有Fp的实现类。
public static classFpAdd implementsFp{
@Override
public intinvoke(intleft, intright) {
return left+right;
原生函数可以通过函数指针实现函数回调,调用外部函数来执行任务。这就是策略模式。
JNA可以方便地模拟函数指针,把Java函数作为函数指针传递给原生函数,实现在原生代码中调用Java代码。
JNA可以模拟原生代码中的指针。Java和原生代码的类型映射表中的指针映射是这样的:
原生代码中的数组,可以使用JNA中对应类型的数组来表示。
原生代码中的指针,可以使用Pointer类型,或者PointerType类型及它们的子类型来模拟。
Pointer代表原生代码中的指针。其属性peer就是原生代码中指针的地址。
我们不可以直接创建Pointer对象,但可以用它表示原生函数中的任何指针。
Pointer类有2个子类:Function, Memory。
Function类代表原生函数的指针,可以通过invoke(Class,Object[],Map)这一系列的方法调用原生函数。
Memory类代表的是堆中的一段内存,它也是我们可以创建的Pointer子类。创建一个Memory类的实例,就是在原生代码的内存区中分配一块指定大小的内存。这块内存会在GC释放这个Java对象时被释放。Memory类在指针模拟中会被经常用到。
PointerType类代表的是一个类型安全的指针。ByReference类是PointerType类的子类。ByReference类代表指向堆内存的指针。ByReference类非常简单。
public abstract class ByReference extends PointerType {
protected ByReference(int dataSize) {
setPointer(new Memory(dataSize));
ByReference类有很多子类,这些类都非常有用。
ByteByReference, DoubleByReference,FloatByReference, IntByReference, LongByReference, NativeLongByReference,PointerByReference, ShortByReference, W32API.HANDLEByReference,X11.AtomByReference, X11.WindowByReference
ByteByReference等类故名思议,就是指向原生代码中的字节数据的指针。
PointerByReference类表示指向指针的指针。
在JNA中模拟指针,最常用到的就是Pointer类和PointerByReference类。Pointer类代表指向任何东西的指针,PointerByReference类表示指向指针的指针。Pointer类更加通用,事实上PointerByReference类内部也持有Pointer类的实例。
PointerByReference类可以嵌套使用,它所指向的指针,本身可能也是指向指针的指针。PointerByReference类的源代码:
public class PointerByReference extends ByReference {
public PointerByReference() {
this(null);
public PointerByReference(Pointervalue) {
super(Pointer.SIZE);
setValue(value);
public void setValue(Pointer value) {
getPointer().setPointer(0,value);
public Pointer getValue() {
returngetPointer().getPointer(0);
可以看到,PointerByReference类的构造器做了如下工作:
1, 首先在堆中分配一个指针大小的内存,并用一个Pointer对象代表。PointerByReference类的实例持有这个Pointer对象。
2, 然后,这个堆上新创建的指针的值被设置为传入的参数的地址,也就是指向传入的Pointer对象。这样,新创建的Pointer对象就是指针的指针。
假设我们有一个结构体UserStruct的实例userStruct,现在又有了一个指向userStruct对象的指针pUser。
为了得到UserStruct**指针在Java中的对等体,我们可以执行如下代码:
PointerByReferenceppUser=new PointerByReference(pUser);
这会在堆中创建一个指针pointer,然后把pUser指针的地址复制到pointer对象中,这样pointer也就是指向pUser的指针。Pointer对象就是代表UserStruct**类型的指针。可以使用ppUser.getPointer()方法返回pointer对象。
我们在Java和原生代码的类型映射表中曾经指出,PointerType和Pointer类型相同,都可以表示指针。PointerByReference类是PointerType类的子类,因此,ppUser对象也可以代表UserStruct**类型的指针。
下面,给大家展示一个完整的例子,展示如何使用Pointer和PointerByReference类型模拟各类原生指针。
C代码:
void sayUser(UserStruct* pUserStruct){
std::wcout<id< std::wcout<name< std::wcout<age<}void sayUser2(UserStruct** ppUserStruct){ //UserStruct** ppUserStruct=*pppUserStruct; UserStruct* pUserStruct=*ppUserStruct; sayUser(pUserStruct);}void sayUser3(UserStruct*** pppUserStruct){ //UserStruct**ppUserStruct=*pppUserStruct; UserStruct** ppUserStruct=*pppUserStruct; sayUser2(ppUserStruct);}然后发布这3个函数。JNA中模拟:在接口中添加方法:public voidsayUser(UserStruct.ByReference struct);public void sayUser2(PointerByReference ppUserStruct);public void sayUser3(PointerpppUserStruct);JNA中调用:UserStruct pUserStruct2=new UserStruct(); pUserStruct2.id=new NativeLong(90); pUserStruct2.age=99; pUserStruct2.name=new WString("乔布斯"); pUserStruct2.write(); PointerpPointer=pUserStruct2.getPointer(); PointerByReferenceppUserStruct=new PointerByReference(pPointer); System.out.println("使用ppUserStruct!!!!"); TestDll1.INSTANCE.sayUser2(ppUserStruct); System.out.println("使用pppUserStruct!!!!"); PointerByReference pppUserStruct=newPointerByReference(ppUserStruct.getPointer()); TestDll1.INSTANCE.sayUser3(pppUserStruct.getPointer()); 可以看到,我们能够使用Pointer或者PointerByReference来表示指向指针的指针。sayUser3中,我们使用了PointerByReference类的getPointer()方法返回了代表UserStruct***类型的指针。 事实上,如果public void sayUser3(Pointer pppUserStruct);定义成public void sayUser3(PointerByReference pppUserStruct);也是可以的,只是调用时提供的参数变为pppUserStruct对象本身即可。 通过使用Pointer和PointerByReference类,我们可以模拟任何原生代码的指针。类详解 setPointer()方法相当于pTr2=&ptr1; setLong()方法相当于ptr2=&long; getPointer(0)相当于 (void*) *ptr2;取指针指向的值,返回的还是指针。 getLong(0)相当于 (long)*ptr2;取指针指向的值,返回的是long类型的数据。 JNA打破了Java和原生代码原本泾渭分明的界限,实现了Java和原生代码的强强联合,在各自擅长的领域分工合作,快速解决问题。Java可以方便地利用原生代码的优势:执行速度快,可以直接操作硬件,机器码不容易被破解等。原生代码可以通过回调Java函数,利用Java的优势:开发效率高,自动内存管理,跨平台,类库丰富,网络功能强大,支持多种脚本语言等。JNA为Java开发者打开了一扇通向广袤的原生代码世界的大门。
std::wcout<name< std::wcout<age<}void sayUser2(UserStruct** ppUserStruct){ //UserStruct** ppUserStruct=*pppUserStruct; UserStruct* pUserStruct=*ppUserStruct; sayUser(pUserStruct);}void sayUser3(UserStruct*** pppUserStruct){ //UserStruct**ppUserStruct=*pppUserStruct; UserStruct** ppUserStruct=*pppUserStruct; sayUser2(ppUserStruct);}然后发布这3个函数。JNA中模拟:在接口中添加方法:public voidsayUser(UserStruct.ByReference struct);public void sayUser2(PointerByReference ppUserStruct);public void sayUser3(PointerpppUserStruct);JNA中调用:UserStruct pUserStruct2=new UserStruct(); pUserStruct2.id=new NativeLong(90); pUserStruct2.age=99; pUserStruct2.name=new WString("乔布斯"); pUserStruct2.write(); PointerpPointer=pUserStruct2.getPointer(); PointerByReferenceppUserStruct=new PointerByReference(pPointer); System.out.println("使用ppUserStruct!!!!"); TestDll1.INSTANCE.sayUser2(ppUserStruct); System.out.println("使用pppUserStruct!!!!"); PointerByReference pppUserStruct=newPointerByReference(ppUserStruct.getPointer()); TestDll1.INSTANCE.sayUser3(pppUserStruct.getPointer()); 可以看到,我们能够使用Pointer或者PointerByReference来表示指向指针的指针。sayUser3中,我们使用了PointerByReference类的getPointer()方法返回了代表UserStruct***类型的指针。 事实上,如果public void sayUser3(Pointer pppUserStruct);定义成public void sayUser3(PointerByReference pppUserStruct);也是可以的,只是调用时提供的参数变为pppUserStruct对象本身即可。 通过使用Pointer和PointerByReference类,我们可以模拟任何原生代码的指针。类详解 setPointer()方法相当于pTr2=&ptr1; setLong()方法相当于ptr2=&long; getPointer(0)相当于 (void*) *ptr2;取指针指向的值,返回的还是指针。 getLong(0)相当于 (long)*ptr2;取指针指向的值,返回的是long类型的数据。 JNA打破了Java和原生代码原本泾渭分明的界限,实现了Java和原生代码的强强联合,在各自擅长的领域分工合作,快速解决问题。Java可以方便地利用原生代码的优势:执行速度快,可以直接操作硬件,机器码不容易被破解等。原生代码可以通过回调Java函数,利用Java的优势:开发效率高,自动内存管理,跨平台,类库丰富,网络功能强大,支持多种脚本语言等。JNA为Java开发者打开了一扇通向广袤的原生代码世界的大门。
std::wcout<age<}void sayUser2(UserStruct** ppUserStruct){ //UserStruct** ppUserStruct=*pppUserStruct; UserStruct* pUserStruct=*ppUserStruct; sayUser(pUserStruct);}void sayUser3(UserStruct*** pppUserStruct){ //UserStruct**ppUserStruct=*pppUserStruct; UserStruct** ppUserStruct=*pppUserStruct; sayUser2(ppUserStruct);}然后发布这3个函数。JNA中模拟:在接口中添加方法:public voidsayUser(UserStruct.ByReference struct);public void sayUser2(PointerByReference ppUserStruct);public void sayUser3(PointerpppUserStruct);JNA中调用:UserStruct pUserStruct2=new UserStruct(); pUserStruct2.id=new NativeLong(90); pUserStruct2.age=99; pUserStruct2.name=new WString("乔布斯"); pUserStruct2.write(); PointerpPointer=pUserStruct2.getPointer(); PointerByReferenceppUserStruct=new PointerByReference(pPointer); System.out.println("使用ppUserStruct!!!!"); TestDll1.INSTANCE.sayUser2(ppUserStruct); System.out.println("使用pppUserStruct!!!!"); PointerByReference pppUserStruct=newPointerByReference(ppUserStruct.getPointer()); TestDll1.INSTANCE.sayUser3(pppUserStruct.getPointer()); 可以看到,我们能够使用Pointer或者PointerByReference来表示指向指针的指针。sayUser3中,我们使用了PointerByReference类的getPointer()方法返回了代表UserStruct***类型的指针。 事实上,如果public void sayUser3(Pointer pppUserStruct);定义成public void sayUser3(PointerByReference pppUserStruct);也是可以的,只是调用时提供的参数变为pppUserStruct对象本身即可。 通过使用Pointer和PointerByReference类,我们可以模拟任何原生代码的指针。类详解 setPointer()方法相当于pTr2=&ptr1; setLong()方法相当于ptr2=&long; getPointer(0)相当于 (void*) *ptr2;取指针指向的值,返回的还是指针。 getLong(0)相当于 (long)*ptr2;取指针指向的值,返回的是long类型的数据。 JNA打破了Java和原生代码原本泾渭分明的界限,实现了Java和原生代码的强强联合,在各自擅长的领域分工合作,快速解决问题。Java可以方便地利用原生代码的优势:执行速度快,可以直接操作硬件,机器码不容易被破解等。原生代码可以通过回调Java函数,利用Java的优势:开发效率高,自动内存管理,跨平台,类库丰富,网络功能强大,支持多种脚本语言等。JNA为Java开发者打开了一扇通向广袤的原生代码世界的大门。
void sayUser2(UserStruct** ppUserStruct){
//UserStruct** ppUserStruct=*pppUserStruct;
UserStruct* pUserStruct=*ppUserStruct;
sayUser(pUserStruct);
void sayUser3(UserStruct*** pppUserStruct){
//UserStruct**ppUserStruct=*pppUserStruct;
UserStruct** ppUserStruct=*pppUserStruct;
sayUser2(ppUserStruct);
然后发布这3个函数。
JNA中模拟:
在接口中添加方法:
public voidsayUser(UserStruct.ByReference struct);
public void sayUser2(PointerByReference ppUserStruct);
public void sayUser3(PointerpppUserStruct);
JNA中调用:
UserStruct pUserStruct2=new UserStruct();
pUserStruct2.id=new NativeLong(90);
pUserStruct2.age=99;
pUserStruct2.name=new WString("乔布斯");
pUserStruct2.write();
PointerpPointer=pUserStruct2.getPointer();
PointerByReferenceppUserStruct=new PointerByReference(pPointer);
System.out.println("使用ppUserStruct!!!!");
TestDll1.INSTANCE.sayUser2(ppUserStruct);
System.out.println("使用pppUserStruct!!!!");
PointerByReference pppUserStruct=newPointerByReference(ppUserStruct.getPointer());
TestDll1.INSTANCE.sayUser3(pppUserStruct.getPointer());
可以看到,我们能够使用Pointer或者PointerByReference来表示指向指针的指针。sayUser3中,我们使用了PointerByReference类的getPointer()方法返回了代表UserStruct***类型的指针。
事实上,如果public void sayUser3(Pointer pppUserStruct);定义成
public void sayUser3(PointerByReference pppUserStruct);也是可以的,只是调用时提供的参数变为pppUserStruct对象本身即可。
通过使用Pointer和PointerByReference类,我们可以模拟任何原生代码的指针。
setPointer()方法相当于pTr2=&ptr1;
setLong()方法相当于ptr2=&long;
getPointer(0)相当于 (void*) *ptr2;
取指针指向的值,返回的还是指针。
getLong(0)相当于 (long)*ptr2;
取指针指向的值,返回的是long类型的数据。
JNA打破了Java和原生代码原本泾渭分明的界限,实现了Java和原生代码的强强联合,在各自擅长的领域分工合作,快速解决问题。
Java可以方便地利用原生代码的优势:执行速度快,可以直接操作硬件,机器码不容易被破解等。
原生代码可以通过回调Java函数,利用Java的优势:开发效率高,自动内存管理,跨平台,类库丰富,网络功能强大,支持多种脚本语言等。
JNA为Java开发者打开了一扇通向广袤的原生代码世界的大门。
将参数声明为特殊类型,以传达通过引用传递它们的事实。因此,如果Delphi参数是这样的:
procedure Foo(var i: Integer; var d: Double);
您可以将其映射到这样的Java函数:
void Foo(IntByReference i, DoubleByReference d);
并调用该函数:
IntByReference iref = new IntByReference(); DoubleByReference dref = new DoubleByReference(); INSTANCE.Foo(iref, dref); int i = iref.getValue(); double d = dref.getValue():
用java后台调用博思财政软件的dll 库这是dll文档对应函数的说明
3.4、转入开票数据说明:根据博思票据预设的需求格式,转入博思开票界面,并可进行打印票据信息。Function PZrPj (ZrTxt:PChar;IsPrn:integer; PjLx,Bz,Res:PChar):integer;StdCall; 参数: ZrTxt : 票据数据(格式详见:数据格式说明.txt 文件) IsPrn : 0 表示显示转入票据信息后再进行开票 1 表示直接开票并打印 2 表示直接开票但不打印PjLx : 票据类型 (传空表示取默认当前的票据类型开票,不为空则指定类型)Bz : 单据的备注 (可选的参数,可不传则转入无备注)Res: 只作为存储返回字符 成功:票据类型号,票据号,金额,注册号(以,分格) 错误:(错误信息)返回值: 参数Res的长度 示例1:var vRes:pChar;SetLength(vRes,500); //vRes变量宽度,请定义足够大以返回完整错误信息 PZrPj(SomeString,1,’001001’,’’,vRes) 返回值:25 vRes返回值:’成功:001001,085256,1200.3,闽财2004票’ 表示:传入开票内容为SomeString,票据类型为’001001’, 备注为空 返回值:长度为25 开票成功,返回值为 票据类型为001001 票据号为085256 金额为1200.3元 注册号 闽财2004票 示例2:var vRes:pChar; PZrPj(SomeString,1,’’,’张三’,vRes) 返回值: 31 vRes返回值:’错误:请先上报核销后才能开票据。’ 表示: 传入开票内容为SomeString,票据类型为默认值, 备注为’张三’ 返回值长度为31 开票不成功,返回值为不成功的错误信息
3.4、转入开票数据
说明:根据博思票据预设的需求格式,转入博思开票界面,并可进行打印票据信息。
Function PZrPj
(ZrTxt:PChar;IsPrn:integer; PjLx,Bz,Res:PChar):integer;StdCall;
参数: ZrTxt : 票据数据
(格式详见:数据格式说明.txt 文件)
IsPrn : 0 表示显示转入票据信息后再进行开票
1 表示直接开票并打印
2 表示直接开票但不打印
PjLx : 票据类型
(传空表示取默认当前的票据类型开票,
不为空则指定类型)
Bz : 单据的备注 (可选的参数,可不传则转入无备注)
Res: 只作为存储返回字符
成功:票据类型号,票据号,金额,注册号(以,分格)
错误:(错误信息)
返回值: 参数Res的长度
示例1:var vRes:pChar;
SetLength(vRes,500);
//vRes变量宽度,请定义足够大以返回完整错误信息
PZrPj(SomeString,1,’001001’,’’,vRes)
返回值:25
vRes返回值:’成功:001001,085256,1200.3,闽财2004票’
表示:传入开票内容为SomeString,票据类型为’001001’,
备注为空
返回值:长度为25
开票成功,返回值为 票据类型为001001
票据号为085256
金额为1200.3元
注册号 闽财2004票
示例2:var vRes:pChar;
PZrPj(SomeString,1,’’,’张三’,vRes)
返回值: 31 vRes返回值:’错误:请先上报核销后才能开票据。’
表示: 传入开票内容为SomeString,票据类型为默认值,
备注为’张三’
返回值长度为31
开票不成功,返回值为不成功的错误信息
我在java上是获取不到res返回的值以下是我实现的步骤dll 接口
package com.synjones.bosi;import com.sun.jna.Library;import com.sun.jna.Native;public interface BosiAPI extends Library{ BosiAPI insanceDll = Native.loadLibrary("KpJk.dll", BosiAPI.class); int PConnect();//连接博思开票系统(方式一) int PLoginSuccess();//判断是否登陆成功 int PZrPj(String ZrTxt,int IsPrn,String PjLx,String Bz,String Res);//转入开票数据 }
package com.synjones.bosi;
import com.sun.jna.Library;
import com.sun.jna.Native;
public interface BosiAPI extends Library{
BosiAPI insanceDll = Native.loadLibrary("KpJk.dll", BosiAPI.class);
int PConnect();//连接博思开票系统(方式一)
int PLoginSuccess();//判断是否登陆成功
int PZrPj(String ZrTxt,int IsPrn,String PjLx,String Bz,String Res);//转入开票数据
测试代码
package com.synjones.bosi;import com.sun.jna.ptr.PointerByReference;public class bositest { public static void main(String[] args) { // TODO Auto-generated method stub int j = BosiAPI.insanceDll.PConnect(); System.out.println(j); int i = BosiAPI.insanceDll.PLoginSuccess(); System.out.println(i); kptest(); } public static void kptest() { System.out.println("开票测试"); String ZrTxt = "<&票据><&票据头>姓名=赵惠卿\t就诊号=12314\t保险编号=\t性别=女\t就诊科室=\t人员类别= \t医保类别=省医保\t付款方式=现金\t缴费流水号=2\t医保收据号=\t医疗费用总额= \t公务员补助支付=\t统筹金支付= \t个人现金支付= \t个人帐户支付= 大额补助支付= 上次账户余额= 个人账户余额=&票据头><&收费项目>收费项目=R01 计费数量=3 收费标准=100 金额=300&收费项目>&票据>"; PointerByReference pref = new PointerByReference(); String res = ""; int i = BosiAPI.insanceDll.PZrPj(ZrTxt, 1, "14004003", "这是备注", res); System.out.println(i);// Pointer p = pref.getValue();// System.out.println(p);// byte [] buffer = p.getByteArray(0,i);// System.out.println(buffer); System.out.println(res); System.out.println("-------------------"); }
import com.sun.jna.ptr.PointerByReference;
public class bositest {
// TODO Auto-generated method stub
int j = BosiAPI.insanceDll.PConnect();
System.out.println(j);
int i = BosiAPI.insanceDll.PLoginSuccess();
System.out.println(i);
kptest();
public static void kptest() {
System.out.println("开票测试");
String ZrTxt = "<&票据><&票据头>姓名=赵惠卿\t就诊号=12314\t保险编号=\t性别=女\t就诊科室=\t人员类别= \t医保类别=省医保\t付款方式=现金\t缴费流水号=2\t医保收据号=\t医疗费用总额= \t公务员补助支付=\t统筹金支付= \t个人现金支付= \t个人帐户支付= 大额补助支付= 上次账户余额= 个人账户余额=&票据头><&收费项目>收费项目=R01 计费数量=3 收费标准=100 金额=300&收费项目>&票据>";
PointerByReference pref = new PointerByReference();
String res = "";
int i = BosiAPI.insanceDll.PZrPj(ZrTxt, 1, "14004003", "这是备注", res);
// Pointer p = pref.getValue();
// System.out.println(p);
// byte [] buffer = p.getByteArray(0,i);
// System.out.println(buffer);
System.out.println(res);
System.out.println("-------------------");
Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpda...
忙的时候项目期肯定要加班 但是每天加班应该还不至于
虽然Java人才越来越多,但是人才缺口也是很大的,我国对JAVA工程师的需求是所有软件工程师当中需求大的,达到全部需求量的60%-70%,所以Java市场在短时间内不可能饱和。其次,Java市场不断变化,人才需求也会不断增加。马云说过,未来的制造业要的不是石油,...
工信部证书含金量较高。工信部是国务院的下属结构,具有发放资质、证书的资格。其所发放的证书具有较强的权威性,在全国范围内收到认可,含金量通常都比较高。 工信部证书,其含义也就是工信部颁发并承认的某项技能证书,是具有法律效力的,并且是国家认可的...
学Java好不好找工作?看学完Java后能做些什么吧。一、大数据技术Hadoop以及其他大数据处理技术都是用Java或者其他,例如Apache的基于Java 的 HBase和Accumulo以及ElasticSearchas。但是Java在此领域并未占太大空间,但只要Hadoop和ElasticSearchas能够成长壮...
就是java的基础知识啊,比如Java 集合框架;Java 多线程;线程的五种状态;Java 虚拟机;MySQL (InnoDB);Spring 相关;计算机网络;MQ 消息队列诸如此类
#{}和${}这两个语法是为了动态传递参数而存在的,是Mybatis实现动态SQL的基础,总体上他们的作用是一致的(为了动态传参),但是在编译过程、是否自动加单引号、安全性、使用场景等方面有很多不同,下面详细比较两者间的区别:1.#{} 是 占位符 :动态解析 ...
没问题的,专科学历也能学习Java开发的,主要看自己感不感兴趣,只要认真学,市面上的培训机构不少都是零基础课程,能跟得上,或是自己先找些资料学习一下。
1、反射对单例模式的破坏采用反射的方式另辟蹊径实例了该类,导致程序中会存在不止一个实例。解决方案其思想就是采用一个全局变量,来标记是否已经实例化过了,如果已经实例化过了,第 二次实例化的时候,抛出异常2、clone()对单例模式的破坏当需要实现单例的...
优点: 一、实例控制 单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。 二、灵活性 因为类控制了实例化过程,所以类可以灵活更改实例化过程。 缺点: 一、开销 虽然数量很少,但如果每次对象请求引用时都要...
这个主要是看你数组的长度是多少, 比如之前写过的一个程序有个数组存的是各个客户端的ip地址:string clientIp[4]={XXX, xxx, xxx, xxx};这个时候如果想把hash值对应到上面四个地址的话,就应该对4取余,这个时候p就应该为4...
哈希表的大小 · 关键字的分布情况 · 记录的查找频率 1.直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)。...
哈希表的大小取决于一组质数,原因是在hash函数中,你要用这些质数来做模运算(%)。而分析发现,如果不是用质数来做模运算的话,很多生活中的数据分布,会集中在某些点上。所以这里最后采用了质数做模的除数。 因为用质数做了模的除数,自然存储空间的大小也用质数了...
是啊,哈希函数的设计至关重要,好的哈希函数会尽可能地保证计算简单和散列地址分布均匀,但是,我们需要清楚的是,数组是一块连续的固定长度的内存空间
解码查表优化算法,seo优化
1.对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。2.哈希值就是这个元素的位置。3.如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就...
最多设置5个标签!
工作中需要用java调用dll,并获取信息
代码如下:
和许多解释执行的语言一样,Java提供了调用原生函数的机制,以加强Java平台的能力。Java™ Native Interface (JNI)就是Java调用原生函数的机制。
事实上,很多Java核心代码内部就是使用JNI实现的。这些Java功能实际上是通过原生函数提供的。
但是,使用JNI对Java开发者来说简直是一场噩梦。
如果你已经有了原生函数,使用JNI,你必须使用C语言再编写一个动态链接库,这个动态链接库的唯一功能就是使用Java能够理解的C代码来调用目标原生函数。
这个没什么实际用途的动态链接库的编写过程令人沮丧。同时编写Java和C代码使开发难度大大增加。
因此,在Java开发社区中,人们一直都视JNI为禁地,轻易不愿涉足。
缺少原生函数的协助使Java的使用范围大大缩小。
反观.NET阵营,其P/Invoke技术调用原生函数非常方便,不需要编写一行C代码,只需要写Annotation就可以快速调用原生函数。因此,与硬件有关的很多开发领域都被.NET所占据。
介绍
JNA(Java NativeAccess)框架是一个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的一个框架。
JNA项目地址:https://jna.dev.java.net/
JNA使Java调用原生函数就像.NET上的P/Invoke一样方便、快捷。
JNA的功能和P/Invoke类似,但编写方法与P/Invoke截然不同。JNA没有使用Annotation,而是通过编写一般的Java代码来实现。
P/Invoke是.NET平台的机制。而JNA是Java平台上的一个开源类库,和其他类库没有什么区别。只需要在classpath下加入jna.jar包,就可以使用JNA。
JNA使Java平台可以方便地调用原生函数,这大大扩展了Java平台的整合能力。
实现原理
JNI 是Java调用原生函数唯一的机制。JNA也是建立在JNI技术之上的。它简化了Java调用原生函数的过程。
JNA提供了一个动态的C语言编写的转发器,可以自动实现Java和C的数据类型映射。你不再需要编写那个烦人的C动态链接库。
当然,这也意味着,使用JNA技术比使用JNI技术调用动态链接库会有些微的性能损失。可能速度会降低几倍。但对于绝大部分项目来说,影响不大。
调用原生函数
让我们先看一个JNA调用原生函数的例子。
使用JNA调用原生函数
假设我们有一个动态链接库,发布了这样一个C函数:
void say(wchar_t* pValue){
std::wcout.imbue(std::locale("chs"));
std::wcout<
[] ( 基本类型的数组)
(*fp)()
[] ( 基本类型的数组)
}
它需要传入一个Unicode编码的字符数组。然后在控制台上打印出一段中文字符。
为了调用这个原生函数,使用JNA,我们需要编写这样的Java代码:
publicinterface TestDll1 extends Library {
TestDll1 INSTANCE =(TestDll1)Native.loadLibrary("TestDll1", TestDll1.class);
public void say(WString value);
}
这里,如果动态链接库是以stdcall方式输出函数,那么就继承StdCallLibrary。
然后就可以像普通的Java程序那样调用这个接口:
public static void main(String[] args) {
TestDll1.INSTANCE.say(newWString("Hello World!"));
System.out.println("Java输出。");
}
执行,可以看到控制台下如下输出:
原生函数说:Hello World!
Java输出。
调用原生函数的模式
JNA不使用native关键字。
JNI使用native关键字,使用一个个Java方法来代表外部的原生函数。
而JNA使用一个Java接口来代表一个动态链接库发布的所有函数。
对于不需要的原生函数,你可以不在Java接口中声明Java方法原型。
如果使用JNI,你需要使用System.loadLibrary方法,把我们专为JNI编写的动态链接库载入进来。这个动态链接库实际上是我们真正需要的动态链接库的代理。
上例中使用JNA类库的Native类的loadLibrary方法 ,是直接把我们需要的动态链接库载入进来。使用JNA,我们不需要编写作为代理的动态链接库,不需要编写一行原生代码。
上面的JNA代码使用了单例,接口的静态变量返回的是接口的唯一实例,这个Java对象是JNA通过反射动态创建的。通过这个对象,我们可以调用动态链接库发布的函数。
和原生代码的类型映射
跨平台、跨语言调用的最大难点,就是不同语言之间数据类型不一致造成的问题。绝大部分跨平台调用的失败,都是这个问题造成的。
JNA使用的数据类型是Java的数据类型。而原生函数中使用的数据类型是原生函数的编程语言使用的数据类型。可能是C,Delphi,汇编等语言的数据类型。因此,不一致是在所难免的。
JNA提供了Java和原生代码的类型映射。
和操作系统数据类型的对应表
Java 类型
C 类型
原生表现
boolean
int
32位整数 (可定制)
byte
char
8位整数
char
wchar_t
平台依赖
short
short
16位整数
int
int
32位整数
long
long long, __int64
64位整数
float
float
32位浮点数
double
double
64位浮点数
Buffer
Pointer
pointer
平台依赖(32或 64位指针)
pointer
array
32或 64位指针(参数/返回值)
邻接内存(结构体成员)
支持常见的数据类型的映射
Java 类型
C 类型
原生表现
String
char*
/0结束的数组 (native encoding or
jna.encoding
)WString
wchar_t*
/0结束的数组(unicode)
String[]
char**
/0结束的数组的数组
WString[]
wchar_t**
/0结束的宽字符数组的数组
Structure
struct*
struct
指向结构体的指针 (参数或返回值) (或者明确指定是结构体指针)
结构体(结构体的成员) (或者明确指定是结构体)
Union
union
等同于结构体
Structure[]
struct[]
结构体的数组,邻接内存
Callback
Java函数指针或原生函数指针
NativeMapped
varies
依赖于定义
NativeLong
long
平台依赖(32或64位整数)
PointerType
pointer
和
Pointer
相同
:
尽量使用基本、简单的数据类型;
尽量少跨平台、跨语言传递数据!
如果有复杂的数据类型需要在Java和原生函数中传递,那么我们就必须在Java中模拟大量复杂的原生类型。这将大大增加实现的难度,甚至无法实现。
如果在Java和原生函数间存在大量的数据传递,那么一方面,性能会有很大的损失。更为重要的是,Java调用原生函数时,会把数据固定在内存中,这样原生函数才可以访问这些Java数据。这些数据,JVM的GC不能管理,会造成内存碎片。
如果在你需要调用的动态链接库中,有复杂的数据类型和庞大的跨平台数据传递。那么你应该另外写一些原生函数,把需要传递的数据类型简化,把需要传递的数据量简化。
模拟结构体
在原生代码中,结构体是经常使用的复杂数据类型。这里我们研究一下怎样使用JNA模拟结构体。
使用JNA调用使用Struct的C函数
假设我们现在有这样一个C语言结构体
struct UserStruct{
long id;
wchar_t* name;
int age;
};
使用上述结构体的函数
#define MYLIBAPI extern "C" __declspec( dllexport )
MYLIBAPI void sayUser(UserStruct* pUserStruct);
对应的Java程序中,在例1的接口中添加下列代码:
public static classUserStruct extendsStructure{
publicNativeLong id;
publicWString name;
public int age;
public static class ByReferenceextends UserStruct implements Structure.ByReference { }
public static class ByValue extends UserStruct implements Structure.ByValue
{ }
}
public void sayUser(UserStruct.ByReference struct);
Java中的调用代码:
UserStruct userStruct=new UserStruct ();
userStruct.id=newNativeLong(100);
userStruct.age=30;
userStruct.name=newWString("奥巴马"); TestDll1.INSTANCE.sayUser(userStruct);
说明
现在,我们就在Java中实现了对C语言的结构体的模拟。
这里,我们继承了Structure类,用这个类来模拟C语言的结构体。
必须注意,Structure子类中的公共字段的顺序,必须与C语言中的结构的顺序一致。否则会报错!
因为,Java调用动态链接库中的C函数,实际上就是一段内存作为函数的参数传递给C函数。
动态链接库以为这个参数就是C语言传过来的参数。
同时,C语言的结构体是一个严格的规范,它定义了内存的次序。因此,JNA中模拟的结构体的变量顺序绝对不能错。
如果一个Struct有2个int变量。 Int a, int b
如果JNA中的次序和C中的次序相反,那么不会报错,但是数据将会被传递到错误的字段中去。
Structure类代表了一个原生结构体。当Structure对象作为一个函数的参数或者返回值传递时,它代表结构体指针。当它被用在另一个结构体内部作为一个字段时,它代表结构体本身。
另外,Structure类有两个内部接口Structure.ByReference和Structure.ByValue。这两个接口仅仅是标记,如果一个类实现Structure.ByReference接口,就表示这个类代表结构体指针。如果一个类实现Structure.ByValue接口,就表示这个类代表结构体本身。
使用这两个接口的实现类,可以明确定义我们的Structure实例表示的是结构体的指针还是结构体本身。
上面的例子中,由于Structure实例作为函数的参数使用,因此是结构体指针。所以这里直接使用了UserStruct userStruct=new UserStruct ();
也可以使用UserStruct userStruct=new UserStruct.ByReference ();
明确指出userStruct对象是结构体指针而不是结构体本身。
模拟复杂结构体
C语言最主要的数据类型就是结构体。结构体可以内部可以嵌套结构体,这使它可以模拟任何类型的对象。
JNA也可以模拟这类复杂的结构体。
结构体内部可以包含结构体对象的数组
structCompanyStruct{
long id;
wchar_t* name;
UserStruct users[100];
int count;
};
JNA中可以这样模拟:
public static classCompanyStruct extendsStructure{
public NativeLong id;
public WString name;
public UserStruct.ByValue[] users=new UserStruct.ByValue[100];
public int count;
}
这里,必须给users字段赋值,否则不会分配100个UserStruct结构体的内存,这样JNA中的内存大小和原生代码中结构体的内存大小不一致,调用就会失败。
结构体内部可以包含结构体对象的指针的数组
structCompanyStruct2{
long id;
wchar_t* name;
UserStruct* users[100];
int count;
};
JNA中可以这样模拟:
public static classCompanyStruct2 extendsStructure{
public NativeLong id;
public WString name;
public UserStruct.ByReference[] users=newUserStruct.ByReference[100];
public int count;
}
测试代码:
CompanyStruct2.ByReference companyStruct2=new CompanyStruct2.ByReference();
companyStruct2.id=new NativeLong(2);
companyStruct2.name=new WString("Yahoo");
companyStruct2.count=10;
UserStruct.ByReference pUserStruct=new UserStruct.ByReference();
pUserStruct.id=new NativeLong(90);
pUserStruct.age=99;
pUserStruct.name=new WString("杨致远");
// pUserStruct.write();
for(inti=0;icount;i++){
companyStruct2.users[i]=pUserStruct;
}
TestDll1.INSTANCE.sayCompany2(companyStruct2);
执行测试代码,报错了。这是怎么回事?
考察JNI技术,我们发现Java调用原生函数时,会把传递给原生函数的Java数据固定在内存中,这样原生函数才可以访问这些Java数据。对于没有固定住的Java对象,GC可以删除它,也可以移动它在内存中的位置,以使堆上的内存连续。如果 原生函数访问没有被固定住的Java对象,就会导致调用失败。
固定住哪些java对象,是JVM根据原生函数调用自动判断的。而上面的CompanyStruct2结构体中的一个字段是UserStruct对象指针的数组,因此,JVM在执行时只是固定住了CompanyStruct2对象的内存,而没有固定住users字段引用的UserStruct数组。因此,造成了错误。
我们需要把users字段引用的UserStruct数组的所有成员也全部固定住,禁止GC移动或者删除。
如果我们执行了pUserStruct.write();这段代码,那么就可以成功执行上述代码。
Structure类的write()方法会把结构体的所有字段固定住,使原生函数可以访问。
代码
JNI技术是双向的,既可以从Java代码中调用原生函数,也可以从原生函数中直接创建Java虚拟机,并调用Java代码。
但是,这样做要写大量C代码,对于广大Java程序员来说是很头疼的。
使用JNA,我们就可以不写一行C代码,照样实现原生代码调用Java代码!
JNA可以模拟函数指针,通过函数指针,就可以实现在原生代码中调用Java函数。
让我们先看一个模拟函数指针的JNA例子:
通过回调函数实现原生代码调用Java代码
intgetValue(int (*fp)(intleft,int right),intleft,int right){
returnfp(left,right);
}
C函数中通过函数指针调用外部传入的函数,执行任务。
JNA中这样模拟函数指针:
public static interfaceFp extendsCallback {
int invoke(int left,int right);
}
C函数用如下Java方法声明代表:
public int getValue(Fp fp,int left,int right);
现在,我们有了代表函数指针int(*fp)(int left,intright)的接口Fp,但是还没有Fp的实现类。
public static classFpAdd implementsFp{
@Override
public intinvoke(intleft, intright) {
return left+right;
}
}
回调函数说明
原生函数可以通过函数指针实现函数回调,调用外部函数来执行任务。这就是策略模式。
JNA可以方便地模拟函数指针,把Java函数作为函数指针传递给原生函数,实现在原生代码中调用Java代码。
模拟指针
JNA可以模拟原生代码中的指针。Java和原生代码的类型映射表中的指针映射是这样的:
Java 类型
C 类型
原生表现
Buffer
Pointer
pointer
平台依赖(32或 64位指针)
pointer
array
32或 64位指针(参数/返回值)
邻接内存(结构体成员)
PointerType
pointer
和
Pointer
相同
原生代码中的数组,可以使用JNA中对应类型的数组来表示。
原生代码中的指针,可以使用Pointer类型,或者PointerType类型及它们的子类型来模拟。
Pointer代表原生代码中的指针。其属性peer就是原生代码中指针的地址。
我们不可以直接创建Pointer对象,但可以用它表示原生函数中的任何指针。
Pointer类有2个子类:Function, Memory。
Function类代表原生函数的指针,可以通过invoke(Class,Object[],Map)这一系列的方法调用原生函数。
Memory类代表的是堆中的一段内存,它也是我们可以创建的Pointer子类。创建一个Memory类的实例,就是在原生代码的内存区中分配一块指定大小的内存。这块内存会在GC释放这个Java对象时被释放。Memory类在指针模拟中会被经常用到。
PointerType类代表的是一个类型安全的指针。ByReference类是PointerType类的子类。ByReference类代表指向堆内存的指针。ByReference类非常简单。
public abstract class ByReference extends PointerType {
protected ByReference(int dataSize) {
setPointer(new Memory(dataSize));
}
}
ByReference类有很多子类,这些类都非常有用。
ByteByReference, DoubleByReference,FloatByReference, IntByReference, LongByReference, NativeLongByReference,PointerByReference, ShortByReference, W32API.HANDLEByReference,X11.AtomByReference, X11.WindowByReference
ByteByReference等类故名思议,就是指向原生代码中的字节数据的指针。
PointerByReference类表示指向指针的指针。
在JNA中模拟指针,最常用到的就是Pointer类和PointerByReference类。Pointer类代表指向任何东西的指针,PointerByReference类表示指向指针的指针。Pointer类更加通用,事实上PointerByReference类内部也持有Pointer类的实例。
PointerByReference类可以嵌套使用,它所指向的指针,本身可能也是指向指针的指针。PointerByReference类的源代码:
public class PointerByReference extends ByReference {
public PointerByReference() {
this(null);
}
public PointerByReference(Pointervalue) {
super(Pointer.SIZE);
setValue(value);
}
public void setValue(Pointer value) {
getPointer().setPointer(0,value);
}
public Pointer getValue() {
returngetPointer().getPointer(0);
}
}
可以看到,PointerByReference类的构造器做了如下工作:
1, 首先在堆中分配一个指针大小的内存,并用一个Pointer对象代表。PointerByReference类的实例持有这个Pointer对象。
2, 然后,这个堆上新创建的指针的值被设置为传入的参数的地址,也就是指向传入的Pointer对象。这样,新创建的Pointer对象就是指针的指针。
使用PointerByReference模拟指向指针的指针
假设我们有一个结构体UserStruct的实例userStruct,现在又有了一个指向userStruct对象的指针pUser。
为了得到UserStruct**指针在Java中的对等体,我们可以执行如下代码:
PointerByReferenceppUser=new PointerByReference(pUser);
这会在堆中创建一个指针pointer,然后把pUser指针的地址复制到pointer对象中,这样pointer也就是指向pUser的指针。Pointer对象就是代表UserStruct**类型的指针。可以使用ppUser.getPointer()方法返回pointer对象。
我们在Java和原生代码的类型映射表中曾经指出,PointerType和Pointer类型相同,都可以表示指针。PointerByReference类是PointerType类的子类,因此,ppUser对象也可以代表UserStruct**类型的指针。
模拟指针
下面,给大家展示一个完整的例子,展示如何使用Pointer和PointerByReference类型模拟各类原生指针。
C代码:
void sayUser(UserStruct* pUserStruct){
std::wcout.imbue(std::locale("chs"));
std::wcout<id<
std::wcout<name<
std::wcout<age<
}
void sayUser2(UserStruct** ppUserStruct){
//UserStruct** ppUserStruct=*pppUserStruct;
UserStruct* pUserStruct=*ppUserStruct;
sayUser(pUserStruct);
}
void sayUser3(UserStruct*** pppUserStruct){
//UserStruct**ppUserStruct=*pppUserStruct;
UserStruct** ppUserStruct=*pppUserStruct;
sayUser2(ppUserStruct);
}
然后发布这3个函数。
JNA中模拟:
在接口中添加方法:
public voidsayUser(UserStruct.ByReference struct);
public void sayUser2(PointerByReference ppUserStruct);
public void sayUser3(PointerpppUserStruct);
JNA中调用:
UserStruct pUserStruct2=new UserStruct();
pUserStruct2.id=new NativeLong(90);
pUserStruct2.age=99;
pUserStruct2.name=new WString("乔布斯");
pUserStruct2.write();
PointerpPointer=pUserStruct2.getPointer();
PointerByReferenceppUserStruct=new PointerByReference(pPointer);
System.out.println("使用ppUserStruct!!!!");
TestDll1.INSTANCE.sayUser2(ppUserStruct);
System.out.println("使用pppUserStruct!!!!");
PointerByReference pppUserStruct=newPointerByReference(ppUserStruct.getPointer());
TestDll1.INSTANCE.sayUser3(pppUserStruct.getPointer());
可以看到,我们能够使用Pointer或者PointerByReference来表示指向指针的指针。sayUser3中,我们使用了PointerByReference类的getPointer()方法返回了代表UserStruct***类型的指针。
事实上,如果public void sayUser3(Pointer pppUserStruct);定义成
public void sayUser3(PointerByReference pppUserStruct);也是可以的,只是调用时提供的参数变为pppUserStruct对象本身即可。
通过使用Pointer和PointerByReference类,我们可以模拟任何原生代码的指针。
类详解
setPointer()方法相当于pTr2=&ptr1;
setLong()方法相当于ptr2=&long;
getPointer(0)相当于 (void*) *ptr2;
取指针指向的值,返回的还是指针。
getLong(0)相当于 (long)*ptr2;
取指针指向的值,返回的是long类型的数据。
JNA打破了Java和原生代码原本泾渭分明的界限,实现了Java和原生代码的强强联合,在各自擅长的领域分工合作,快速解决问题。
Java可以方便地利用原生代码的优势:执行速度快,可以直接操作硬件,机器码不容易被破解等。
原生代码可以通过回调Java函数,利用Java的优势:开发效率高,自动内存管理,跨平台,类库丰富,网络功能强大,支持多种脚本语言等。
JNA为Java开发者打开了一扇通向广袤的原生代码世界的大门。
将参数声明为特殊类型,以传达通过引用传递它们的事实。因此,如果Delphi参数是这样的:
您可以将其映射到这样的Java函数:
并调用该函数:
用java后台调用博思财政软件的dll 库
这是dll文档对应函数的说明
3.4、转入开票数据
说明:根据博思票据预设的需求格式,转入博思开票界面,并可进行打印票据信息。
Function PZrPj
(ZrTxt:PChar;IsPrn:integer; PjLx,Bz,Res:PChar):integer;StdCall;
参数: ZrTxt : 票据数据
(格式详见:数据格式说明.txt 文件)
IsPrn : 0 表示显示转入票据信息后再进行开票
1 表示直接开票并打印
2 表示直接开票但不打印
PjLx : 票据类型
(传空表示取默认当前的票据类型开票,
不为空则指定类型)
Bz : 单据的备注 (可选的参数,可不传则转入无备注)
Res: 只作为存储返回字符
成功:票据类型号,票据号,金额,注册号(以,分格)
错误:(错误信息)
返回值: 参数Res的长度
示例1:var vRes:pChar;
SetLength(vRes,500);
//vRes变量宽度,请定义足够大以返回完整错误信息
PZrPj(SomeString,1,’001001’,’’,vRes)
返回值:25
vRes返回值:’成功:001001,085256,1200.3,闽财2004票’
表示:传入开票内容为SomeString,票据类型为’001001’,
备注为空
返回值:长度为25
开票成功,返回值为 票据类型为001001
票据号为085256
金额为1200.3元
注册号 闽财2004票
示例2:var vRes:pChar;
PZrPj(SomeString,1,’’,’张三’,vRes)
返回值: 31 vRes返回值:’错误:请先上报核销后才能开票据。’
表示: 传入开票内容为SomeString,票据类型为默认值,
备注为’张三’
返回值长度为31
开票不成功,返回值为不成功的错误信息
我在java上是获取不到res返回的值
以下是我实现的步骤
dll 接口
package com.synjones.bosi;
import com.sun.jna.Library;
import com.sun.jna.Native;
public interface BosiAPI extends Library{
BosiAPI insanceDll = Native.loadLibrary("KpJk.dll", BosiAPI.class);
int PConnect();//连接博思开票系统(方式一)
int PLoginSuccess();//判断是否登陆成功
int PZrPj(String ZrTxt,int IsPrn,String PjLx,String Bz,String Res);//转入开票数据
}
测试代码
package com.synjones.bosi;
import com.sun.jna.ptr.PointerByReference;
public class bositest {
public static void main(String[] args) {
// TODO Auto-generated method stub
int j = BosiAPI.insanceDll.PConnect();
System.out.println(j);
int i = BosiAPI.insanceDll.PLoginSuccess();
System.out.println(i);
kptest();
}
public static void kptest() {
System.out.println("开票测试");
String ZrTxt = "<&票据><&票据头>姓名=赵惠卿\t就诊号=12314\t保险编号=\t性别=女\t就诊科室=\t人员类别= \t医保类别=省医保\t付款方式=现金\t缴费流水号=2\t医保收据号=\t医疗费用总额= \t公务员补助支付=\t统筹金支付= \t个人现金支付= \t个人帐户支付= 大额补助支付= 上次账户余额= 个人账户余额=&票据头><&收费项目>收费项目=R01 计费数量=3 收费标准=100 金额=300&收费项目>&票据>";
PointerByReference pref = new PointerByReference();
String res = "";
int i = BosiAPI.insanceDll.PZrPj(ZrTxt, 1, "14004003", "这是备注", res);
System.out.println(i);
// Pointer p = pref.getValue();
// System.out.println(p);
// byte [] buffer = p.getByteArray(0,i);
// System.out.println(buffer);
System.out.println(res);
System.out.println("-------------------");
}
相关问题推荐
Statement的execute(String query)方法用来执行任意的SQL查询,如果查询的结果是一个ResultSet,这个方法就返回true。如果结果不是ResultSet,比如insert或者update查询,它就会返回false。我们可以通过它的getResultSet方法来获取ResultSet,或者通过getUpda...
忙的时候项目期肯定要加班 但是每天加班应该还不至于
虽然Java人才越来越多,但是人才缺口也是很大的,我国对JAVA工程师的需求是所有软件工程师当中需求大的,达到全部需求量的60%-70%,所以Java市场在短时间内不可能饱和。其次,Java市场不断变化,人才需求也会不断增加。马云说过,未来的制造业要的不是石油,...
工信部证书含金量较高。工信部是国务院的下属结构,具有发放资质、证书的资格。其所发放的证书具有较强的权威性,在全国范围内收到认可,含金量通常都比较高。 工信部证书,其含义也就是工信部颁发并承认的某项技能证书,是具有法律效力的,并且是国家认可的...
学Java好不好找工作?看学完Java后能做些什么吧。一、大数据技术Hadoop以及其他大数据处理技术都是用Java或者其他,例如Apache的基于Java 的 HBase和Accumulo以及ElasticSearchas。但是Java在此领域并未占太大空间,但只要Hadoop和ElasticSearchas能够成长壮...
就是java的基础知识啊,比如Java 集合框架;Java 多线程;线程的五种状态;Java 虚拟机;MySQL (InnoDB);Spring 相关;计算机网络;MQ 消息队列诸如此类
#{}和${}这两个语法是为了动态传递参数而存在的,是Mybatis实现动态SQL的基础,总体上他们的作用是一致的(为了动态传参),但是在编译过程、是否自动加单引号、安全性、使用场景等方面有很多不同,下面详细比较两者间的区别:1.#{} 是 占位符 :动态解析 ...
没问题的,专科学历也能学习Java开发的,主要看自己感不感兴趣,只要认真学,市面上的培训机构不少都是零基础课程,能跟得上,或是自己先找些资料学习一下。
1、反射对单例模式的破坏采用反射的方式另辟蹊径实例了该类,导致程序中会存在不止一个实例。解决方案其思想就是采用一个全局变量,来标记是否已经实例化过了,如果已经实例化过了,第 二次实例化的时候,抛出异常2、clone()对单例模式的破坏当需要实现单例的...
优点: 一、实例控制 单例模式会阻止其他对象实例化其自己的单例对象的副本,从而确保所有对象都访问唯一实例。 二、灵活性 因为类控制了实例化过程,所以类可以灵活更改实例化过程。 缺点: 一、开销 虽然数量很少,但如果每次对象请求引用时都要...
这个主要是看你数组的长度是多少, 比如之前写过的一个程序有个数组存的是各个客户端的ip地址:string clientIp[4]={XXX, xxx, xxx, xxx};这个时候如果想把hash值对应到上面四个地址的话,就应该对4取余,这个时候p就应该为4...
哈希表的大小 · 关键字的分布情况 · 记录的查找频率 1.直接寻址法:取关键字或关键字的某个线性函数值为散列地址。即H(key)=key或H(key) = a·key + b,其中a和b为常数(这种散列函数叫做自身函数)。...
哈希表的大小取决于一组质数,原因是在hash函数中,你要用这些质数来做模运算(%)。而分析发现,如果不是用质数来做模运算的话,很多生活中的数据分布,会集中在某些点上。所以这里最后采用了质数做模的除数。 因为用质数做了模的除数,自然存储空间的大小也用质数了...
是啊,哈希函数的设计至关重要,好的哈希函数会尽可能地保证计算简单和散列地址分布均匀,但是,我们需要清楚的是,数组是一块连续的固定长度的内存空间
解码查表优化算法,seo优化
1.对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。2.哈希值就是这个元素的位置。3.如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就...