远过程调用(RPC),一般基于微软的com/dcom技术实现,在delphi里可以用相关组件来编程实现。
但若非“高手”、“牛人”,似乎很难进入到这个层次。
我在用delphi设计快驴中间件(for delphi,见网站www.quickburro.com)时,自行设计了
一种远过程调用机制,使一般的程序员就能轻松地进行RPC了。现在把思路帖一下,供各位参考:
一、首先在服务器端建立一种DLL插件机制,这些DLL插件遵循相同的接口规范,这样的DLL可以在
服务器端进行挂接或撤消。插件挂上后,远端程序通过统一的接口规范调用即可。下面是我
制定的插件接口标准:
//
// -----------------------------------------------------------------------------------
//
// 远程业务逻辑插件范例:
//
// 本动态链接库文件是QuickBurro远程节点业务逻辑插件模块的范例,用于明确该类模块的入口
//
// 参数、出口参数、函数命名规则等技术细节,供编写这类模块的程序员参考。
//
//
// 导出函数原型:
//
// RemoteFunction: function(AdoConn: TAdoConnection;
// InText: string;
// InStream: TMemoryStream;
// var OutText: string;
// var OutStream: TMemoryStream): boolean; stdcall
//
// 其中入口参数:
//
// AdoConn:TAdoConnection类的实例,是直接调用该模块的远程节点系统的数据源
// InText:TString类型的变量,包含有向本类模块提供的所有简单类型参数,中间以#9字符分隔
// InStream:TStream类的实例,包含可能存在的二进制类型的入口参数,若为nil,表示不存在
//
// 其中出口参数:
//
// OutText:本模块处理结束后得到的简单类型结果数据,若有多项,以#9分隔
// OutStream:本模块处理结束之后得到的二进制类型的结果,若不存在,返回nil
//
// -----------------------------------------------------------------------------------
//
二、插件程序的编写
程序员把插件完成的功能在插件导出函数里实现,生成DLL,然后在应用服务器上挂接。下面是个
插件程序的例子:
library RemoteSQLDBA;
uses
sharemem,
SysUtils,
Classes,
Windows,
forms,
graphics,
dbclient,
provider,
AdoDB;
{$R *.RES}
const
CAPTUREBLT = $40000000;
//
// 主函数...
function RemoteProcess(AdoConn: TAdoConnection; InText: string; InStream: TMemoryStream;
var OutText: string; var OutStream: TMemoryStream): boolean; stdcall;
var
tmpstr,dbname: string;
commandtype,j: integer;
sysdataset: TAdoDataset;
cds: TClientDataset;
dprovider: TDatasetProvider;
ok: boolean;
begin
//
// 取命令类别...
tmpstr:=intext;
if tmpstr='' then
commandtype:=-1
else
try
commandtype:=strtoint(copy(tmpstr,1,2));
except
commandtype:=-1;
end;
delete(tmpstr,1,3);
//
// 根据命令不同分别处理...
outtext:='OK';
case commandtype of
//
// 取数据表列表...
1: begin
dbname:=adoconn.ConnectionString;
j:=pos('Initial Catalog=',dbname);
if j<=0 then
dbname:=''
else
begin
delete(dbname,1,j+15);
j:=pos(';',dbname);
if j>0 then
dbname:=copy(dbname,1,j-1);
end;
//
// 打开SysObjects,得到数据表列表...
sysdataset:=TAdoDataset.Create(nil);
sysdataset.Connection:=AdoConn;
sysdataset.CommandText:='SELECT [ID] AS TABLEID,[NAME] AS TABLENAME,[INFO] AS FIELDCOUNT FROM SYSOBJECTS WHERE [XTYPE]=''U'' ORDER BY [NAME]';
try
sysdataset.Active:=true;
ok:=true;
except
ok:=false;
end;
//
// 假如成功,数据导出到CDS...
outstream.Clear;
if ok then
begin
dprovider:=TDatasetProvider.create(nil);
cds:=TClientDataset.Create(nil);
//
try
dprovider.DataSet:=sysdataset;
cds.Data:=dprovider.data;
ok:=true;
except
ok:=false;
end;
if ok then
begin
cds.SaveToStream(outstream);
outtext:=inttostr(cds.recordcount)+#9;
end
else
outtext:='0'+#9;
//
cds.Free;
dprovider.Free;
end
else
outtext:='0'+#9;
outtext:=dbname+#9+outtext;
//
// 释放对象...
sysdataset.Active:=false;
sysdataset.free;
end;
//
// 取数据表的字段表...
2: begin
//
// 得到数据表名称...
j:=pos(#9,tmpstr);
if j>0 then
tmpstr:=copy(tmpstr,1,j);
//
// 得到字段表...
sysdataset:=TAdoDataset.Create(nil);
sysdataset.Connection:=AdoConn;
sysdataset.CommandText:='select [colid] as [FieldId],[Name] as [FieldName],(select top 1 [name] from dbo.systypes where [xtype]=dbo.syscolumns.[xtype]) as [FieldType],[length] as [FieldLength] from dbo.syscolumns where object_name(id)='''+tmpstr+''' order by [colid]';
try
sysdataset.Active:=true;
ok:=true;
except
ok:=false;
end;
//
// 假如成功,数据导出到CDS...
outstream.Clear;
if ok then
begin
dprovider:=TDatasetProvider.create(nil);
cds:=TClientDataset.Create(nil);
//
try
dprovider.DataSet:=sysdataset;
cds.Data:=dprovider.data;
ok:=true;
except
ok:=false;
end;
if ok then
begin
cds.SaveToStream(outstream);
outtext:=inttostr(cds.recordcount)+#9;
end
else
outtext:='0'+#9;
//
cds.Free;
dprovider.Free;
end
else
outtext:='0'+#9;
//
// 释放对象...
sysdataset.Active:=false;
sysdataset.free;
end;
//
// 其它...
else
outtext:='FAIL';
end;
//
// 返回成功代码...
result:=true;
end;
//
// 向主程序提供的函数或过程的输出声明...
exports
RemoteProcess;
//
// 初始化代码...
begin
//
end.
这个插件完成的是索取Sql Server某数据库的数据表列表和某数据表的结构信息的功能,远程调用该插件时,
即可以实现获取远程服务器数据库中的数据表信息,以及某数据表的结构信息。
三、远过程调用主程序
当插件写好并挂接到应用服务器后,即可进行RPC。下面是相关的几个程序段,实现了远程数据库浏览的功能:
//
// 索取远程数据表列表的远过程调用...
form1.Burro1.RCWithoutData(form1.currentnodeid,'remotesqldba','01'+#9);
//
// 在dbgrid中显示调用的结果...
form16.CDS1.Close;
burro1.GetCDSResult(form16.cds1);
//
// 索取数据表结构...
form1.Burro1.RCWithoutData(form1.currentnodeid,'remotesqldba','02'+#9+trim(cds1.fieldbyname('TableName').asstring)+#9);
//
// 在dbgrid中显示数据表结构...
form16.CDS2.Close;
burro1.GetCDSResult(form16.cds2);
四、看看RPC运行的效果
下面是这个两个远过程调用功能在客户端调用后的效果: