DCOM回调?
由于服务器端有一个很漫长的过程A(1分多种),如果在客户端调用,就像死机一样,我想先让客户端调用一个服务器的另一个过程B来激活这个A过程;服务器执行完A再发送消息给客户端来读取数据.那么想从服务器中返回消息给客户端;必须怎样做才可以. 问题点数:100、回复次数:6Top
1 楼constantine(飘遥的安吉儿)回复于 2005-04-04 16:30:48 得分 0
阅Top
2 楼NetSpider9804040(网络蛛蛛)回复于 2005-04-04 17:52:39 得分 0
关注!Top
3 楼MEFULEU(没有作不到,只有想不到)回复于 2005-04-05 08:45:03 得分 0
以前看到一些e文资料,但是e文水平有限....谁帮我翻译一下
There is also a way for the server to call methods on the client. Its a
little involved to set up and I have not tried it myself, however, the
following is how to do it :
Server
-------
1). In the server _TLB unit, create a new interface, lets call it IClient.
2). After adding this, expand its tree and add any client methods that will
be called. For example, lets add a method called SendRecordsProcessed which
takes one parameter NumberRecords of type Integer.
3). Now expand the main RemoteDataModule interface to add a new server
automation method called ConnectClient which takes one parameter, IClient.
4). ConnectClient assigns its interface reference parameter to a variable
that is added to the remote data module, so, edit that RemoteDataModule's
unit and add a private variable ClientConnection of type IClient. In other
words, ClientConnection is the server's reference to the object that
implements the IClient interface with the client. With this interface
reference the server can call any method of the interface.
5). Insert this new method's implementation code as follows :
procedure TServer.ConnectClient(const Client: IClient);
begin
ClientConnection := Client;
end;
6). In your server you must have a procedure(s) or function(s) which are
doing all of this processing of which you speak, so at the end of the
processing insert the following call :
ClientConnection.SendRecordsProcessed( <NumberOfRecords > );
So this is using the interface reference to call the SendRecordsProcessed
method in the client app.
Client
-------
We must now add the client side processing necessary, ie. an object that
implements the IClient interface. In your main form do the following :
1). Declare the client side object that implements the IClient interface
TCallBack = class( TAutoIntfObject, IClient )
procedure SendRecordsProcessed( const NumberRecords:
Integer ); safecall;
end;
The TCallback object descends from TAutoIntfObject which provides support
for the IDispatch interface. The implementation code for the
SendRecordsProcessed method does whatever it is you want to do with that
number of records processed, ie. display it in a message box or on a form,
so I'll leave that up to you.
2). Include the server's _TLB unit in the uses clause of the main form.
3). Variable declarations global to the main form's unit
var
ServerTypeLib: ITypeLib;
TypeLibResult: HResult;
CallBack: IClient;
4). Build the heart of the callback mechanism in the OnCreate event handler
of the client app's main form.
procedure TMainForm.FormCreate(Sender: TObject);
var
Srvr: <ServerInterfaceName>; // Name as defined in the
server TLB, eg. IServer
4.1). Call the Windows API function LoadRegTypeLib to load the server's type
library. The parameters are :
The type library's GUID, definied as a constant LIBID_XXX in the
server's TLB unit
The type library's major version number
The type library's minor version number
The national language code of the library
An interface reference variable of the type ITypeLib that is
initialised by the call
to point to the type library ( ServerTypeLib var above )
TypeLibResult := LoadRegTypeLib( LIBID_XXX, 1, 0, 0,
ServerTypeLib )
4.2). Check the result of the LoadRegTypeLib
If TypeLibResult <> S_OK Then
Begin
ShowMessage( 'Error loading type library' );
Exit;
End;
4.3). Create an instance of the TCallback automation object. The type
library and interface that the TCallBack object implements are passed as
parameters to its constructor and the returned value is assigned to the
interface reference variable CallBack.
CallBack := TCallBack.Create( ServerTypeLib, IClient );
4.4). Next, obtain a reference to the server's interface IClient by casting
the DComConnection ( MidasConnection ) AppServer property to the interface
type.
Srvr := IDispatch( <DataModule>.<DComConnection>.AppServer ) as
<ServerInterfaceName>;
4.5). Finally call the server's ConnectClient automation method, passing the
interface reference variable for the TCallBack object.
Srvr.ConnectClient( CallBack );
I apologise in advance if I have mis-typed or left anything out, but it
should be a good enough starting point.
DaveTop
4 楼lidawen(Darwin)回复于 2005-04-05 08:46:53 得分 0
學習Top
5 楼MEFULEU(没有作不到,只有想不到)回复于 2005-04-05 12:57:58 得分 0
高手呢?都转行了吗?Top
6 楼lanren_me(阿波)回复于 2005-04-08 09:03:48 得分 100
今天试验成功了,原来很容易的,过程大约如下:
客户端:
1。在客户端创建一个类型库,添加一个用于回调的接口,在其中声明接口方法。
2。在客户应用程序中声明一个类,从 TAutoIntfObject 继承,并实现上面添加的回调接口。
3。客户应用程序启动时创建这个类,然后调用中间层的一个接口方法,该方法把用于回调的接口传递给中间层。
中间层:
1。在中间层的类型库中添加一个接口方法,客户端通过调用此方法把回调接口传递给中间层。其形式为:procedure SetCallback(ACallback:OleVariant);
2。在该接口方法的实现代码中,把客户端传递来的接口用一个本地的 OleVariant 变量(如FCallback:=ACallback)保存。
3。在需要回调时,执行:FCallback.AMethod(...)。
用这种方式,客户端也同时作为 COM 服务器了吧,配置每个客户端可能也是比较麻烦的事情。
unit ClientM; /////////客户端窗体;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,ComObj,Server_TLB,MConnect, DB, DBClient, SConnect,ActiveX,
ComServ;
type
TClientCallBack = Class(TAutoIntfObject , IDCOMBackCalls)
procedure SetInfo(var InfoName: OleVariant; var InfoPass: OleVariant); safecall;
end;
//////////////////////////////////////////////////////////////////////////////
//////////TClientCallBack = Class(TAutoIntfObject,IDCOMBackCalls) TClientCallBack 它继承类TAutoIntfObject
//////////同时又是实现了接口IDCOMBackCalls,而这个接口是需要在TypeLib中声明的;
///////////接口的实现,需要知道,之后还要进行类的实例化;
///////////需要实现这个接口的方法 :SetInfo(.....) SafeCall ,注意,这儿只能用SafeCall;
//////////下边有接口的实例化,FClientCallBack : TClientCallBack;
//////////uses ComObj;
///////////////////////////////////////////////////////////////////////////////////
TForm2 = class(TForm)
Button1: TButton;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
SocketConnection1: TSocketConnection;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
FClientCallBack : TClientCallBack;
/////////这一句是接口正真的实例化;
{ Private declarations }
public
MyDCOMConnection : TDispatchConnection;
//////注意这一句:TDispatchConnection 是如下定义的:
//////TDispatchConnection = class(TCustomRemoteServer)
{ Public declarations }
end;
var
Form2: TForm2;
ClientVar : IDCOMServerSL1;
//////////////////////////////////////////////////////////////////////////////
//////定义一个接口,是TypeLib中声明的哦;uses Server_TLB,(User TypeLib)
//////////////////////////////////////////////////////////////////////////////
implementation
{$R *.dfm}
procedure TForm2.Button1Click(Sender: TObject);
begin
ClientVar := CoDCOMServerSL1.Create;
if ClientVar.GetInfo(Edit1.Text,Edit2.Text) then
begin
Application.MessageBox('恭喜您,登录成功!','操作提示',MB_OK + MB_ICONINFORMATION);
end else
begin
Application.MessageBox('对不起,登录失败!','操作提示',MB_OK + MB_ICONINFORMATION);
end;
/////////////////////////////////////////////////////////////////////////////////////
/////////MyDCOMConnection.AppServer.ProcName;,也可以用这种进行回调
/////////CoDCOMBackCall = class
/////////class function Create: IDCOMBackCall;
////////class function CreateRemote(const MachineName: string): IDCOMBackCall;
////////end;
////////看一看它的定义,它其实是一个类,呵呵,协同接口类;是实现一个或多个接口的类,
///////有一个类生成库和一个类标识器;
///////这一部分是DCOM的,和回调无关;
///////////////////////////////////////////////////////////////////////////////////////
end;
{ TClientCallBack }
procedure TForm2.FormCreate(Sender: TObject);
var
TypeLib : ITypeLib;
begin
MyDCOMConnection := SocketConnection1;
MyDCOMConnection.Connected := True;
OLECheck(LoadRegTypeLib(LIBID_Server,0,0,1,TypeLib));
MyDCOMConnection.AppServer.GetInfos(FClientCallBack as IDispatch);
{/////////////////////////////////////////////////////////////////////////////
//////MyDCOMConnection := SocketConnection1;是回调的关键,你要用它来代替TSocketConnection,操作还是类实例之间,你明白吗?
//////MyDCOMConnection.Connected := True;还用说吗?
//////OLECheck(LoadRegTypeLib(LIBID_Server,0,0,1,TypeLib));在客户端注册;
//////MyDCOMConnection.AppServer.GetInfos(FClientCallBack as IDispatch);开始执行,而且进行接口转化;
//////TypeLib : ITypeLib;可以应用类型库类 Uses ActiveX;
//////////////////////////////////////////////////////////////////////////////}
end;
{ TClientCallBack }
procedure TClientCallBack.SetInfo(var InfoName, InfoPass: OleVariant);
begin
ShowMessage(InfoName);
ShowMessage(InfoPass);
{/////////////////////////////////////////////////////////////////////////////
/////我不知道你是否知道RDM中是如果引用接口的,这和它是一样的;
//////////////////////////////////////////////////////////////////////////////}
end;
end.
unit ServerRDM; ////////远程数据模块,是回调机制用的,于DCOM无关;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
Windows, Messages, SysUtils, Classes, ComServ, ComObj, VCLCom, DataBkr,
DBClient, Server_TLB, StdVcl;
type
TDCOMBackCall = class(TRemoteDataModule, IDCOMBackCall)
private
BackCallSL : OleVariant;
{ Private declarations }
protected
class procedure UpdateRegistry(Register: Boolean; const ClassID, ProgID: string); override;
procedure GetInfos(var Infos: OleVariant); safecall;
public
{ Public declarations }
end;
implementation
{$R *.DFM}
class procedure TDCOMBackCall.UpdateRegistry(Register: Boolean; const ClassID, ProgID: string);
begin
if Register then
begin
inherited UpdateRegistry(Register, ClassID, ProgID);
EnableSocketTransport(ClassID);
EnableWebTransport(ClassID);
end else
begin
DisableSocketTransport(ClassID);
DisableWebTransport(ClassID);
inherited UpdateRegistry(Register, ClassID, ProgID);
end;
end;
procedure TDCOMBackCall.GetInfos(var Infos: OleVariant);
begin
BackCallSl := Infos;
BackCallSl.SetInfo('a','b');
{////////////////////////////////////////////////
///////BackCallSl := Infos;(还记得这个参数传过来的是什么吗?呵呵,看前边去;)
/////回调,可以由任意事件进行激发
///////////////////////////////////////////////// }
end;
initialization
TComponentFactory.Create(ComServer, TDCOMBackCall,
Class_DCOMBackCall, ciMultiInstance, tmApartment);
Top




