1. File->New->Other,在打开的对话框中,选择Web Services/SOAP Server Application。
2. 在这里选择ISAPI DLL。
3. 问咱Create Interface for SOAP module,当然是yes啦,然后输入个名称XXX,等下会生成XXXIntf.pas和XXXImple.pas这两个文件,分别对应接口和类的实现。在这里,对外公布的都是接口。
Service name: SampleService
如果这里不添加,也可以使用SOAP Server Interface向导来添加接口。
最后编译生成的叫libss.dll。
这里最主要关注的是SampleServiceIntf.pas和SampleServiceImpl.pas。
SampleServiceIntf.pas
unit SampleServiceIntf;
interface
uses InvokeRegistry, Types, XSBuiltIns;
type
{ Invokable interfaces must derive from IInvokable }
ISampleService = interface (IInvokable)
[ ' {C003A413-B302-437C-990C-1ADFAA03A619} ' ]
{ Methods of Invokable interface must not use the default }
{ calling convention; stdcall is recommended }
end;
implementation
initialization
{ Invokable interfaces must be registered }
InvRegistry.RegisterInterface(TypeInfo(ISampleService));
end.
SampleServiceImpl.pas
unit SampleServiceImpl;
interface
uses InvokeRegistry, Types, XSBuiltIns, SampleServiceIntf;
type
{ TSampleService }
TSampleService = class (TInvokableClass, ISampleService)
public
end ;
implementation
initialization
{ Invokable classes must be registered }
InvRegistry.RegisterInvokableClass(TSampleService);
end .
要公布的接口必须继承IInvokable
IInvokable其实跟IInterface是一样的,不同之处在于带上了{$M}编译指令,使用这个编译指令,使得它以及它的子类都具有RTTI。
IInvokable = interface (IInterface)
end ;
{$M - }
要用于数据传递的类必须继承TRemotable
要用于数据传递的类必须继承TRemotable,并且必须在published中以property的形式公布数据。对于复杂的类引用(包括递归引用),也是没问题的。可是,如果在结果的地方返回一个对象,它将会在什么时候被销毁呢?会不会造成内存泄漏呢?看看TRemotable.Create的源代码就知道了。
begin
inherited;
if RemotableDataContext <> nil then
begin
TDataContext(RemotableDataContext).AddObjectToDestroy(Self);
Self.DataContext : = TDataContext(RemotableDataContext);
end ;
end ;
修改后的代码
下面是SampleServiceIntf和SampleServiceImpl这两个单元修改后的代码。
SampleServiceIntf.pas
unit SampleServiceIntf;
interface
uses InvokeRegistry, Types, XSBuiltIns;
type
// 要用于传送数据的类必须继承TRemotable
// 要传送的数据必须在published中用property公开
TComplex = class (TRemotable)
private
FI: real;
FR: real;
procedure SetI( const Value: real);
procedure SetR( const Value: real);
published
published
property R:real read FR write SetR;
property I:real read FI write SetI;
end ;
// 复杂的数据
TExtraData = class (TRemotable)
private
FX: Integer ;
procedure SetX( const Value: Integer );
published
property X: Integer read FX write SetX;
end ;
TLoopable = class (TRemotable)
private
FData: TExtraData;
FNext: TLoopable;
procedure SetData( const Value: TExtraData);
procedure SetNext( const Value: TLoopable);
published
published
property Data:TExtraData read FData write SetData;
property Next :TLoopable read FNext write SetNext default nil;
end ;
// 递归引用
TClassA = class ;
TClassB = class (TRemotable)
private
Fref: TClassA;
procedure Setref( const Value: TClassA);
published
property ref:TClassA read Fref write Setref default nil;
end ;
TClassA = class (TRemotable)
private
Fref: TClassB;
procedure Setref( const Value: TClassB);
published
property ref:TClassB read Fref write Setref default nil;
end ;
{ Invokable interfaces must derive from IInvokable }
ISampleService = interface (IInvokable)
[ ' {C003A413-B302-437C-990C-1ADFAA03A619}']
// 这里参数传递方法最好用stdcall
function ComplexAdd(a,b:TComplex):TComplex;stdcall;
function Loop (Level: Integer ):TLoopable;stdcall;
function GetClassAB:TClassA;stdcall;
end ;
implementation
{ TComplex }
procedure TComplex.SetI( const Value: real);
begin
FI : = Value;
end ;
procedure TComplex.SetR( const Value: real);
begin
FR : = Value;
end ;
{ TExtraData }
procedure TExtraData.SetX( const Value: Integer );
begin
FX : = Value;
end ;
{ TLoopable }
procedure TLoopable.SetData( const Value: TExtraData);
begin
FData : = Value;
end ;
procedure TLoopable.SetNext( const Value: TLoopable);
begin
FNext : = Value;
end ;
{ TClassA }
procedure TClassA.Setref( const Value: TClassB);
begin
Fref : = Value;
end ;
{ TClassB }
procedure TClassB.Setref( const Value: TClassA);
begin
Fref : = Value;
end ;
initialization
{ Invokable interfaces must be registered }
InvRegistry.RegisterInterface(TypeInfo(ISampleService));
end .
SampleServiceImpl.pas
unit SampleServiceImpl;
interface
uses InvokeRegistry, Types, XSBuiltIns, SampleServiceIntf;
type
{ TSampleService }
TSampleService = class (TInvokableClass, ISampleService)
public
function ComplexAdd(a,b:TComplex):TComplex;stdcall;
function Loop (Level: Integer ):TLoopable;stdcall;
function GetClassAB:TClassA;stdcall;
end ;
implementation
{ TSampleService }
function TSampleService.ComplexAdd(a, b: TComplex): TComplex;
begin
Result: = TComplex.Create;
Result.R: = a.R + b.R;
Result.I: = a.I + b.I;
// 可是这个对象,什么时候被销毁呢????
end ;
function TSampleService.GetClassAB: TClassA;
begin
Result: = TClassA.Create;
Result.ref: = TClassB.Create;
Result.ref.ref: = Result;
// 这个对象能否被安全销毁呢?
end ;
function TSampleService.Loop(Level: Integer ): TLoopable;
var tmp:TLoopable;
I: Integer ;
begin
Result: = nil;
for I : = 1 to Level do
begin
tmp: = TLoopable.Create;
tmp.Data: = TExtraData.Create;
tmp.Data.X: = I;
tmp.Next: = Result;
Result: = tmp;
end ;
// 返回的对象将在什么时候被销毁呢????
end ;
initialization
{ Invokable classes must be registered }
InvRegistry.RegisterInvokableClass(TSampleService);
end .
编译、部署
ISAPI DLL的编译部署没啥好说的吧!?部署完了,可以用浏览器打开看到相应的定义。
编写客户端
既然已经用delphi编写了server端,那么就用其它语言来编写个客户端来调用它。这里用vs2005 c#2.0。
首先,添加web referrence,这里是http://localhost/MyServices/SampleService/libss.dll/wsdl/ISampleService,引用名为SampleServiceRef。
代码如下:
using System.Collections.Generic;
using System.Text;
using SampleService.Invoker.CS.SampleServiceRef;
namespace SampleService.Invoker.CS
... {
class Program
...{
static void Main(string[] args)
...{
// ISampleServiceservice is a class
ISampleServiceservice service = new ISampleServiceservice();
// ComplexAdd
TComplex a = new TComplex();
a.R = 1;
a.I = 2;
TComplex b = new TComplex();
b.R = 3;
b.I = 4;
TComplex x = service.ComplexAdd(a, b);
Console.WriteLine("{0} + {1}i", x.R, x.I);
// Loop
TLoopable p = service.Loop(5);
while (p != null)
...{
if (p.Data != null)
Console.WriteLine(p.Data.X);
else Console.WriteLine("null");
p = p.Next;
}
// GetClassAB
TClassA ca = service.GetClassAB();
if (ca.@ref.@ref == ca) Console.WriteLine("true");
else Console.WriteLine("false");
}
}
}
附上自动生成的Reference.cs,仅供参考。
// <auto-generated>
// 此代码由工具生成。
// 运行库版本:2.0.50727.42
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
// ------------------------------------------------------------------------------
//
// 此源代码是由 Microsoft.VSDesigner 2.0.50727.42 版自动生成。
//
#pragma warning disable 1591
<