如何返回子接口 [已经放一年多了,真没有人知道吗?] (200分)

to nkiller
下面这几个调用方式及调用结果你看了一定感兴趣:

var
Form1: TForm1;

implementation

uses Project3_TLB;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
Var
MyCtrl, MySubCtrl : Variant;
Str : String;
begin
MyCtrl := CreateOleObject('Project3.MyTest');
MySubCtrl := MyCtrl.subctrl;
Str := MyCtrl.Test
//IMyCtrl自身的属性可以访问,问题是现在的MySubCtrl应该是IMySubTest
caption:=str;
end;

procedure TForm1.Button2Click(Sender: TObject);
Var
MyCtrl, MySubCtrl : Variant;
Str : String;
begin
MyCtrl := CreateOleObject('Project3.MyTest');
MyCtrl.subctrl:=myctrl;
MySubCtrl := MyCtrl.subctrl;
Str := MysubCtrl.name
//出错,//IMySubCtrl自身的属性不能访问,现在的MySubCtrl应该是IMySubTest
caption:=str;
end;
//以上两个调用,说明虽然我们指定了TMyTest的类型为IMySubTest,但函数返回的仍是IMyTest,没有返回子接口

procedure TForm1.Button3Click(Sender: TObject);
var MyCtrl, MySubCtrl : Variant;
Str : String;
begin
MyCtrl := CreateOleObject('Project3.MyTest');
MySubCtrl := MyCtrl.SubCtrl;
Str := MySubCtrl.Testb
//这里可以执行,这个属性是IMyTest的,起代码如下:
// Result:=(Self as IMySubTest).name;
//这说明在com内部转换成功
caption:=str;
end;

procedure TForm1.Button4Click(Sender: TObject);
var MyCtrl, MySubCtrl : Variant;
Str : String;
begin
str:=(CreateOleObject('Project3.MyTest') as IMySubTest).name
//成功执行
//引用Project3_TLB后,可以强制转换MyTest为IMySubTest

caption:=str;
end;

end.

*********************************************************************
另外,你的com代码也有问题,应该象下面这样写:

type
TMyTest = class(TASPObject, IMyTest, IMySubTest)
protected
FSubCtrl:IMySubTest;
procedure Initialize;override;
procedure OnEndPage
safecall;
procedure OnStartPage(const AScriptingContext: IUnknown)
safecall;
function Get_test: OleVariant
safecall;
function Get_Name: OleVariant
safecall;
function Get_SubCtrl: IMySubTest
safecall;
function Get_Testb: OleVariant
safecall;
end;

implementation

uses ComServ;

procedure TMyTest.OnEndPage;
begin
inherited OnEndPage;
end;

procedure TMyTest.OnStartPage(const AScriptingContext: IUnknown);
begin
inherited OnStartPage(AScriptingContext);
end;

function TMyTest.Get_test: OleVariant;
begin
Result:='Test';
end;

function TMyTest.Get_Name: OleVariant;
begin
Result:='Name';
end;

function TMyTest.Get_SubCtrl: IMySubTest;
begin
Result:=FSubCtrl;
end;

procedure TMyTest.Initialize
//com中的初始化函数
begin
inherited;
FSubCtrl:=Self as IMySubTest
//通过变量访问借口,实际上不行,返回的还是IMyTest
end;

function TMyTest.Get_Testb: OleVariant;
begin
Result:=(Self as IMySubTest).name;
end;

procedure TMyTest.Set_Testb(Value: OleVariant);
begin

end;

initialization
TAutoObjectFactory.Create(ComServer, TMyTest, Class_MyTest,
ciMultiInstance, tmApartment);
end.

**********************************************************************
上面的例子不能解决你的问题,但说明下面几个观点
1、你的这种实现方式返回的是两个并列的接口,不是子接口。
2、这种方式好象你能用IDispatch接口调用,不能用于Variant的调用方法。
另外,我有新的发现,正在研究中......
 
呵呵,研究研究,这个问题我最多可以用500分,其他人呢?进来讨论啊,等搞定了之后放个完整的示例在这里,方便其他富翁啊。
 

找到办法了,不过挺麻烦的。
从delphihelp中查到:必须使用一种聚合的技术。帮助文件如下:
Aggregation

Sometimes, a server object makes use of another COM object to
perform some of its functions. For example, an inventory
management object might make use of a separate invoicing object
to handle customer invoices. If the inventory management object
wants to present the invoice interface to clients, however, there
is a problem: Although a client that has the inventory interface
can call QueryInterface to obtain the invoice interface, when the
invoice object was created it did not know about the inventory
management object and can't return an inventory interface in
response to a call to QueryInterface. A client that has the invoice
interface can't get back to the inventory interface.

To avoid this problem, some COM objects support aggregation. When
the inventory management object creates an instance of the invoice
object, it passes it a copy of its own IUnknown interface. The invoice
object can then use that IUnknown interface to handle any
QueryInterface calls that request an interface, such as the
inventory interface, that it does not support. When this happens,
the two objects together are called an aggregate. The invoice object
is called the inner, or contained object of the aggregate, and the
inventory object is called the outer object.

Note

In order to act as the outer object of an aggregate, a COM object must
create the inner object using the Windows API CoCreateInstance or
CoCreateInstanceEx, passing its IUnknown pointer as a parameter
that the inner object can use for QueryInterface calls.

In order to create an object that can act as the inner object of an
aggregate, it must descend from TContainedObject. When the object is
created, the IUnknown interface of the outer object is passed to the
constructor so that it can be used by the QueryInterface method on calls
that the inner object can't handle.

delphi中有一个类TContainedObject可以帮助实现这个功能,但它是在IInterface
之上的,在客户端恐怕无法看见,只能用于com内部使用,作用和子接口是一样的
我做了一个例子不成功,调用出错,并且在注册表中找不到guid,当然,如果加上
coClass是可以找到的,但TContainedObject不是一个可以自动创建com对象,没有什么意义
我察看ado的实现方式,它的filed字段不能单独创建,但注册表中可以看得见,
所以我的用法好像是错误的。但用该怎么用,是在TContainedObject的基础上实现
TAutoObject的功能,还是用别的方法,就不得而知了。
我这里没有msdn,谁有的话,找个例子看看就好了。

 
wfzha, 去下面这个帖子登个记,我把它结束了,这个帖子暂时不想结束,还想继续讨论下去。
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1095217
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
1K
DelphiTeacher的专栏
D
D
回复
0
查看
892
DelphiTeacher的专栏
D
D
回复
0
查看
2K
DelphiTeacher的专栏
D
顶部