请问大家COM+问题,高分呀。(100分)

  • 主题发起人 主题发起人 lrcstar
  • 开始时间 开始时间
L

lrcstar

Unregistered / Unconfirmed
GUEST, unregistred user!
客户端(ASP上的一个ACTIVEX)
服务器为一个COM+组件
将com+中的一个数据集返回给客户端可以吗?
怎么做呢?给我个例子好吗?
lrcstar@cncce.com
 
我也想要呀。
 
请大家先回答一下,这样是否可以。
 
var
query1:TQuery;
DataSetProvider1:TDataSetProvider;
begin
...
QUERY1.active := true;
Result := DataSetProvider1.Data;
end;
DataSetProvider1和TQUERY相连
 
为什么呢?我的客户在本机应用时可以,而在别人机器连我的COM+组件时总出现 interface ont support的错误提示。
客户端代码如下:
CoMtsExample.CreateRemote('192.168.10.80').insert;
其中:insert 在服务器端只实现了向一表插入数据功能。
 
问了这么久了,为什么没人回答我呀?
 
我是按下面这篇文章做的:
不过我只用了自己定义的属性可以总出现: interface not support错呀,在自己机器上没事。

在数据库应用的三层结构中,客户端能够取得应用程序服务器的接口方法来获取与更新数据.
我所作的尝试是,在中间层运用OOP设计方法,将数据封装在一些对象中,在客户端获取这些对象的
接口的引用来实现获取数据及更新数据.
由于COM+提供了事务处理能力,并且能轻易实现Object Pooling技术,所以我选择了COM+组件
形式作为中间层.
第一步,新建一个ActiveX Library,新增一个Transactional Data Module,名为UserMgr.意思为用户管理模块,提供的接口为IUserMgr.在数据模块中放入一个TADOConnection(ADOConnection1), 一个TADOQuery(ADOQuery1). 设置ADOConnection1的连接属性,将ADOQuery1
的Connection属性设为ADOConnection1.在UserMgr的OnActive与OnDeactive事件中处理据库连
接:
procedure TUserMgr.MtsDataModuleActivate(Sender: TObject);
begin
ADOConnection1.Connected:=True;
end;
procedure TUserMgr.MtsDataModuleDeactivate(Sender: TObject);
begin
ADOConnection1.Connected:=False;
end;

第二步,开始我们的OOP设计了.由于这个模块的目的是用户管理,所以写封装用户信息的
TUser类,由于这个类需要被客户端引用.所以我们采用一个新的基类-----TAutoIntfObject.
先回到Type Library,新增一个接口,名为IUser,在这个接口下新增以下几个属性:
UserID: long read only;
LoginID:BSTR read write;
UserName:BSTR read write;
Password;BSTR read write;
Updated:Variant_Bool read write;
//记录是否被修改
Deleted;
Variant_Bool read write;
//记录是否被删除
再新增一个Unit名为uUser,在里面写上TUser的声明与实现,该单元如下:
unit uUser;
interface
uses
Classes, SysUtils, TestServer_TLB, ComObj, ComServ, uUserMgr;
type
TUser=class(TAutoIntfObject,IUser)
private
FUserID:Integer;
//FUserID=-1时代表为新增用户
FLoginID:string;
FUserName:string;
FPassword:string;
FUpdated:Boolean;
FDeleted:Boolean;
protected
function Get_UserID: Integer;
safecall;
function Get_LoginID: WideString;
safecall;
procedure Set_LoginID(const Value: WideString);
safecall;
function Get_UserName: WideString;
safecall;
procedure Set_UserName(const Value: WideString);
safecall;
function Get_Password: WideString;
safecall;
procedure Set_Password(const Value: WideString);
safecall;
function Get_Updated: WordBool;
safecall;
procedure Set_Updated(Value: WordBool);
safecall;
function Get_Deleted: WordBool;
safecall;
procedure Set_Deleted(Value: WordBool);
safecall;
public
constructor Create(AUserID:Integer=-1;ALoginID:string='';
AUserName:string='';

APassword:string='');reintroduce;
end;

implementation
{ TUser }
constructor TUser.Create(AUserID: Integer;
ALoginID, AUserName,
APassword: string;
AAdmin: Boolean);
begin
inherited Create(ComServer.TypeLib,IUser);
FUserID:=AUserID;
FLoginID:=ALoginID;
FUserName:=AUserName;
FPassword:=APassword;
end;

function TUser.Get_Deleted: WordBool;
begin
Result:=FDeleted;
end;

function TUser.Get_Updated: WordBool;
begin
Result:=FUpdated;
end;

function TUser.Get_LoginID: WideString;
begin
Result:=FLoginID;
end;

function TUser.Get_Password: WideString;
begin
Result:=FPassword;
end;

function TUser.Get_UserID: Integer;
begin
Result:=FUserID;
end;

function TUser.Get_UserName: WideString;
begin
Result:=FUserName;
end;

procedure TUser.Set_Deleted(Value: WordBool);
begin
FDeleted:=Value;
end;

procedure TUser.Set_Updated(Value: WordBool);
begin
FUpdated:=Value;
end;

procedure TUser.Set_LoginID(const Value: WideString);
begin
if FLoginID <> Value then
begin
FLoginID:=Value;
FUpdated:=True;
end;
end;

procedure TUser.Set_Password(const Value: WideString);
begin
if FPassword <> Value then
begin
FPassword:=Value;
FUpdated:=True;
end;
end;

procedure TUser.Set_UserName(const Value: WideString);
begin
if FUserName <> Value then
begin
FUserName:=Value;
FUpdated:=True;
end;
end;
由于TUser实现了IUser,而IUser继承自IDispatch,所以客户端可以通过获取IUser的接口来访问
位于中间层的TUser.
第三步, 由于用户不止一个,我们将创建一个用户列表类TUserList,实现新增或者删除一个用
户,修改一个用户的信息等. 先也是回到Type Library,新增一个接口IUserList, 再新增两个性:
Item[Index]:IUser;
它为只读属性. 注意, 在这里用到了IUser接口,用户将可以通过Item
来获取一个TUser对象的接口.
Count:Long ;
它也为只读属性. 代表用户的个数.
再增加三个方法:
New: New的返回类型为IUser,它可以实现新增一个用户.
Delete: 传入一个参数Index:long, 它用来实现删除一个用户;
ApplyUpdates:更新当前所有已经修改(包括新增,删除)的用户信息;
同样的,TUserList也是继承自TAutoIntfObject,实现IUserList接口.
以下是TUserList的代码:
unit uUserList;
interface
uses
Classes, SysUtils, ComObj, ComServ, TestServer_TLB, uUser, uUserMgr, Dialogs;
type
TUserList=class(TAutoIntfObject,IUserList)
private
FDM:TUserMgr;
FRef:IUnknown;
FList:TInterfaceList;
procedure Initialize;
protected
function Get_Item(Index: Integer): IUser;
safecall;
function Get_Count: Integer;
safecall;
procedure Delete(Index: Integer);
safecall;
function New: IUser;
safecall;
function ApplyUpdates: Integer;
safecall;
public
constructor Create(ADM:TUserMgr;
ARef:IUnknown);reintroduce;
destructor Destroy;override;
end;

implementation
{ TUserList }
function TUserList.ApplyUpdates: Integer;
var
I:Integer;
User:IUser;
begin
Result:=0;
with FDM, ADOQuery1do
try
for I:=0 to FList.Count-1do
begin
User:=IUser(FList.Items);
if User.Deleted and (User.UserID>0) then
//删除
begin
Close;
SQL.Clear;
SQL.Add('DELETE Users WHERE UserID=' + IntToStr(User.UserID));
ExecSQL;
Inc(Result);
end else
if User.Updated and (User.UserID=-1) then
//新增
begin
Close;
SQL.Clear;
SQL.Add('INSERT INTO Users(LoginID,UserName,Password)');
SQL.Add('VALUES(:LoginID,:UserName,:Password)');
Parameters[0].Value:=User.LoginID;
Parameters[1].Value:=User.UserName;
Parameters[2].Value:=User.Password;
ExecSQL;
Inc(Result);
end else
if User.Updated and (User.UserID > 0) then
//修改
begin
Close;
SQL.Clear;
SQL.Add('UPDATE Users SET LoginID=:LoginID, UserName=:UserName,');
SQL.Add(' Password=:Password);
SQL.Add('WHERE UserID=:UserID');
Parameters[0].Value:=User.LoginID;
Parameters[1].Value:=User.UserName;
Parameters[2].Value:=User.Password;
Parameters[3].Value:=User.UserID;
ExecSQL;
Inc(Result);
end;
end;
Initialize;
SetComplete;
except
SetAbort;
Result:=-1;
end;
end;

constructor TUserList.Create(ADM: TUserMgr;
ARef:IUnknown);
begin
inherited Create(ComServer.TypeLib,IUserList);
FDM:=ADM;
FRef:=ARef;

FList:=TInterfaceList.Create;
Initialize;
end;

procedure TUserList.Delete(Index: Integer);
begin
IUser(FList.Items[Index]).Deleted:=True;
//作删除标记
end;

destructor TUserList.Destroy;
begin
FRef:=nil;

FList.Clear;
FList.Free;
inherited;
end;

function TUserList.Get_Count: Integer;
begin
Result:=FList.Count;
end;

function TUserList.Get_Item(Index: Integer): IUser;
begin
Result:=FList.Items[Index] as IUser;
end;

procedure TUserList.Initialize;
var
User:IUser;
begin
FList.Clear;
with FDM,ADOQuery1do
try
Close;
SQL.Clear;
SQL.Add('SELECT UserID, LoginID, UserName, Password');
SQL.Add('FROM Users');
Open;
while not Eofdo
begin
User:=TUser.Create(FieldByName('UserID').AsInteger,
FieldByName('LoginID').AsString,
FieldByName('UserName').AsString,
FieldByName('Password').AsString)
FList.Add(User);
Next;
end;
Close;
SetComplete;
except
SetAbort;
end;
end;

function TUserList.New: IUser;
begin
Result:=TUser.Create;
FList.Add(Result);
end;

end.
这段代码里,肯定有不少人会不明白为什么创建这个对象时要加上一个ARef:IUnknown参数吧?
这是一个技巧,因为这个对象是通过一个服务器的一个方法输出,方法执行完后,将进入Deactive
状态,一段时间后,COM+对象若再无客户端连接的话,它会释放,这样,对象TUser中的FDM引用将无效,无法更新数据.所以,在创建这个TUserList对象时,我们人为地引用一次COM+服务器对象.
直到TUserList释放时,再释放该引用.
第四步, 在数据模块中新增一个方法,让用户可以通过该方法获取IUserList接口.也是先回到
Type Library Editor,在IUserMgr接口下新增一个GetUserList方法.刷新后写上以下实现代码:
function TUserMgr.GetUserList: IUserList;
begin
_AddRef;
Result:=TUserList.Create(Self, Self as IUnknown);
_Release;
end;
你一定会问,为什么要加上_AddRef与_Release呢,因为Self as IUnknown会增加一次引用,若不人
为用_AddRef来增加一次引用,将导致一退出该过程,TUserList就将自动释放,用户端无法引用该
对象.
OK,到这里为止,服务器部分算是完成了,那么客户端如何使用这个对象呢? 在连接服务器上,
我比较喜欢先期连接,即客户端直接引用服务器的Type Library,这样编程方便,速度性能好.
以下是客户端部分代码:
uses
....,TestServer_TLB;
var
UserList:IUserList;
procedure TForm1.FormCreate(Sender: TObject);
var
I:Integer;
Item:TListItem;
User:IUser;
begin
FUserList:=CoUserMgr.CreateRemote('192.168.1.1').GetUserList;
Lv.Clear;
//Lv为TListView
for I:=0 to FUserList.Count-1do
begin
User:=FUserList.Item;
Item:=Lv.Items.Add;
Item.Caption:=User.LoginID;
Item.SubItems.Add(User.UserName);
end;
if Lv.Items.Count>0 then
Lv.ItemIndex:=0;
end;

后记:这可是我这一次发表的"长篇大论", 其实自己也觉得没什么高深的技术,新各位看后不
要见笑,有什么更好的看法大家多多交流,呵呵.


 
应该可以直接将RECORDSET回传的,记得在“ADO技术详解”上论述过RECORDSET跨进程传递的问题的
 
没明白呀,能给个例子吗?
 
不懂,帮你顶一下。
 
后退
顶部