核心提示:现在逐步解析一下,避免以后大家出现我一样的问题。所有的通过Variant调用接口都会通过IDispatch的GetIDsOfNames,通过名字来获取ID(至于怎么通过名字来访问,因为编译器为接口中的...
现在逐步解析一下,避免以后大家出现我一样的问题。所有的通过Variant调用接口都会通过IDispatch的GetIDsOfNames,通过名字来获取ID(至于怎么通过名字来访问,因为编译器为接口中的每个方法生成一个静态的TCallDesc结果,如果你知道某个方法的TCallDesc结构地址,你可以通过这行代码就知道显示的是什么了,ShowMessage(pchar(@PCallDesc($4873f4)^.ArgTypes[1]));),然后通过ID来调用Invoke方法。在delphi内部有一个包装过程,最后通过的是Variant单元的_DispInvoke方法来转发这个过程,下面是_DispInvoke的核心代码:
if Assigned(VarDispProc) then
VarDispProc(PVariant(LDestPtr), Variant(Source), CallDesc, @Params);
VarDispProc是一个全局的函数指针,在单元初始化的时候是一个错误函数接口赋值给它(这个错误的字符串信息就是臭名昭著的"Variant method calls not supported")。VarDispProc := @_DispInvokeError;开始我以为真正的代码是由delphi编译器自动加入的,毕竟见得太多类似的“魔法”了:-) 但后来发现自己的接口连脚本也运行不了,最后只得在delphi的Source里面搜索VarDispProc了(这里我推荐大家使用“百度硬盘搜索”,真是太方便了),马上发现了端倪:在ComObj单元的初始化中有这么一句:VarDispProc := @VarDispInvoke;
再看看VarDispInvoke代码,正是真正的派发函数:-) ,在开发的接口单元uses部分加入ComObj,完全OK了,后来自己觉得不爽,写了一个函数,在没有引用ComObj单元的情况下发现也可以很好的运行了o(∩_∩)o...。
下面是我自己实现的一个简单的派发接口,在一般情况下也够用了:-)
procedure MyDispatch(Dest: PVariant; const Source: Variant;
CallDesc: PCallDesc; Params: Pointer); cdecl;
var
IDisp:IDispatch;
pDispid,wcharlen:integer;
pwc:pwidechar;
pc:pchar;
begin
if TVarData(Source).VType=varDispatch then
begin
IDisp:=IDispatch(TVarData(Source).VDispatch);
pc:=pchar(@callDesc^.ArgTypes[callDesc^.ArgCount]);
wcharlen:=MultiByteToWideChar(0,0,pc,strlen(pc),nil,0);
pwc:=allocMem(wcharlen*2+2);
MultiByteToWideChar(0,0,pc,strlen(pc),pwc,wcharlen);
IDisp.GetIDsOfNames(GUID_NULL,pointer(@pwc),calldesc^.NamedArgCount,0,@pDispid);
FreeMem(pwc);
IDisp.Invoke(pDispid,GUID_NULL,0,0,Params,nil,nil,nil);
end
else
raise exception.Create('Variant method calls not supported');//我也发出这个语句:-)
end;
在初始化和终止化的时候替换原来的地址就可以了,
initialization
Old:=Variants.VarDispProc;
Variants.VarDispProc:=@MyDispatch;
finalization
Variants.VarDispProc:=Old;
end.
上面代码经过delphi7的测试,没有uses ComObj也可以正常派发接口调用。