怎样在COM的事件中返回参数。(200分)

E

ebow

Unregistered / Unconfirmed
GUEST, unregistred user!
我需要在一个过程中返回一些参数,象在vcl中的事件
Procedure OnXXXX( var RetCode: integer)
begin
.....
RetCode := 100; // 这样就可以返回参数了。
end;

但是在我写的Com的事件里,却无法返回。
Procedure OnComXXX( var RetCode : integer);
begin
...
RetCode := 100; //却无法将数值传递回去。
end;
请问谁知道原因??有没有什么办法解决???

 
to ebow:

你需要将该过程的参数Retcode定义为variant类型,
Com在跨进程边界传递参数时,需要用variant类型。还有关于COM
方面的编程,我介绍一本书你看看,就叫delphi COM深入编程。
 
楼上的,不知道你是否在事件里试过,你说的数据类型我测试过,也不行。
如果你有方法可以的话,能不能把测试代码贴出来??
 
不是的,你看看click和keyPress的事件在xxx_TLB中申明的区别,我想,你可以改这个吧
 
TO going_cc:
你说的是Activex Control,直接从vcl 控件转过来的,我说的是Automation Object,因为
我做的是自动化服务器,不是ocx控件,我比较了下,它们继承的类不同。谁在自动化服务
器里做过可返回参数的事件??
 
IEditXEvents = dispinterface
['{0D96B3C6-803D-4288-AD39-909242E4F76F}']
procedure OnChange; dispid 1;
procedure OnClick; dispid 2;
procedure OnDblClick; dispid 4;
procedure OnKeyPress(var Key: Smallint); dispid 10;
end;
这是事件的出接口,自动化对象中也是这样实现的,其实是用的dispinterface为出接口了
都是连接点对象的原理
自动化对象的事件发生的时候调用客户端的委托代码
我们在自动化对象的自定义事件中当然给了这个事件的参数,就好象:
procedure OnKeyPress(var Key: Smallint); dispid 10;
不过要返回值的话,我要说的是:
好象由于是用dispinterface,而且客户端的事件绑定的实现代码可能就没有实现out的处理,过会儿我会去看看代码的,
没有实现,自己改一下也是可以的。
 
〉我们在自动化对象的自定义事件中当然给了这个事件的参数,就好象:
〉procedure OnKeyPress(var Key: Smallint); dispid 10;

这种参数没有起作用,无法返回值,var跟没有一样。
盼望指教
 
是啊,这是in类型的参数?不是out类型的
而且,我们可以看ActiveX有in参数的事件委托代码:
procedure TButtonX.KeyPressEvent(Sender: TObject; var Key: Char);
var
TempKey: Smallint;
begin
TempKey := Smallint(Key);
if FEvents <> nil then FEvents.OnKeyPress(TempKey);
Key := Char(TempKey);
end;
这里明显把我们这个组件的这个事件发生时的执行代码委托给了FEvents(就是IButtonXEnents)的方法
这样客户端实际是实现这个方法(当然也实现了这个对象,这是内部机制了)
我们自己可以在类型库中的事件接口的定义加入out参数,
控件(支持事件的自动化一样的)在客户端的事件代码中,就写回这个参数
这样,我们(就是自动化组件)就可以获得数据了。
 
我在自动化对象的事件里添加一个[in,out]的类型参数,
HRESULT ontest([in] long Param1, [in, out] long * handle );
会自动生成一个过程。
ItestEvents = dispinterface
['{41CFF2C1-ACC6-4E6B-B79F-B81E743FC9CF}']
procedure ontest(Param1: Integer; var handle: Integer); dispid 1;
end;
可以看到out类型已经被定义成了var类型。
到这里都是符合逻辑的,但是在客户端使用的时候却不起作用了。
即使对handle进行赋值,也没改变。
服务端:
procedure TTest.Sendmeback(handle: integer);
var
EC: IEnumConnections;
ConnectData: TConnectData;
Fetched: Cardinal;
Temp :integer;
begin
EC := GetConnectionEnumerator;
if EC <> nil then
begin
temp := 100;
while EC.Next(1,ConnectData,@Fetched) = S_OK do
begin
try
if ConnectData.pUnk <> nil then
(connectData.pUnk as ItestEvents).ontest(handle,Temp);
// 在这里向每个客户端发起事件。
// 按逻辑事件结束后,temp应该被客户端改成11。但问题出现了,Temp原封不动。

except
end;
end;
end;
end;

客户端:
t1 := TTest.Create;
t1.Ontest := TestProc;
t1.connect;

procedure TestProc((Sender: TObject;Param1: Integer;var handle: integer);
begin
memo1.lines.Add(inttostr(handle));
handle := 11; <---- 这里,按逻辑应该改变了,handle 原来=100,现在应该=11
end;

To Going_cc: 你可以做个自动化服务试验一下。在Activex Control中是正常的。


 
真的呀,我现在在找原因,好象是自动化服务器内部在出接口处理的时候没有处理Out类型的。
本以为只有异步事件才不能处理Out,这里居然也没有处理,自己改或者自己实现接配器。
 
TO going_cc:
是啊,怎么事件还有异步的么?改天好好向你请教。
我查了一下,问题出在Toleserver类里,我不知道怎么改。盼指教。
 
修正:
我跟踪了代码,问题好像出在OleServer单元的 TServerEventDispatch类里。
在 TServerEventDispatch.Invoke方法里就无法将参数返回了。
不知道going_cc你怎么认为?
 
我刚开始接触Delphi,没有什么材料,自己看了点代码,Toleserver还没有看呢^_^
这两天打算看,刚大概把服务器端的一些实现看了一下。自动化组件在服务器端的实现方面大概是没有问题的。
我想,问题可能就是在TServerEventDispatch里了。
 
对Delphi这种语言不了解,好象算是强类型的语言。
不知道为什么会有TDispatchSilencer这样的类,而起
EventSinkChanged(TDispatchSilencer.Create(Sink, FAutoFactory.FEventIID));
这样的作用,我们看到的就是
procedure Tsola.EventSinkChanged(const EventSink: IUnknown);
begin
FEvents := EventSink as IsolaEvents;
end;
我就不解,为什么在我们的服务器中对与出接口要一个包装,而不直接使用获得的接口指针
感觉有可能是类型转换的原因,直接用指针可能无法通过编译
 
哈哈
老大,问题发现了,我的定义是:
IthirdEvents = dispinterface
['{DD188522-7B6D-4243-A6A3-F78B2D783914}']
procedure Method1; dispid 1;
procedure Method2(gogogo: Integer; var back: Integer); dispid 2;
end;
自动生成的函数原形是:
TthirdMethod2 = procedure(Sender: TObject; gogogo: Integer; var back: OleVariant) of object;
到这里都没有问题,但是看下面:
procedure Tthird.InvokeEvent(DispID: TDispID; var Params: TVariantArray);
begin
case DispID of
-1: Exit; // DISPID_UNKNOWN
1: if Assigned(FOnMethod1) then
FOnMethod1(Self);
2: if Assigned(FOnMethod2) then
FOnMethod2(Self, Params[1] {var Integer}, Params[0] {Integer});
end; {case DispID}
end;
现在可以发现FOnMethod2(Self, Params[1] {var Integer}, Params[0] {Integer});
出现了问题,参数的顺序反了^_^
自己实验一下吧。
我看了代码TServerEventDispatch.Invoke写的很完美,没有问题。
 
不好意思,刚才的内容是一个问题,还有一个问题:
我发现了运行环境在自己定义的时候,可能会这样:
IthirdEvents = dispinterface
['{DD188522-7B6D-4243-A6A3-F78B2D783914}']
procedure Method1; dispid 1;
function Method2(gogogo: Integer; var back: Integer; last: Integer): Integer; dispid 2;
end;
这个时候你会发现我们纯Out的值变成函数返回值,而结果就是参数传递时候就没有了这个值,
而返回值也没有被回收,传回。

////////////////
我以为,我们发现了Delphi环境的bug
 
对于第一个参数顺序问题,我这里并没有发现啊,
Ttestontest = procedure(Sender: TObject; Param1: Integer; var handle: OleVariant) of object;
在实现中;
procedure Ttest.InvokeEvent(DispID: TDispID; var Params: TVariantArray);
begin
case DispID of
-1: Exit; // DISPID_UNKNOWN
1: if Assigned(FOnontest) then
FOnontest(Self, Params[0] {Integer}, Params[1] {var {??TGUID} OleVariant});
end; {case DispID}
end;
并没有你说的顺序相反问题,你是不是手工写的??我是用typelibrary自动生成。
这是第一个问题,第二个问题就是在事件里,如果我在typelibrary里定义一个带返回参数
的方法,自动生成的代码中返回参数就不见了。不知道你有没有试过。
 
参考TServerEventDispatch源代码,下面是它的事件分发过程。

function TServerEventDispatch.Invoke(DispID: Integer; const IID: TGUID;
LocaleID: Integer; Flags: Word; var Params;
VarResult, ExcepInfo, ArgErr: Pointer): HResult;
var
ParamCount, I: integer;
VarArray : TVariantArray;
begin
// Get parameter count
ParamCount := TDispParams(Params).cArgs;
// Set our array to appropriate length
SetLength(VarArray, ParamCount);
// Copy over data
for I := Low(VarArray) to High(VarArray) do
VarArray[High(VarArray)-I] := OleVariant(TDispParams(Params).rgvarg^);
// 在这里它把参数复制给了一个本地临时数组。

// Invoke Server proxy class
if FServer <> nil then FServer.InvokeEvent(DispID, VarArray);
// 在这里调用具体的Invoke过程。
// 注意VarArray,如果在这里设一个断点,你能发现Vararray的确可以被客户端的事件
// 过程改变,也就是说参数被传递回来到这里了。

// Clean array
SetLength(VarArray, 0);
// 我怀疑的就是这里,delphi简单的把临时数组释放了,但我们想要的返回参数还在里面。

// Pascal Events return 'void' - so assume success!
Result := S_OK;
end;

going_cc, 你可以尝试跟踪测试一下,不知道我是否看错了。
看来你只有晚上才能回复,我白天只能工作之余来看看,现在我只能通过传递
指针来暂时解决这个问题,但这种临时方法只能用在InProc服务里,真是麻烦。

 
出入参数都在OleVariant里,这不是问题,如果单纯的是Out,val之类的参数,类型库工具
会把他改成一个function,这样,实现端InvokeEvent就无法提取返回值。
还有,你的:
FOnontest(Self, Params[0] {Integer}, Params[1] {var {??TGUID} OleVariant});
这里的参数顺序是反了,Params是反过来的,你的顺序反了,不是0123,而是3210
这是Com的规定
反正我觉得无论源代码还是类型库工具,都有些问题。
 
对了,我南京的,你呢?
 
顶部