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

使用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,如果是其他类型要同时对应起来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值