Advertisement

E COM研究笔记 / 挂接事件

阅读量:

为COM挂接一个回调接口,在如C++,C#中可以调用AtlAdvise或从

IConnectionPointContainer挂接(Sink必须具象继承接口的全部成员)

但易语言则不同,易语言在COM支持上一直存在缺陷,易语言是一个

强类型语言,而“Object / 对象”似乎很类似IDispatch*但似乎又不是

而易语言本身则未提供任何可以具备事件点对点的方法 需要挂接的话

着实令人头疼不已

// IDispatch* pdisp = NULL;
// if(SUCCEEDED(CoInitialize(NULL)) != FALSE)
// {
// CoCreateInstance(__uuidof(CLSID_ScriptControl), NULL, CLSCTX_INPROC_SERVER, __uuidof(IDispatch), (void**)&pdisp);
// }

// UINT pctinfo;

// IDispatch* pdisp;

// pdisp->GetTypeInfoCount(&pctinfo);

// mov esi,esp

// lea eax,dword ptr [pctinfo]
// push eax

// mov ecx,dword ptr [pdisp]
// mov edx,dword ptr [ecx]
// mov eax,dword ptr [pdisp]
// push eax

// mov ecx,dword ptr [edx+0Ch]
// call ecx

上面是一个调用IDispatch::GetTypeInfoCount的代码及内联汇编

可以得到下述内容:

// express: ((int)(((int)pdisp) + 0xC))

上图则为易语言的部分,实际上易语言对象内包含的是IUnknown*

如果调用“Object::QueryInterface”则会返回IID所对应的指针,但

内部会被以IDispatch*表示、


上面则是一段PB(Power Basic)的代码,先把CLSID与IID压入变

量后创建COM实例,在从中得到“虚函数指针”具体参照下文

CALL DWORD 调用函数地址(四字节),综合上述与下述代码可以

得到关于COM类结构形式、倒不如说是一个类与接口的结构形式、

上面的易语言代码很简单,复制并构建一个IDispatch接口类但是

它是一个具象类,该类是由汇编代码进行复制并构造

typedef class IUnknown
{
public:
virtual int AddRef() = 0;
virtual int Release() = 0;
} *LPIUnknown;

typedef class IDispatch : public IUnknown
{

public:
int AddRef() override
{
return 2;
}

int Release() override
{
return 1;
}
} *LPIDispatch;

上面是一个类与一个接口的结构、仔细观察与思考上述代码
int _tmain(int argc, _TCHAR* argv[])
{
IDispatch* idisp = new IDispatch;

int x = idisp->AddRef();
int y = ((int)(((int)idisp) + 4)); // Release
_asm
{
call dword ptr[y]
mov dword ptr[y], eax
pop eax
}
printf("{x=%d, y=%d}", x, y); // {x=2, y=1}
getchar();
return 0;
}

接口中所有成员都是抽象的,具象由实体重写成员

仔细看看上图,在一般类情况下父类不存在 “__vfptr / 虚函数指针”

它用于寄存被重写的父类成员地址链表、

那么回到正题,由于此处研究易语言COM事件挂接的问题,那么

需要知道Sink的结构是什么,上述已经提到事件接受器(EventSink)

Sink必须具象继承接口的全部成员、

COM内部托管被挂接的Sink,共有一份与Sink相同接口协议公约、

它们所有的一切都惊人的类似

但是Sink是具象方而COM只是抽象调用方,那么需要知道常规类

函数(方法)是静态固定的、

正常情况下只可以获取到静态类函数的地址,而不可获取类成员

函数地址,但是可以通过union或inline-asm获取它的地址 不过需

要提到一点,即使虚函数是以一个虚函数指针寄存但不可否认它

包含值是静态的、所谓函数皆静态可不是闹着玩的 要是托管代码

我倒也无话可说,代码是静态的运行时是动态(JIT Auto Complie)

但是会被优化,何时为动态我举个小例子一旦获取函数指针那么

则会重新构建该函数但只针对托管代码(.NET) 一般只编译一次

上面是在易语言中被挂接的COM IDispatch Sink 那么需要知道

COM中IDispatch::Invoke的作用,它是一个整个COM互调用的

核心部分,当COM调用函数或属性时都会最终由该函数处理

在COM中拥有举足轻重的地位、如果没它COM则会变得无意义

该部分是必须的,每次COM接口调用函数时都会先调用该函数

获取对应COM接口的指针 如果ppvObject远过程返回无效 那么

最终结果不是软体停止工作那么就是没有任何反应、

在易语言中有一个与VARIANT相同的类型、及“变体型” 上述代码

表示侦听dispid 0x66及响应DWebBrowserEvents2::StatusTextChange

由于COM传递参数全部以VARIANT所以在这里编写一个函数

PTR_TO_VARIANT 方便调用 方法倒也很简单,使用汇编交换两者地址

然后再返回 不然则需要使用API去绕过易语言的类型检查 没办法的事

不知为什么编写一个CALL COM接口函数的汇编时在易语言中无法使用 倒

是在C++中内嵌时没问题 倒是很让我有些头疼 不过幸好有一种替代方案

汇编 虽然不是我写的、呵呵

差不多了、剩下的还需要靠大家自己理解了 不可说,不可言 一朵相似之花

项目源代码: http://pan.baidu.com/s/1hqgyRPy

全部评论 (0)

还没有任何评论哟~