自己实现IDispatch::Invoke方法

 

 

因为种种原因,在只能得到一个IWebBrowser指针的情况下要接收javascript的window.external.XXX调用,

 

不得已自己实现了IDocHostUIHandler和IDispatch,为了使用方便,自己又需要实现类似MFC的DISPATCH_MAP:

首先用一个结构体保存每个DISPATCH方法的ID,名字,this指针,函数地址,返回值类型,参数类型列表。这些都没有什么难度。

然后在IDispatch::GetIDsOfNames里面通过方法名称查找到方法ID返回。

最关键的是IDispatch::Invoke方法的实现,怎么实现可变参数调用。

一、根据Invoke方法的参数与对应的保存的结构体信息对参数个数,参数类型,返回值类型做安全校验。

二、因为保存的函数地址指针,没法确定函数原型(和MFC的DISPATCH_MAP一样,参数个数、类型和返回值类型都是不定的),

只好想用汇编的call指令来直接调用,但参数入栈又是一个难题。

三、后来想了一个办法,因为VC++里面标准__thiscall调用约定,会按顺序把参数压入栈,把this指针写入ECX寄存器,

所以我先根据参数类型把所有参数序列化到一个buffer里,得到所有参数总长度size,然后在栈上分配长度为size的空间,把buffer直接copy到分配好的栈里。然后直接用call指令调用函数,这其中还有要注意的一些东西,一是原寄存器的保存,及调用后的还原。二是不要修改EBP,不然在后面的汇编指令里不能直接使用原来的局部变量了,因为临时变量和参数实际都是通过EBP这个栈帧来快速访问(访问参数用EBP+XX、访问局部变量用EBP-XX)。三是保持栈平衡,寄存器入栈、出栈,分配的栈空间释放等。

四、返回值的获取,32位返回值会保存在EAX里,64位返回值分别在EDX和EAX保存高低32位,我的做法是直接用一个64位变量取出EDX和EAX值,然后根据函数实际的返回值类型取对应的8/32/64位,因为在汇编里做这些判断是很麻烦的,而这个取EDX和EAX的值对程序又不会有影响。

下面是关键代码:

 

 

_asm{
		push ecx;
		mov ecx, pthis;  //把this指针压入ECX
                push ebx;
		mov ebx, esp;   //保存栈帧,不要使用ebp, 会导致后面不能直接使用栈变量
		sub esp, liSize;   //移动栈指针 分配liSize大小的栈空间

		push esi;       //memcpy begin 把序列化好的参数copy到分配好的栈里
		push edi;
		push ecx;
		mov ecx, liSize
		mov esi, pStack;
		mov edi, ebx;
		sub edi, liSize
		rep movsb;
		pop ecx;
		pop edi;
		pop esi;        //memcpy end

		call fn;       //调用函数
		mov esp, ebx;
		pop ebx;
		
		mov dword ptr[result], eax; //取返回值 如果没返回值 这个也不会有影响
		lea eax, [result];
		add eax, 4;
		mov dword ptr[eax], edx;   //假定都返回64位返回值 这里取高32位
	}

后来才知道,微软有一个API实现了这个功能。

 

HRESULT DispInvoke(
  void *_this,
  ITypeInfo *ptinfo,
  DISPID dispidMember,
  WORD wFlags,
  DISPPARAMS *pparams,
  VARIANT *pvarResult,
  EXCEPINFO *pexcepinfo,
  UINT *puArgErr
);
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值