Java通过JNA调用动态库
编写一个demo程序,通过导入JNA的jar包使用JNA来进行zpl动态库的调用,完成zpl打印机的打开端口,关闭端口,打印文本,条码以及二维码的功能。
1:下载Jna的jar包并且在项目中导入jar包文件
通过在mvn仓库下载对应版本的jar包文件,此项目下载的是5.5.0的jar包,之后将jar包放入项目目录中,注意jar包存放的位置非常关键!一开始我只将jar包放置在根目录中,导致后期在项目中调用jna库中的类时出现NOCLASSFOUND异常,困扰了我很长时间,因此在项目中规范存放并导入jar包很重要。
首先在工程项目的根目录中新建一个lib文件,并将jar包存放在lib文件夹中。
将jar包添加到构建路径中。右键使用的jar包,选择选择Build Path–>Add to Build Path。
此时工程目录下就多了一个referenced libraries目录,在该目录下有刚刚添加的jar包,这样jar包才是真正在项目中添加完毕。
2:编写接口类
要想通过JNA调用DLL动态库,那就需要新建一个接口类用来导出DLL动态库的方法。
首先导入JNA包中的一系列类用来加载dll动态库以及完成java函数与动态库中的c函数方法转换以及对应形参成员的类型映射装换实现,具体的c -> java映射下图所示。
![在这里插入图片描述](https://img-blog.csdnimg.cn/41e50027459f4b929be07faa67a741da.png
注意上述的java方法都是将dll库中需要使用的函数进行转换映射为相应的java函数方法以便java程序进行方法的调用。如果你只有相应动态库而没有源码无法得知动态库中的方法时可以通过以下方法进行dll动态库中的方法查看。
下图是相应的c函数形参数据类型与java形参数据类型的转换表。
通过以上实现即可完成dll动态库的调用以及相应dll库函数的转化成java方法实现。
编写Demo程序实现动态库函数的调用
这就需要通过调用java的控件类来完成gui界面的绘制,具体代码如下所示。通过导入上面的接口类,并调用接口类中的实例成员的方法即可实现函数调用。
在实现调用dll动态库函数的时候一定要注意参数装换有没有出现错误,这个一定要非常仔细检查,否则后期在通过java调用的时候会出现很多问题,有些到后期代码量增多的时候或许就不好排查了,因此在参照c ->java 映射表的时候需要仔细查看,还有一些数据类型映射实际还没有讲到,后期有机会会总结一下具体的动态库调用数据类型装换。
具体项目中出现的问题:
在调用打印函数时打印文本时发现调用成功,但是打印机并没有进行打印,后来发现在打印函数前后需要加上标签函数设置开始以及结束标签后才会开始正常打印。
在打印文本以及条码时候出现编码格式问题,导致打印出来的数据均是乱码状态,通过检查发现输入的数据类型应该要改成WString,改后可以成功打印。
package PrinterSdk;
import com.sun.jna.WString;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.UnsupportedEncodingException;
import PrinterSdk.DllUse;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
import com.sun.jna.ptr.IntByReference;
public class PrinterDemo {
private JFrame frame;
private JTextField barcode_data;
private JTextField qrcode_data;
private JTextField Text;
private JButton PrBar_btn;
private JButton PrQr_btn;
private JButton PrTx_btn;
private JButton openPort_btn;
private JButton closePort_btn;
private int port;
public PrinterDemo()
{
initialize();
}
private void initialize()
{
frame=new JFrame();
frame.setBounds(100, 100, 700, 550);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
frame.setTitle("PrinterDemo");
PrTx_btn=new JButton("打印文本");
PrTx_btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
try {
// IntByReference i=new IntByReference(0);
// int o1=DllUse.instanceDll.OpenPortEx(0, i, 9600, 0);
WString data = new WString(Text.getText().toString());//.getBytes("gb18030");
// int s=DllUse.instanceDll.StartFormat_ZPL(100,100,10000,100);
int o=DllUse.instanceDll.StartData_ZPL(2000, 2000);
// DllUse.instanceDll.SetDefaultFont_ZPL(o1, "D".getBytes("gb18030"), 9, 5);
// DllUse.instanceDll.SetDataFont_ZPL("C".getBytes("gb18030"), 10000, 10000, "N".getBytes("gb18030"));
// DllUse.instanceDll.DataOrientation_ZPL("N".getBytes("gb18030"));
String data1="no.1234567";
// byte[] data2=data1.getBytes("gb18030");
int s=DllUse.instanceDll.StartFormat_ZPL(200,200,40039,40039);
int i1=DllUse.instanceDll.PrintData_ZPL(data);
DllUse.instanceDll.EndFormat_ZPL(port);
System.out.println("文本:"+i1+" "+s);
// int status=DllUse.instanceDll.GetPrintStatues_USB(0);
// System.out.println("打印机状态:"+status);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
PrTx_btn.setBounds(500, 100, 100, 80);
frame.getContentPane().add(PrTx_btn);
PrBar_btn=new JButton("打印条码");
PrBar_btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
try {
// IntByReference i1=new IntByReference(0);
// int o1=DllUse.instanceDll.OpenPortEx(0, i1, 9600, 0);
DllUse.instanceDll.DataOrientation_ZPL("N".getBytes("gb18030"));
WString data = new WString(barcode_data.getText().toString());
WString data1 = new WString("Y");
// byte[] data=barcode_data.getText().getBytes("gb18030");
// CodeType type=CodeType.CODE_39;
// System.out.println(type);
int s1=DllUse.instanceDll.StartFormat_ZPL(200,200,40039,10000);
int i=DllUse.instanceDll.BarCode_ZPL(data,2, 100, data1);
DllUse.instanceDll.EndFormat_ZPL(port);
System.out.println("条码:"+i);
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
PrBar_btn.setBounds(500, 200, 100, 80);
frame.getContentPane().add(PrBar_btn);
PrQr_btn=new JButton("打印二维码");
PrQr_btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
try {
// IntByReference i1=new IntByReference(0);
// int o1=DllUse.instanceDll.OpenPortEx(0, i1, 9600, 0);
// int s1=DllUse.instanceDll.StartFormat_ZPL(10,10,100,100);
DllUse.instanceDll.DataOrientation_ZPL("N".getBytes("gb18030"));
WString data = new WString(qrcode_data.getText().toString());
// byte[] data=qrcode_data.getText().getBytes("gb18030");
int s=DllUse.instanceDll.StartFormat_ZPL(200,200,40039,10000);
int i=DllUse.instanceDll.QRCode_ZPL(data, "M".getBytes("gb18030"), 2, 8);
System.out.println("二维码:"+i+" "+s);
DllUse.instanceDll.EndFormat_ZPL(port);
} catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
PrQr_btn.setBounds(500,300,100,80);
frame.getContentPane().add(PrQr_btn);
openPort_btn=new JButton("open port");
openPort_btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
IntByReference i=new IntByReference(0);
port=DllUse.instanceDll.OpenPortEx(0, i, 9600, 0);
System.out.println("打开端口:"+port);
}
});
openPort_btn.setBounds(100, 170, 100, 40);
frame.getContentPane().add(openPort_btn);
closePort_btn=new JButton("close port");
closePort_btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
DllUse.instanceDll.ClosePort(port);
}
});
closePort_btn.setBounds(100, 270, 100, 40);
frame.getContentPane().add(closePort_btn);
Text=new JTextField();
Text.setBounds(300,120,150,40);
frame.getContentPane().add(Text);
Text.setColumns(10);
barcode_data=new JTextField();
barcode_data.setBounds(300,220,150,40);
frame.getContentPane().add(barcode_data);
barcode_data.setColumns(10);
qrcode_data=new JTextField();
qrcode_data.setBounds(300,320,150,40);
frame.getContentPane().add(qrcode_data);
qrcode_data.setColumns(10);
}
public static void main(String[] args) {
PrinterDemo window=new PrinterDemo();
window.frame.setVisible(true);
}
}
成功运行javaDemo程序并调用相应动态库成功。
将java程序打包成jar文件
方式一:
在工程根目录下新建一个MANIFEST.MF文件
文件内容是:
MAIN-CLASSK开头需要空格然后写main方法所在的主类的绝对名字,最后回车空出第四行。
点击项目文件选择export,并输入jar,选择JAR file。
选择你要导出的java程序以及第三方包,在JAR file出选择jar包要导出的位置以及jar包名字。
- Export generated class files and resources 表示只导出生成的.class文件和其他资源文件
- Export all output folders for checked projects 表示导出选中项目的所有文件夹
- Export java source file and resouces 表示导出的jar包中将包含你的源代码*.java,如果你不想泄漏源代码,那么就不要选这项了
- Export refactorings for checked projects 把一些重构的信息文件也包含进去
之后选择自己创建的MANIFEST文件,点击finish完成。
方式二:
选择导出runnable jar
关于library handling部分的解释如下:
(1)Extract required libraries into generated JAR。
把所有的import JAR都拆开来,包含在JAR的各个目录中,ex. net/org/xxx.class
(2)Package required libraries into generated JAR。
把所有的import JAR都包在JAR的根目录下
(3)Copy required libraries into a sub-folder next to the generated JAR。
把所有import JAR放在JAR外面独立的一个文件夹
通过上述两种方式就可以将java程序打包成jar文件,并且可以通过终端调用jar文件进行程序的运行。