这两天正在做一个项目,需要使用Delphi与互联网社区网站进行对接,协议中使用了RSA加密;通过测试,Delphi的RSA算法无法与Java的RSA算法兼容,经过各种周折最终尝试了Delphi调用Java的类的方法进行RSA加密解密;这是非常令人振奋的解决方案,但是前期也遇到不少问题。
在网上找到http://www.delphi-jedi.org/中的jni.pas及相关的例子,但是用的是jdk1.4的,问了对方说至少需要jdk1.5,差点放弃了该方案;通过各种尝试及网上搜索终于找到了一个支持jdk1.6的jni.pas接口源文件,一切豁然开朗,下面分享一下我的成果。
运行效果图
源代码:
unit main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, uJNI, StdCtrls;
type
Tfmain = class(TForm)
btnInvoking: TButton;
mmoResult: TMemo;
edtClassName: TEdit;
lblClassName: TLabel;
edtMethodName: TEdit;
lblMethodName: TLabel;
edtData: TEdit;
lblData: TLabel;
lblResult: TLabel;
btnExit: TButton;
procedure FormDestroy(Sender: TObject);
procedure btnInvokingClick(Sender: TObject);
procedure btnExitClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
FJavaVM: TJavaVM;
FJNIEnv: TJNIEnv;
// 加载虚拟机
procedure LoadVM();
// 调用Java方法
procedure InvokingJavaMethod();
public
{ Public declarations }
end;
var
fmain: Tfmain;
implementation
{$R *.dfm}
// 加载虚拟机
procedure Tfmain.LoadVM;
var
Errcode: Integer;
VM_args: JavaVMInitArgs;
Options: array[0..10] of JavaVMOption;
begin
try
// 创建虚拟机对象,并传入jvm.DLL接口路径
FJavaVM := TJavaVM.Create(JNI_VERSION_1_6, 'jre6\bin\client\jvm.dll');
FillChar(Options, SizeOf(Options), #0);
// 设置jar包或类的搜索路径,多个jar使用分号隔开
Options[0].optionString := '-Djava.class.path=.;RSA.jar';
// 声明使用jdk1.6版本
VM_Args.version := JNI_VERSION_1_6;
VM_Args.options := @Options;
VM_Args.nOptions := 1;
VM_Args.ignoreUnrecognized := False;
// 加载虚拟机
ErrCode := FJavaVM.LoadVM(VM_Args);
if ErrCode < 0 then
begin
// Loading the VM more than once will cause this error
if Errcode = JNI_EEXIST then
MessageDlg('Java VM has already been loaded. Only one VM can be loaded.', mtError, [mbOK], 0)
else
ShowMessageFmt('Error creating JavaVM, code = %d', [Errcode]);
Exit;
end;
// Create the Env class
FJNIEnv := TJNIEnv.Create(FJavaVM.Env);
except
on E: Exception do
begin
ShowMessage('Error: ' + E.Message);
Exit;
end;
end;
end;
// 调用Java方法
procedure Tfmain.InvokingJavaMethod;
var
Cls: JClass;
Mid: JMethodID;
sResult: WideString;
JStr: JString;
sData: UTF8String;
sMethod, sClass: PAnsiChar;
begin
try
// 将传入的参数值转成UTF8字符,用于支持中文
sData :=UTF8Encode(edtData.Text);
// 方法名称
sMethod := PAnsiChar(edtMethodName.Text);
// 类名称
sClass := PAnsiChar(edtClassName.Text);
// 查找类,注意:类的路径需要做转换,如:cn.com.tcsl.RSA 应转化为 cn/com/tcsl/RSA
Cls := FJNIEnv.FindClass(sClass);
if Cls = nil then
begin
ShowMessage('Can''t find class: '+sClass);
Exit;
end;
// 定位类的静态方法sMethod,并配置参数结构
Mid := FJNIEnv.GetStaticMethodID(Cls, sMethod, '(Ljava/lang/String;)Ljava/lang/String;');
if Mid = nil then
begin
ShowMessage('Can''t find method: '+sMethod);
Exit;
end;
// 调用静态方法
JStr := FJNIEnv.CallStaticObjectMethod(Cls, Mid, [sData]);
// 将JString转化成Delphi String,注意:需要使用UTF8Decode解码UTF8字符
sResult :=UTF8Decode(FJNIEnv.JStringToString(JStr));
// 显示结果
mmoResult.Lines.Add(sResult);
except
on E: Exception do
ShowMessage('Error: ' + E.Message);
end;
end;
procedure Tfmain.FormCreate(Sender: TObject);
begin
LoadVM();
end;
procedure Tfmain.FormDestroy(Sender: TObject);
begin
FJNIEnv.Free;
FJavaVM.Free;
end;
procedure Tfmain.btnInvokingClick(Sender: TObject);
begin
InvokingJavaMethod();
end;
procedure Tfmain.btnExitClick(Sender: TObject);
begin
Close;
end;
end.
该例子只使用了最简单的参数模式,但是一般已经够用了(即调用一个Java类的方法,传入字符,返回处理后的字符),更复杂的用法可以参考http://www.pacifier.com/~mmead/jni/delphi/JEDI/DOCS/delphi-jni-3.html。
为了方便大家调试我上传了完整的源码及jar6包,大家省事了,下载直接能够调试运行。