java与Delphi写的dll交互

有时候在项目开发的时候难免会和硬件提供的开发包接触,这些开发接口不是c写的就是Delphi写的。

如下是一个Delphi开发的dll,里面以各种接口方法进行java调用测试。大部分映射写法都能够找到。有时间会继续补充。

Delphi7工具下载地址:

http://big2.139.xdowns.com/b/BorlandDelphi7.zip
汉化包下载地址:

ftp://122.227.29.237/delphifans2009/tools/delphi7chs.rar
附上一些资料。便于读懂源码。以下例子足够java和Delphi交互使用。希望对大家有帮助。

新建一个dll很容易,通过向导就可以创建,下面写好的dll,内容如下:

library FirstDLL;

{ Important note about DLL memory management: ShareMem must be the
  first unit in your library's USES clause AND your project's (select
  Project-View Source) USES clause if your DLL exports any procedures or
  functions that pass strings as parameters or function results. This
  applies to all strings passed to and from your DLL--even those that
  are nested in records and classes. ShareMem is the interface unit to
  the BORLNDMM.DLL shared memory manager, which must be deployed along
  with your DLL. To avoid using BORLNDMM.DLL, pass string information
  using PChar or ShortString parameters. }

uses
  SysUtils,
  Classes;

{$R *.res}

type Date =record
     Year:Word;
     Month:Byte;
     Day:Integer;
end;
PDate = ^Date;

TRec_1 = record
  a: Word;//2字节  449218,449219
  b: Byte;//1字节  449220,449221(补空),实际占2字节
  c: Word;//2字节  449222,449222
  //0x0000-0x0004共6个字节
end;
TRec_2 = record
  a: Byte;//1字节 449192
  b: Byte;//1字节 449193 449194 449195 449196 449197 449198 449199(4,9补空直到449200被8整除),所以实际占7字节
  c: Double;//8字节 449200
  //共16字节
end;
Date_1 =record
     Year:Word; //2字节,449208,449209
     Month:Byte;//1字节,449210,449211(补空) ,实际占2字节
     Day:Integer;//4字节,449212,449213,449214,449214
     //共8个字节
end;
//加了packed按照实际字节来对齐
Date_2 =packed record
     Year:Word; //2字节,449201,449202
     Month:Byte;//1字节,449203
     Day:Integer;//4字节,449204,449205,449206,449207
     //共7个字节
end;
Date_3 =record
     Year:Word; //2字节 449264 449265 449266 449267实际占4字节
     Day:Integer;//4字节 449268 449269 449270 449271
     Month:Byte;//1字节 449272 449273 449274 449275(补3个) 
    //共9个字节,4的倍数,所以12字节
end;

//Student和SFRCDA除了packed,其他都一样
//但是java类型映射的写法差别很大
type Student=packed record
            kh:Integer;//4字节 449249,449250,449251,449252
            mu:Byte;//1字节  449253
            qty:Smallint;//2字节 449254 449255
            je,ye:Integer;//8字节 449256 449257 449258 449259 449260 449261 449262 449263
            year:Word;//2字节 449264 449265
            month,day,hour,minute,second,ms,sd:Byte;//7字节 449266 449267 449268 449269 449270 449271 449272
            jh:Byte;//1字节 449273
            lb:Byte;//1字节 449274
            online:Byte;//1字节 449275
            //共27个字节
          end;
PStudent=^Student;
//packed结构压缩,按照实际对齐
type SFRCDA=record
            kh:Integer;
            mu:Byte;
            qty:Smallint;
            je,ye:Integer;
            year:Word;
            month,day,hour,minute,second,ms,sd:Byte;
            jh:Byte;
            lb:Byte;
            online:Byte;
          end;
PSFRCDA=^SFRCDA;

type User=packed record
          userName:PChar;
          age:Integer;
          intA:array[0..10] of Integer;
          byteA:array[0..1] of Byte;
          charA:array[0..2] of Char;
end;
PUser=^User;

//下面是一个结构体嵌套测试。
//个性参数结构体类型。TMD,真够多。
type SFPRDA = packed record
 czmm:Word;
 sqcz:array[1..8] of packed record dm:Byte;mm:Word end;
 sdje:array[0..3] of packed record dz,jz:Word end;
 open_xffs:Byte;
 open_menu:Byte;
 open_online:Byte;
 para:array[0..7] of packed record
    xffs:array[0..3] of packed record
      dzlx_1:Byte;
      dzje_1:Word;
      jzlx_1:Byte;
      jzje_1:Word;
      dzlx_2:Byte;
      dzje_2:Word;
      jzlx_2:Byte;
      jzje_2:Word;
    end;
    zk:array[0..3] of Shortint;
 end;
end;
PSFPRDA=^SFPRDA;


type SFMUDA=array[1..2] of Word;
PSFMUDA=^SFMUDA;

type SFMTDA = array[0..2] of packed record
  cs_sd:Word;
  je_sd:Integer;
  cs_day:Word;
  je_day:Integer;
end;
PSFMTDA = ^SFMTDA;

function CallSFMTDA(sfmt:PSFMTDA):Integer;stdcall;
begin
  sfmt[0].cs_sd := 65535;
  sfmt[0].je_sd := 70000;
  sfmt[0].cs_day := 60000;
  sfmt[0].je_day := 80000;

  sfmt[1].cs_sd := 60001;
  sfmt[1].je_sd := 70001;
  sfmt[1].cs_day := 60001;
  sfmt[1].je_day := 80001;

  sfmt[2].cs_sd := 60002;
  sfmt[2].je_sd := 70002;
  sfmt[2].cs_day := 60002;
  sfmt[2].je_day := 80002;
  

  Result := 0;
end;

function CallSFMUDA(mu:PSFMUDA;black:PInteger):Integer;stdcall;
begin
  Writeln('mu[1]=',mu[1]);
  Writeln('mu[2]=',mu[2]);

  Writeln('black0=',black^);
  Inc(black);
  Writeln('black1=',black^);
  Inc(black);
  Writeln('black2=',black^);
  Result := 0;
end;


function CallSFPRDA(pr:PSFPRDA):Integer;stdcall;
var i:Integer;
begin
  Writeln('czmm=',pr.czmm);
    for i := 1 to 8 do
    begin
      Writeln('pr.sqcz[',i,'].dm=', pr.sqcz[i].dm);
      Writeln('pr.sqcz[',i,'].dm=', pr.sqcz[i].mm);
      Writeln('*****************************************');
    end;
    for i := 0 to 3 do
    begin
      Writeln('pr.sdje[',i,'].dz=', pr.sdje[i].dz);
      Writeln('pr.sdje[',i,'].jz=', pr.sdje[i].jz);
      Writeln('*****************************************');
    end;
    Writeln('open_xffs=',pr.open_xffs);
    Writeln('open_menu=',pr.open_menu);
    Writeln('open_online=',pr.open_online);
    Writeln('*****************************************');
    Writeln('pr.para[0].zk[3]=',pr.para[0].zk[3]);
  Result := 0;
end;

function CallUser(user:PUser):Integer;stdcall;
var i:Integer;
begin
  user.age := 22;
  user.userName := 'forever';
  for i := 0 to High(user.intA) do
  begin
     user.intA[i] := i*2;
  end;
  for i := 0 to High(user.byteA) do
  begin
     user.byteA[i] := i;
   end;
    user.charA[0] := 'a';
    user.charA[1] := 'b';
    user.charA[2] := 'c';
  Result := 0;
end;

function CallSFRCDA(sfrc:PSFRCDA):Integer;stdcall;
begin
  sfrc.kh := 9385295;
  sfrc.mu := 255;
  sfrc.qty := 32767;
  sfrc.je := -2000;
  sfrc.ye := 4000;
  sfrc.year := 2010;
  sfrc.month := 09;
  sfrc.day := 8;
  sfrc.hour := 18;
  sfrc.minute := 22;
  sfrc.second := 44;
  sfrc.ms :=22;
  sfrc.sd :=3;
  sfrc.jh :=4;
  sfrc.lb := 31;
  sfrc.online := 1;
  Result := 0;
end;

function CallStudent(student:PStudent):Integer;stdcall;
begin
  student.kh := 9385295;
  student.mu := 255;
  student.qty := 32767;
  student.je := -2000;
  student.ye := 4000;
  student.year := 2010;
  student.month := 09;
  student.day := 8;
  student.hour := 18;
  student.minute := 22;
  student.second := 44;
  student.ms :=22;
  student.sd :=3;
  student.jh :=4;
  student.lb := 31;
  student.online := 1;
  Result := 0;
end;

function CallStuArray(student:PStudent;var size:Integer):Integer;stdcall;
var i:Integer;
begin
  for i := 0 to 2 do
  begin
    student.kh := 9385295;
    student.mu := 255;
    student.qty := 32767;
    student.je := -2000;
    student.ye := 4000;
    student.year := 2010;
    student.month := 09;
    student.day := 8;
    student.hour := 18;
    student.minute := 22;
    student.second := 44;
    student.ms :=22;
    student.sd :=3;
    student.jh :=4;
    student.lb := 31;
    student.online := 1;
    Inc(student);
  end;
  size := 3;

  


  Result := 0;
end;


function CallByteSize():Integer;stdcall;
var t1:TRec_1;
var t2:TRec_2;
var d1:Date_1;
var d2:Date_2;
var d3:Date_3;
var sfrc:SFRCDA; //这里可以改成Student试试
begin
  Writeln(Format('t1字节长度:%d',[SizeOf(t1)]));
  Writeln(Format('t1中的a的地址是:%d',[Integer(@t1.a)]));
  Writeln(Format('t1中的b的地址是:%d',[Integer(@t1.b)]));
  Writeln(Format('t1中的c的地址是:%d',[Integer(@t1.c)]));
  Writeln('**************************************************');
  Writeln(Format('t2字节长度:%d',[SizeOf(t2)]));
  Writeln(Format('t2中的a的地址是:%d',[Integer(@t2.a)]));
  Writeln(Format('t2中的b的地址是:%d',[Integer(@t2.b)]));
  Writeln(Format('t2中的c的地址是:%d',[Integer(@t2.c)]));
  Writeln('**************************************************');
  Writeln(Format('d1字节长度:%d',[SizeOf(d1)]));
  Writeln(Format('d1中的Year的地址是:%d',[Integer(@d1.Year)]));
  Writeln(Format('d1中的Month的地址是:%d',[Integer(@d1.Month)]));
  Writeln(Format('d1中的Day的地址是:%d',[Integer(@d1.Day)]));
  Writeln('**************************************************');
  Writeln(Format('d2字节长度:%d',[SizeOf(d2)]));
  Writeln(Format('d2中的Year的地址是:%d',[Integer(@d2.Year)]));
  Writeln(Format('d2中的Month的地址是:%d',[Integer(@d2.Month)]));
  Writeln(Format('d2中的Day的地址是:%d',[Integer(@d2.Day)]));
  Writeln('**************************************************');
  Writeln(Format('d3字节长度:%d',[SizeOf(d3)]));
  Writeln(Format('d3中的Year的地址是:%d',[Integer(@d3.Year)]));
  Writeln(Format('d3中的Month的地址是:%d',[Integer(@d3.Month)]));
  Writeln(Format('d3中的Day的地址是:%d',[Integer(@d3.Day)]));
  Writeln('**************************************************');
  Writeln(Format('sfrc字节长度:%d',[SizeOf(sfrc)]));
  Writeln(Format('sfrc中的kh的地址是:%d',[Integer(@sfrc.kh)]));
  Writeln(Format('sfrc中的mu的地址是:%d',[Integer(@sfrc.mu)]));
  Writeln(Format('sfrc中的qty的地址是:%d',[Integer(@sfrc.qty)]));
  Writeln(Format('sfrc中的je的地址是:%d',[Integer(@sfrc.je)]));
  Writeln(Format('sfrc中的ye的地址是:%d',[Integer(@sfrc.ye)]));
  Writeln(Format('sfrc中的year的地址是:%d',[Integer(@sfrc.year)]));
  Writeln(Format('sfrc中的month的地址是:%d',[Integer(@sfrc.month)]));
  Writeln(Format('sfrc中的day的地址是:%d',[Integer(@sfrc.day)]));
  Writeln(Format('sfrc中的hour的地址是:%d',[Integer(@sfrc.hour)]));
  Writeln(Format('sfrc中的minute的地址是:%d',[Integer(@sfrc.minute)]));
  Writeln(Format('sfrc中的second的地址是:%d',[Integer(@sfrc.second)]));
  Writeln(Format('sfrc中的ms的地址是:%d',[Integer(@sfrc.ms)]));
  Writeln(Format('sfrc中的sd的地址是:%d',[Integer(@sfrc.sd)]));
  Writeln(Format('sfrc中的jh的地址是:%d',[Integer(@sfrc.jh)]));
  Writeln(Format('sfrc中的lb的地址是:%d',[Integer(@sfrc.lb)]));
  Writeln(Format('sfrc中的online的地址是:%d',[Integer(@sfrc.online)]));
  Writeln('**************************************************');
  Result := 0;
end;

function CallDate(date:PDate;outDate:PDate):Integer;stdcall;

begin

  outDate.Year := date.Year;
  outDate.Month := date.Month;
  outDate.Day := date.Day;
  Result := 0;
end;

function GetSum(a,b:Integer):Integer;stdcall;
begin
  Result := a + b ;
end;

function CallVRef(a,b:Integer;var c:Integer):Integer;stdcall;
begin
  c := a + b;
  Result := 0;
end;

function CallPChar(a,b:PChar):PChar;stdcall;
begin
  Result := PChar(a+''+b);
end;

function CallType(
           var kh:Integer;
           var mu:Byte;
           var qty:Smallint;
           var je,ye:Integer;
           var year:Word;
           var month,day,hour,minute,second,ms,sd:Byte;
           var jh:Byte;
           var lb:Byte;
           var online:Byte
):Integer;stdcall;
begin
  kh := 9385295;
  mu := 255;
  qty := 32767;
  je := -2000;
  ye := 4000;
  year := 2010;
  month := 09;
  day := 8;
  hour := 18;
  minute := 22;
  second := 44;
  ms :=22;
  sd :=3;
  jh :=4;
  lb := 31;
  online := 1;
  Result := 0;
end;

function CallPointer(
pint:PInteger;
pbyte:PByte;
pchar:PChar;
outPchar:PChar):Integer;stdcall;
var i:Integer;
var s:string;
begin
 for i := 0 to 3 do
 begin
     pint^ := i*2;
     Inc(pint);
 end;
 for i := 0 to 3 do
 begin
     pbyte^ := i*2;
     Inc(pbyte);
 end;

 pchar^ := 'a';
 Inc(pchar,1);
 pchar^ := 'b';
 Inc(pchar,1);
 pchar^ := 'c';
 s := 'Delphi字符串输出内容';
 StrPCopy(outPchar,s);
 Result := 0;
end;

function SfRead(kh,kx,srv:Integer;var dj,jb,mm,ye:Integer;bz:PChar):Integer;stdcall;
var s:string;
begin
  dj := 1;
  jb := 0;
  mm := 8888;
  ye := 100000;
  s := 'bz备注信息';
  StrPCopy(bz,s);
  Result := 0 ;
end;

exports
GetSum,
CallVRef,
CallPChar,
CallType,
CallDate,
CallSFRCDA,
CallStudent,
CallByteSize,
CallPointer,
CallUser,
SfRead,
CallStuArray,CallSFPRDA,CallSFMUDA,
CallSFMTDA;
begin

end.

 

 

源码不多介绍。看哈资料应该就能看懂了。

java接口对应内容:

package org.forever.delphi.dll;

import com.sun.jna.Native;
import com.sun.jna.Structure;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.ShortByReference;
import com.sun.jna.win32.StdCallLibrary;

/**
 * java调用Delphi写的dll进行交互
 * @author 陈均
 *
 */
public interface IDelphiFirstDLL extends StdCallLibrary{
	//绝对路径,相对路径只需名字即可
	IDelphiFirstDLL INSTANCE = (IDelphiFirstDLL)Native.loadLibrary("H:\\一卡通开发资料\\DelphiFirstDLL\\FirstDLL.dll", IDelphiFirstDLL.class);
	
	/**
	 * 普通的值传递测试
	 * @param a
	 * @param b
	 * @return 返回两数之和
	 */
	public int GetSum(int a,int b);
	
	/**
	 * 传入引用参数的测试
	 * @param a 
	 * @param b
	 * @param c 返回 a+b的和
	 * @return 0表示成功
	 */
	public int CallVRef(int a,int b,IntByReference c);
	
	/**
	 * Delphi各种基本类型的指针传递测试
	 * Delphi中的char只有1字节,java的char是2字节,所以用byte去对应。
	 * @param pint 对应Delphi中的pint:PInteger 整形数组
	 * @param pbyte 对应Delphi中的pbyte:PByte; 字节数组
	 * @param pchar 对应Delphi中的pchar:PChar;这里表示返回字符数组
	 * @param outPchar 对应Delphi中的outPchar:PChar,这里表示返回字符串
	 * @return
	 */
	public int CallPointer(
			int[]pint,
			byte[]pbyte,
			byte[] pchar,
			byte[] outPchar);
	
	/**
	 * 传入字符串测试
	 * @param a
	 * @param b
	 * @return 返回两个字符串拼接结果
	 */
	public String CallPChar(String a,String b);
	
	/**
	 * 传入各种基本类型的引用测试
	 * @return 0表示成功
	 */
	public int CallType(
		IntByReference kh,
		UnsignedByteByReference mu,
		ShortByReference qty,
		IntByReference je,
		IntByReference ye,
		UnsignedShortByReference year,
		UnsignedByteByReference month,
		UnsignedByteByReference day,
		UnsignedByteByReference hour,
		UnsignedByteByReference minute,
		UnsignedByteByReference second,
		UnsignedByteByReference ms,
		UnsignedByteByReference sd,
		UnsignedByteByReference jh,
		UnsignedByteByReference lb,
		UnsignedByteByReference online
	);
	
	/**
	 * 日期结构体类型
	 * @author 陈均
	 *
	 */
	public class Date extends Structure{
		public short Year;
		public byte Month;
		public int Day;
		
	}
	
	/**
	 * 传入结构体类型
	 * @param date 结构体类型日期
	 * @param outDate 将date的数据copy到outDate里面
	 * @return 0表示成功
	 */
	public int CallDate(Date date,Date outDate);
	
	/**
	 * 传入结构体类型参数测试
	 * @param sfrc 方法执行完后获取所需要的值
	 * @return 0表示成功
	 */
	public int CallSFRCDA(SFRCDA sfrc);
	
	/**
	 * 传入结构类型参数测试,这个结构体式packed
	 * @param student 方法执行完后获取所需要的值
	 * @return 返回0表示成功
	 */
	public int CallStudent(Student student);
	
	/**
	 * 传入结构类型数组参数测试,这个结构体式packed
	 * @param students 结构体数组
	 * @param size 返回实际数据大小
	 * @return 返回0表示成功
	 */
	public int CallStuArray(Student[]students,IntByReference size);
	
	/**
	 * 测试各种类型字节长度
	 * @return 返回0表示成功
	 */
	public int CallByteSize();
	
	/**
	 * 带数组的结构体调用测试
	 * @param user
	 * @return 返回0表示成功
	 */
	public int CallUser(User user);
	
	/**
	 * 调用嵌套结构体参数类型测试
	 * @param pr 结构体参数
	 * @return 返回0表示成功。
	 */
	public int CallSFPRDA(SFPRDA pr);
	
	/**
	 * 调用只含数组的结构体测试。
	 * @param mu
	 * @return 返回0表示成功。
	 */
	public int CallSFMUDA(SFMUDA mu,int[]black);
	
	/**
	 * 如果只含有数组的这种结构体也可以直接传字节数组。但长度一定要对
	 * @param mu 结构体指针字节数组
	 * @param black 整形指针数组
	 * @return 返回0表示成功。
	 */
	public int CallSFMUDA(byte[] mu,int[]black);
	
	/**
	 * sfmt为指向SFMTDA结构的指针,用于存放返回数据
	 * @param sfmt
	 * @return 返回0表示成功。
	 */
	public int CallSFMTDA(SFMTDA sfmt);
	
	/**
	 * 测试值传递和返回参数传递和返回字符串内容
	 * @return 0表示成功
	 */
	public int SfRead(int kh,int kx,int srv,IntByReference dj,IntByReference jb,
			IntByReference mm,IntByReference ye,byte[] bz);
}

 

 

所需测试的结构体类型SFRCDA:

package org.forever.delphi.dll;

import com.sun.jna.Structure;

/**
 * 结构体类型
 * 注意:
 * 当Delphi得结构体不是packed record的时候,下面映射才正确,可以对照Student分析,
 * 如果delphi里面用的是Byte无符号1字节类型,则用java的byte对应,
 * 取值的时候要进行判断,
 * 如果值大于0就是本身,
 * 如果值小于0就是mu&0xff,
 * 如果delphi里面用的是Word无符号2字节类型,则用java的short对应,
 * 取值的时候要进行判断,
 * 如果值大于0就是本身,
 * 如果值小于0就是year&0xffff.
 * */
public class SFRCDA extends Structure{
	public int kh;
	public byte mu;
	public short qty;
	public int je;
	public int ye;
	public short year;
	public byte month;
	public byte day;
	public byte hour;
	public byte minute;
	public byte second;
	public byte ms;
	public byte sd;
	public byte jh;
	public byte lb;
	public byte online;
}

 结构体SFMTDA类:

package org.forever.delphi.dll;

import com.sun.jna.Structure;

public class SFMTDA extends Structure{

	/**对以下结构体进行映射。
	 * type SFMTDA = array[0..2] of packed record
	 *	  cs_sd:Word;
	 *	  je_sd:Integer;
	 *	  cs_day:Word;
	 *	  je_day:Integer;
	 *	end;
	 *	PSFMTDA = ^SFMTDA;
	 *
	 * 3 * (2+4+2+4) = 36;
	 * 每12个字节为一个结构体对象。
	 * 变量以 2 4 2 4划分即可。
	 */
	
	public byte[] sfmt = new byte[36];
	
	public class Item{
		public int cs_sd;
		public int je_sd;
		public int cs_day;
		public int je_day;
	}
	
	/**
	 * 通过下标访问数据
	 * @param index 索引。范围在0-3不包含3
	 * @return 返回查找到的数据
	 */
	public Item get(int index){
		if(index<0 || index >2){
			throw new RuntimeException("index="+index+"数组下标越界!");
		}
		int startP = 12*index;//起始位置
		byte[] cs_sd = new byte[2];
		byte[] je_sd = new byte[4];
		byte[] cs_day = new byte[2];
		byte[] je_day = new byte[4];
		
		cs_sd[0] = sfmt[startP];
		cs_sd[1] = sfmt[startP+1];
		
		je_sd[0] = sfmt[startP+2];
		je_sd[1] = sfmt[startP+3];
		je_sd[2] = sfmt[startP+4];
		je_sd[3] = sfmt[startP+5];
		
		cs_day[0] = sfmt[startP+6];
		cs_day[1] = sfmt[startP+7];
		
		je_day[0] = sfmt[startP+8];
		je_day[1] = sfmt[startP+9];
		je_day[2] = sfmt[startP+10];
		je_day[3] = sfmt[startP+11];
		
		Item item = new Item();
		item.cs_sd = DataUtil.byteArrayToUnsignedShort(cs_sd);
		item.je_sd = DataUtil.byteArrayToInt(je_sd);
		item.cs_day = DataUtil.byteArrayToUnsignedShort(cs_day);
		item.je_day = DataUtil.byteArrayToInt(je_day);
		
		return item;
		
	}
	
	
}

 多层结构体SFPRDA类:

package org.forever.delphi.dll;

import com.sun.jna.Structure;

/**
 * 多层嵌套结构体类型;
 * 将Delphi里面的匿名结构体换算成字节即可
 * @author 陈均
 */
public class SFPRDA extends Structure{
	public byte[] czmn = new byte[2];//Word
	
	/**对应Delphi中的array[1..8] of packed record dm:Byte;mm:Word end;<br/>
	 * 8 * (1+2) = 24
	 */
	public byte[] sqcz = new byte[24];
	/**对应Delphi中的array[0..3] of packed record dz,jz:Word end;<br/>
	 *	4 * (2+2) = 16;
	 */
	public byte[] sdje = new byte[16];
	public byte open_xffs;//Byte
	public byte open_menu;//Byte
	public byte open_online;//Byte
	/**对应Delphi中的<br/>
	 *	para:array[0..7] of packed record<br/>
	 *   xffs:array[0..3] of packed record<br/>
	 *     dzlx_1:Byte;<br/>
	 *     dzje_1:Word;<br/>
	 *     jzlx_1:Byte;<br/>
	 *     jzje_1:Word;<br/>
	 *     dzlx_2:Byte;<br/>
	 *     dzje_2:Word;<br/>
	 *     jzlx_2:Byte;<br/>
	 *     jzje_2:Word;<br/>
	 *   end;<br/>
	 *   zk:array[0..3] of Shortint;<br/>
	 *   end;<br/>
  	 *  8 * (4*(1+2+1+2+1+2+1+2)+4) = 416;长度	
    */
	public byte[] para = new byte[416];
}





 

结构体Student类:

package org.forever.delphi.dll;

import com.sun.jna.Structure;

public class Student extends Structure{
	public int kh;
	public byte mu;
	public byte[] qty = new byte[2];
	public byte[] je = new byte[4];
	public byte[] ye = new byte[4];
	public byte[] year = new byte[2];
	public byte month;
	public byte day;
	public byte hour;
	public byte minute;
	public byte second;
	public byte ms;
	public byte sd;
	public byte jh;
	public byte lb;
	public byte online;
	
}

 

结构体User类:

package org.forever.delphi.dll;

import com.sun.jna.Structure;

public class User extends Structure {
	
	public String userName;
	public byte[] age = new byte[4];
	//对应着Delphi中array[0..10] of Integer;11*4=44
	public byte[]intA = new byte[44];
	//对应着Delphi中array[0..1] of Byte;2*1=2;
	public byte[] byteA = new byte[2];
	//对应着Delphi中array[0..2] of Char;3*1=2;
	public byte[] charA = new byte[3];
	
	
}

 

  测试文件 DelphiFirstDLLTest:

package org.forever.delphi.dll;


import org.forever.delphi.dll.IDelphiFirstDLL.Date;
import org.forever.delphi.dll.SFMTDA.Item;
import org.forever.delphi.dll.SFRCDA;
import org.forever.delphi.dll.Student;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.ShortByReference;
import static org.forever.delphi.dll.DataUtil.byteArrayToInt;
import static org.forever.delphi.dll.DataUtil.byteArrayToShort;
import static org.forever.delphi.dll.DataUtil.byteArrayToString;
import static org.forever.delphi.dll.DataUtil.byteArrayToUnsignedShort;
import static org.forever.delphi.dll.DataUtil.byteToUnsignedByte;
import static org.forever.delphi.dll.DataUtil.unsignedShortToByteArray;

public class DelphiFirstDLLTest {

	public static void main(String[] args) {
		IDelphiFirstDLL delphiFirstDLL = IDelphiFirstDLL.INSTANCE;
		int a = 4;
		int b = 3;
		int state;
		int result;
		result = delphiFirstDLL.GetSum(a, b);
		assert result==7;
		
		IntByReference c = new IntByReference();
		state = delphiFirstDLL.CallVRef(a, b, c);
		assert state==0 && c.getValue()==7;
		
		String str1 = " come ";
		String str2 = " on ";
		String msg = delphiFirstDLL.CallPChar(str1, str2);
		assert msg.equals(str1 + str2);
		
		IntByReference kh = new IntByReference();
		UnsignedByteByReference mu = new UnsignedByteByReference();
		ShortByReference qty = new ShortByReference();
		IntByReference je = new IntByReference();
		IntByReference ye = new IntByReference();
		UnsignedShortByReference year = new UnsignedShortByReference();
		UnsignedByteByReference month = new UnsignedByteByReference();
		UnsignedByteByReference day = new UnsignedByteByReference();
		UnsignedByteByReference hour = new UnsignedByteByReference();
		UnsignedByteByReference minute = new UnsignedByteByReference();
		UnsignedByteByReference second = new UnsignedByteByReference();
		UnsignedByteByReference ms = new UnsignedByteByReference();
		UnsignedByteByReference sd = new UnsignedByteByReference();
		UnsignedByteByReference jh = new UnsignedByteByReference();
		UnsignedByteByReference lb = new UnsignedByteByReference();
		UnsignedByteByReference online = new UnsignedByteByReference();
		state = delphiFirstDLL.CallType(kh, mu, qty, je, ye, year, month, day, hour, minute, second, ms, sd, jh, lb, online);
		assert kh.getValue()==9385295
		&& mu.getValue()==255
		&& qty.getValue()==32767
		&& je.getValue()==-2000
		&& ye.getValue()==4000
		&& year.getValue()==2010
		&& month.getValue()==9
		&& day.getValue()==8
		&& hour.getValue()==18
		&& minute.getValue()==22
		&& second.getValue()==44
		&& ms.getValue()==22
		&& sd.getValue()==3
		&& jh.getValue()==4
		&& lb.getValue()==31
		&& online.getValue()==1
		&& state==0;
		
		Date date = new Date();
		date.Year = 2010;
		date.Month = 9;
		date.Day = 8;
		Date outDate = new Date();
		state = delphiFirstDLL.CallDate(date,outDate);
		assert state==0
		&& outDate.Year==date.Year
		&& outDate.Month==date.Month
		&& outDate.Day==date.Day;
		
		SFRCDA sfrc = new SFRCDA();
		state = delphiFirstDLL.CallSFRCDA(sfrc);
		assert state==0
		&& sfrc.kh==9385295
		&&(sfrc.mu<0 && byteToUnsignedByte(sfrc.mu)==255)
		&& sfrc.qty==32767
		&& sfrc.je==-2000
		&& sfrc.ye==4000
		&& sfrc.year==2010
		&& sfrc.month==9
		&& sfrc.day==8
		&& sfrc.hour==18
		&& sfrc.minute==22
		&& sfrc.second==44
		&& sfrc.ms==22
		&& sfrc.sd==3
		&& sfrc.jh==4
		&& sfrc.lb==31
		&& sfrc.online==1;
		
		Student student = new Student();
		state = delphiFirstDLL.CallStudent(student);
		assert state==0
		&& byteArrayToInt(student.kh)==9385295
		&& byteToUnsignedByte(student.mu)==255
		&& byteArrayToShort(student.qty)==32767
		&& byteArrayToInt(student.je)==-2000
		&& byteArrayToInt(student.ye)==4000
		&& byteArrayToUnsignedShort(student.year)==2010
		&& byteToUnsignedByte(student.month)==9
		&& byteToUnsignedByte(student.day)==8
		&& byteToUnsignedByte(student.hour)==18
		&& byteToUnsignedByte(student.minute)==22
		&& byteToUnsignedByte(student.second)==44
		&& byteToUnsignedByte(student.ms)==22
		&& byteToUnsignedByte(student.sd)==3
		&& byteToUnsignedByte(student.jh)==4
		&& byteToUnsignedByte(student.lb)==31
		&& byteToUnsignedByte(student.online)==1;
		
		int[]pint = new int[3];
		byte[]pbyte = new byte[3];
		byte[]pchar = new byte[3];
		byte[]outPchar = new byte[20];
		state = delphiFirstDLL.CallPointer(pint,pbyte,pchar,outPchar);
		assert state==0;
		assert pint[0]==0 && pint[1]==2 && pint[2]==4;
		assert pbyte[0]==0 && pbyte[1]==2 && pbyte[2]==4;
		assert pchar[0]==97 && pchar[1]==98 && pchar[2]==99;
		assert new String(outPchar).trim().equals("Delphi字符串输出内容");
		
		
		User user = new User();
		state = delphiFirstDLL.CallUser(user);
		assert state==0
		&& user.userName.equals("forever")
		&& byteArrayToInt(user.age)==22;
		for (int i = 0,j=0; i < user.intA.length; i+=4,j++) {
			int iVal = byteArrayToInt(new byte[]{user.intA[i],user.intA[i+1],user.intA[i+2],user.intA[i+3]});
			assert iVal== j * 2;
		}
		
		for (int i = 0; i < user.byteA.length; i++) {
			assert user.byteA[i]==i;
		}
		
		assert (char)user.charA[0]=='a'
			&&(char)user.charA[1]=='b'
			&&(char)user.charA[2]=='c';
		
		
		int kah = 9385292;
		int kx = 0;
		int srv = 1;
		IntByReference dj = new IntByReference();
		IntByReference jb = new IntByReference();
		IntByReference mm = new IntByReference();
		IntByReference yue = new IntByReference();
		byte[] bz = new byte[200];
		state = delphiFirstDLL.SfRead(kah, kx, srv, dj, jb, mm, yue, bz);
		assert state == 0
		&& dj.getValue()==1
		&& jb.getValue()==0
		&& mm.getValue()==8888
		&& yue.getValue()==100000
		&& byteArrayToString(bz).equals("bz备注信息");
		
		IntByReference size = new IntByReference();
		Student[]students = new Student[4];
		state = delphiFirstDLL.CallStuArray(students, size);
		assert state == 0 && size.getValue()==3;
		for (int i = 0; i < size.getValue(); i++) {
			Student item = students[1];
			assert byteArrayToInt(item.kh)==9385295
			&& byteToUnsignedByte(item.mu)==255
			&& byteArrayToShort(item.qty)==32767
			&& byteArrayToInt(item.je)==-2000
			&& byteArrayToInt(item.ye)==4000
			&& byteArrayToUnsignedShort(item.year)==2010
			&& byteToUnsignedByte(item.month)==9
			&& byteToUnsignedByte(item.day)==8
			&& byteToUnsignedByte(item.hour)==18
			&& byteToUnsignedByte(item.minute)==22
			&& byteToUnsignedByte(item.second)==44
			&& byteToUnsignedByte(item.ms)==22
			&& byteToUnsignedByte(item.sd)==3
			&& byteToUnsignedByte(item.jh)==4
			&& byteToUnsignedByte(item.lb)==31
			&& byteToUnsignedByte(item.online)==1;
		}
		
		SFPRDA pr = new SFPRDA();
		pr.czmn = unsignedShortToByteArray(32769);
		8个结构体//
		pr.sqcz[0] = 11;
		byte[] t = unsignedShortToByteArray(1111);
		pr.sqcz[1] = t[0];
		pr.sqcz[2] = t[1];
		
		pr.sqcz[3] = 22;
		t = unsignedShortToByteArray(2222);
		pr.sqcz[4] = t[0];
		pr.sqcz[5] = t[1];
		
		pr.sqcz[6] = 33;
		t = unsignedShortToByteArray(4444);
		pr.sqcz[7] = t[0];
		pr.sqcz[8] = t[1];
		
		pr.sqcz[9] = 44;
		t = unsignedShortToByteArray(5555);
		pr.sqcz[10] = t[0];
		pr.sqcz[11] = t[1];
		
		pr.sqcz[12] = 55;
		t = unsignedShortToByteArray(5555);
		pr.sqcz[13] = t[0];
		pr.sqcz[14] = t[1];
		
		pr.sqcz[15] = 66;
		t = unsignedShortToByteArray(6666);
		pr.sqcz[16] = t[0];
		pr.sqcz[17] = t[1];
		
		pr.sqcz[18] = 77;
		t = unsignedShortToByteArray(7777);
		pr.sqcz[19] = t[0];
		pr.sqcz[20] = t[1];
		
		pr.sqcz[21] = 88;
		t = unsignedShortToByteArray(8888);
		pr.sqcz[22] = t[0];
		pr.sqcz[23] = t[1];
		4个结构体//
		t = unsignedShortToByteArray(11);
		pr.sdje[0] = t[0];
		pr.sdje[1] = t[1];
		t = unsignedShortToByteArray(22);
		pr.sdje[2] = t[0];
		pr.sdje[3] = t[1];
		
		t = unsignedShortToByteArray(33);
		pr.sdje[4] = t[0];
		pr.sdje[5] = t[1];
		t = unsignedShortToByteArray(44);
		pr.sdje[6] = t[0];
		pr.sdje[7] = t[1];
		
		t = unsignedShortToByteArray(55);
		pr.sdje[8] = t[0];
		pr.sdje[9] = t[1];
		t = unsignedShortToByteArray(32768);
		pr.sdje[10] = t[0];
		pr.sdje[11] = t[1];
		
		t = unsignedShortToByteArray(77);
		pr.sdje[12] = t[0];
		pr.sdje[13] = t[1];
		t = unsignedShortToByteArray(40000);
		pr.sdje[14] = t[0];
		pr.sdje[15] = t[1];
		
		pr.open_xffs = (byte)255;
		pr.open_menu = (byte)200;
		pr.open_online = (byte)100;
		
		8个结构体//
		//para[0]个结构体赋值
		//para[0].xffs[0]个结构体赋值
		//para[0].xffs[0].dzlx_1
		pr.para[0] = 0;
		//para[0].xffs[0].dzje_1
		t = unsignedShortToByteArray(1);
		pr.para[1] = t[0];
		pr.para[2] = t[1];
		//para[0].xffs[0].jzlx_1
		pr.para[3] = 2;
		//para[0].xffs[0].jzje_1
		t = unsignedShortToByteArray(3);
		pr.para[4] = t[0];
		pr.para[5] = t[1];
		//para[0].xffs[0].dzlx_2
		pr.para[6] = 4;
		//para[0].xffs[0].dzje_2
		t = unsignedShortToByteArray(5);
		pr.para[7] = t[0];
		pr.para[8] = t[1];
		//para[0].xffs[0].jzlx_2
		pr.para[9] = 6;
		//para[0].xffs[0].jzje_2
		t = unsignedShortToByteArray(7);
		pr.para[10] = t[0];
		pr.para[11] = t[1];
		
		//para[0].xffs[1].dzlx_1
		pr.para[12] = 0;
		//para[0].xffs[1].dzje_1
		t = unsignedShortToByteArray(1);
		pr.para[13] = t[0];
		pr.para[14] = t[1];
		//para[0].xffs[1].jzlx_1
		pr.para[15] = 2;
		//para[0].xffs[1].jzje_1
		t = unsignedShortToByteArray(3);
		pr.para[16] = t[0];
		pr.para[17] = t[1];
		//para[0].xffs[1].dzlx_2
		pr.para[18] = 4;
		//para[0].xffs[1].dzje_2
		t = unsignedShortToByteArray(5);
		pr.para[19] = t[0];
		pr.para[20] = t[1];
		//para[0].xffs[1].jzlx_2
		pr.para[21] = 6;
		//para[0].xffs[1].jzje_2
		t = unsignedShortToByteArray(7);
		pr.para[22] = t[0];
		pr.para[23] = t[1];
		
		//para[0].xffs[2].dzlx_1
		pr.para[24] = 0;
		//para[0].xffs[2].dzje_1
		t = unsignedShortToByteArray(1);
		pr.para[25] = t[0];
		pr.para[26] = t[1];
		//para[0].xffs[2].jzlx_1
		pr.para[27] = 2;
		//para[0].xffs[2].jzje_1
		t = unsignedShortToByteArray(3);
		pr.para[28] = t[0];
		pr.para[29] = t[1];
		//para[0].xffs[2].dzlx_2
		pr.para[30] = 4;
		//para[0].xffs[2].dzje_2
		t = unsignedShortToByteArray(5);
		pr.para[31] = t[0];
		pr.para[32] = t[1];
		//para[0].xffs[2].jzlx_2
		pr.para[33] = 6;
		//para[0].xffs[2].jzje_2
		t = unsignedShortToByteArray(7);
		pr.para[34] = t[0];
		pr.para[35] = t[1];
		
		//para[0].xffs[3].dzlx_1
		pr.para[36] = 0;
		//para[0].xffs[3].dzje_1
		t = unsignedShortToByteArray(1);
		pr.para[37] = t[0];
		pr.para[38] = t[1];
		//para[0].xffs[3].jzlx_1
		pr.para[39] = 2;
		//para[0].xffs[3].jzje_1
		t = unsignedShortToByteArray(3);
		pr.para[40] = t[0];
		pr.para[41] = t[1];
		//para[0].xffs[3].dzlx_2
		pr.para[42] = 4;
		//para[0].xffs[3].dzje_2
		t = unsignedShortToByteArray(5);
		pr.para[43] = t[0];
		pr.para[44] = t[1];
		//para[0].xffs[3].jzlx_2
		pr.para[45] = 6;
		//para[0].xffs[3].jzje_2
		t = unsignedShortToByteArray(7);
		pr.para[46] = t[0];
		pr.para[47] = t[1];
		
		//para[0].zk[0]
		pr.para[48] = 8;
		//para[0].zk[1]
		pr.para[49] = 9;
		//para[0].zk[2]
		pr.para[50] = 10;
		//para[0].zk[3]
		pr.para[51] = 11;
		//para[0]赋值完毕,真够麻烦的。

		state = delphiFirstDLL.CallSFPRDA(pr);
		assert state == 0;
		
		SFMUDA sfmuda = new SFMUDA();
		
		t = unsignedShortToByteArray(7);
		sfmuda.mu[0]= t[0];
		sfmuda.mu[1]= t[1];
		t = unsignedShortToByteArray(8);
		sfmuda.mu[2]= t[0];
		sfmuda.mu[3]= t[1];
		int[]black = new int[3];
		black[0] = 3;
		black[1] = 6;
		black[2] = 9;
		state = delphiFirstDLL.CallSFMUDA(sfmuda,black);
		assert state==0;
		
		state = delphiFirstDLL.CallSFMUDA(sfmuda.mu,black);
		assert state == 0;
		
		SFMTDA sfmt = new SFMTDA();
		state = delphiFirstDLL.CallSFMTDA(sfmt);
		assert state==0;
		
		byte[] cs_sd = new byte[2];
		cs_sd[0] = sfmt.sfmt[0];
		cs_sd[1] = sfmt.sfmt[1];
		System.out.println("------------->"+byteArrayToUnsignedShort(cs_sd));
		
		Item item = sfmt.get(2);
		
		System.out.println("item.cs_sd=" + item.cs_sd);
		System.out.println("item.je_sd=" + item.je_sd);
		System.out.println("item.cs_day=" + item.cs_day);
		System.out.println("item.je_day=" + item.je_day);
		
	}
	
	
}

 

 UnsignedByteByReference类:

package org.forever.delphi.dll;

import com.sun.jna.ptr.ByReference;

/**
 * Delphi无符号1字节引用类型
 * Byte 0-255
 * 该类型作为方法的引用参数传递使用
 * @author 陈均
 *
 */
public class UnsignedByteByReference extends ByReference {
    
    public UnsignedByteByReference() {
        this((byte)0);
    }
    
    public UnsignedByteByReference(byte value) {
        super(1);
        setValue(value);
    }

    public void setValue(byte value) {
        getPointer().setByte(0, value);
    }
    
    public int getValue() {
    	byte b = getPointer().getByte(0);
    	if(b<0){
    		return getPointer().getByte(0)&0xff;
    	}
        return getPointer().getByte(0);
    }
}

 

UnsignedShortByReference类:

package org.forever.delphi.dll;

import com.sun.jna.ptr.ByReference;

/**
 * Delphi无符号2字节引用类型
 * Word 0-65535
 * 该类型作为方法的引用参数传递使用
 * @author 陈均
 *
 */
public class UnsignedShortByReference extends ByReference {
    
    public UnsignedShortByReference() {
        this((short)0);
    }
    
    public UnsignedShortByReference(short value) {
        super(2);
        setValue(value);
    }
    
    public void setValue(short value) {
        getPointer().setShort(0, value);
    }
    
    public int getValue() {
    	short value = getPointer().getShort(0);
    	if(value<0){
    		return getPointer().getShort(0)&0xffff;
    	}
        return getPointer().getShort(0);
    }
    
}

附上一个数据转换类:

package org.forever.delphi.dll;

/**
 * 数据解析工具类
 * @author 陈均
 *
 */
public class DataUtil {

	/**
	 * 字节数组转化为字符串
	 * @param b 字节数组
	 * @return 转换后的字符串值
	 */
	public static String byteArrayToString(byte[]b){
		return new String(b).trim();
	}
	
	/**
	 * 有符号字节转无符号值,对应Byte
	 * 无符号字节0-255
	 * @param b 有符号字节
	 * @return 返回无符号字节值
	 */
	public static short byteToUnsignedByte(byte b){
		if(b<0) return (short)(b & 0xff);
		return b;
	}
	
	/**
	 * 1字节转完整八位二进制字符串
	 * 比如byte = 10转换后00001010
	 * @param binaryString
	 * @return
	 */
	public static String byteToBinaryString(byte b){
		String binaryString = Integer.toBinaryString(b&0xff);
		int number = binaryString.length();
		if(number==8)return binaryString;
		return Integer.toBinaryString(((0xff>>number))).replaceAll("1", "0")+binaryString;
	}
	
	/**
	 * 有符号字节数组转无符号short,Word类型
	 * @param bytes
	 * @return 返回无符号short值
	 */
	public static int byteArrayToUnsignedShort(byte[]bytes){
		String b_1 = byteToBinaryString(bytes[0]);
		String b_2 = byteToBinaryString(bytes[1]);
		String value = b_2+b_1;
		return Integer.parseInt(value,2);
	}
	
	/**
	 * 无符号2字节类型转字节数组形式
	 * @param s 无符号值
	 * @return 返回以字节数组表示的无符号值
	 */
	public static byte[] unsignedShortToByteArray(int s){
		byte[] bytes = new byte[2];
		if(s<0 || s>65535){
			throw new RuntimeException("无符号字节返回在0-6535");
		}
		String bs = Integer.toBinaryString(s);
		//16位无符号
		int num =16  - bs.length();
		String val = Integer.toBinaryString(1<<num).substring(1)+bs;
		bytes[0] = (byte) Integer.parseInt(val.substring(8,16), 2);//低八位
		bytes[1] = (byte) Integer.parseInt(val.substring(0,8), 2);//高八位
		return bytes;
	}
	
	/**
	 * 将字节数组转换成整数
	 * @param bytes 4个字节长度,按照二进制,从左到右依次8位一个字节
	 * @return 转换后的整数值
	 */
	public static int byteArrayToInt(byte[]bytes){
		String b_1 = byteToBinaryString(bytes[0]);
		String b_2 = byteToBinaryString(bytes[1]);
		String b_3 = byteToBinaryString(bytes[2]);
		String b_4 = byteToBinaryString(bytes[3]);
		String value =b_4 + b_3 + b_2 + b_1;
		//进行判断,如果最高位是1表示是负数
		if(value.startsWith("1")){
			//取反
			value = value.replaceAll("1", "2")
			.replaceAll("0", "3")
			.replaceAll("2", "0")
			.replaceAll("3", "1");
			return(Integer.parseInt(value,2)+1)*-1;
		}
		return Integer.parseInt(value,2);	
	}
	/**
	 * 将字节数组转换成short类型
	 * @param bytes bytes[0]是二进制低八位,bytes[1]是二进制高八位
	 * @return 转化后的short值
	 */
	public static short byteArrayToShort(byte[]bytes){
		String b_1 = byteToBinaryString(bytes[0]);
		String b_2 = byteToBinaryString(bytes[1]);
		String value = b_2+b_1;
		//进行判断,如果最高位是1表示是负数
		if(value.startsWith("1")){
			//取反
			value = value.replaceAll("1", "2")
			.replaceAll("0", "3")
			.replaceAll("2", "0")
			.replaceAll("3", "1");
			return (short) ((Short.parseShort(value,2)+1)*-1);
		}	
		return Short.parseShort(value,2);
	}

	/**
	 * 无符号字节转字节
	 */
	public static byte unsignedByteToByte(short s) {
		if(s<0 || s>255){
			throw new RuntimeException("超出无符号字节有效范围0-255");
		}
		return (byte)s;
	}
	
	/**
	 * 整型值转字节数组形式
	 * @param s 整型值
	 * @return 返回以字节数组表示的整型值
	 */
	public static byte[] intToByteArray(int i){
		byte[] bytes = new byte[4];
		String bs = Integer.toBinaryString(i);
		int num =32  - bs.length();
		String val = Integer.toBinaryString(1<<num).substring(1)+bs;
		bytes[0] = (byte) Integer.parseInt(val.substring(24,32), 2);
		bytes[1] = (byte) Integer.parseInt(val.substring(16,24), 2);
		bytes[2] = (byte) Integer.parseInt(val.substring(8,16), 2);
		bytes[3] = (byte) Integer.parseInt(val.substring(0,8), 2);
		return bytes;
	}
	
	public static byte[] shortToByteArray(short s) {
		byte[] bytes = new byte[2];
		String bs = Integer.toBinaryString(s);
		if(bs.length()>16)bs = bs.substring(16);
		int num =16  - bs.length();
		String val = Integer.toBinaryString(1<<num).substring(1)+bs;
		bytes[0] = (byte) Integer.parseInt(val.substring(8,16), 2);//低八位
		bytes[1] = (byte) Integer.parseInt(val.substring(0,8), 2);//高八位
		return bytes;
	}
	
}

   

运行的时候开启断言即可。

总的一句话,类型匹配上用字节匹配今本上都可以搞定。

 DelphiDLLDemo_java.rar java测试源码。

  DelphiFirstDLL_Delphi.rar  Delphi dll源码。

有问题可以留言。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值