您的位置:首页 > 产品设计 > UI/UE

转:用ATL库开发COM组件常见的几个问题 (ie不响应事件,解决悲剧的方法)

2010-02-09 15:12 946 查看
使用ATL开发COM组件时有几个问题,可能会经常遇到,并且如果不知道的话,还很难找到解决的方法。我看到有些人在CSDN上也问到了相同的问题,但很少有人给出满意的答案,所以我将这几个常见问题写在下面,以免其他人再重复劳动。

一、    问:做的一个控件,在网页里面调用时,IE浏览器总是提示:“在此页上的ActiveX控件和本页上其它部分的交互可能不安全.你想允许这种交互吗?” 请问如何将这个提示去掉。

答:在你的控件中加入如下处理即可。在你的控件的类定义部分再多继承一个父类

public IObjectSafetyImpl<CCommunicationsLink,INTERFACESAFE_FOR_UNTRUSTED_CALLER|INTERFACESAFE_FOR_UNTRUSTED_DATA>

同时在接口映射中也多加一句:

BEGIN_COM_MAP(CXXX)

         ........

 COM_INTERFACE_ENTRY(IObjectSafety)

END_COM_MAP()

进行如上处理,则IE就不会再提示了。

二、    问:我用VC的ATL库写的控件,用VB可以接收到事件,但是用IE网页却死活收不到事件?

答:一般有以下几种原因:

第一个是IE浏览器只能支持单套间的模式,你的控件只能是单套间的,不能是多套间。

第二个是IE浏览器不会主动调用Advise等注册事件的接口,所以你的控件的类定义必须再多继承以下的父类

public

IProvideClassInfo2Impl<&CLSID_Test, &DIID__ITestEvents, &LIBID_TESTLib,1,0>

同时接口映射中加入以下处理

BEGIN_COM_MAP(CTest)

    //用来查询接口类信息

    COM_INTERFACE_ENTRY(IProvideClassInfo)

    COM_INTERFACE_ENTRY(IProvideClassInfo2)

END_COM_MAP()

这样IE才能够注册准备接收事件。

参考 http://support.microsoft.com/default.aspx?scid=kb;en-us;200839
第三是你在控件中激活事件的语句(象调用Fire_Event的地方)如果不在主线程里面,而是在另外开的线程里面激活,则要经过处理才行。具体的处理步骤在微软的知识库中提到了,网址是
http://support.microsoft.com/kb/q280512/
需要下载一个该网页上的文件ATLCPImplMT.h,在控件事件实现的文件中进行如下处理:包含该文件,加上一些小修改,示例代码如下

#include "ATLCPImplMT.h" // 包含此句

 

// 注意下面一句的继承类由IconnectionPointImpl变成了 IConnectionPointImplMT

template <class T>

class CProxy_ITestEvents : public IConnectionPointImplMT<T, &DIID__ITestEvents, CComDynamicUnkArray>

{

    //Warning this class may be recreated by the wizard.

public:

    HRESULT FireEvents(DISPID p_DispID, CComVariant* p_pvars, long p_lParNum)

    {

        CComVariant varResult;

        T* pT = static_cast<T*>(this);

        int nConnectionIndex;

        int nConnections = m_vec.GetSize();

    

        for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)

        {

//  屏蔽掉向导生成的这三句

//            pT->Lock();

//            CComPtr<IUnknown> sp = m_vec.GetAt(nConnectionIndex);

//            pT->Unlock();

            // 换成下面这两句

            CComPtr<IUnknown> sp;

            sp.Attach(GetInterfaceAt(nConnectionIndex));

            IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);

            if (pDispatch != NULL)

            {

                VariantClear(&varResult);

                DISPPARAMS disp = {p_pvars, NULL, p_lParNum, 0 };

  
             pDispatch->Invoke(p_DispID, IID_NULL,
LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL,
NULL);

            }

        }

        return varResult.scode;    

    }

当然,还有象通过给主线程发消息、线程间列集等方法,不过我觉得这种操作起来更简单些,进行以上的处理后IE网页就可以接收事件了。

在网页中接收事件的语法大致如下:

 

vbs 中

 

sub 控件ID_On事件函数名(参数)

............

end sub

 

js 中

<script for="控件ID" event="事件函数名(参数)">

............

</script>

三、    问:我在控件里面有SAFEARRAY类型的数据,怎样通过事件传给IE网页或VB程序?

答:比如你的IDL定义如下:

dispinterface _ITestEvents

    {

        properties:

            methods:

            [id(1), helpstring("DataReceived")]

            HRESULT DataReceived([in] SAFEARRAY(unsigned char)* Data);    

    };

此时如果收到数据了,需要通过事件将数据传出去。在事件处理的函数中定义如下

HRESULT Fire_DataReceived(SAFEARRAY * *  Data)

你会发现ATL向导生成的代码根本无法将SAFEARRAY类型的数据传出去,其实可以将向导生成的代码进行一下修改即可

//        pvars[0] = Data;

        pvars[0].vt = VT_ARRAY | VT_BYREF | VT_UI1;

        pvars[0].pparray = Data;

将原来的pvars[0] = Data;屏蔽掉,换成下面两句即可。注意其中VT_UI1跟你发送事件前,组织SAFEARRAY数据时的类型相同,比如你用下面的函数组织数据

SafeArrayCreate(VT_UI1, 1, pSab);

则是VT_UI1,如果是其他类型要同时对应起来。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐