VC6 COM控件增加事件支持

 

前一阵子写一个控件,感觉接口有些地方使用返回值控制不大方便使用,所以打算给控件增加上几个事件,结果在网上搜了很久,也看不到合适的方法,网上说的一些东西,在我的VC环境中看的时候,根本就没有办法那么操作(包括VC6和VC2010)。于是找了一个示例程序,反反复复试验了好多次,终于自己的控件能够通过事件回调了,现在把我的试验方法记录下来。(网上说的更多是在建立控件工程时增加事件,如果对于一个建立工程时没有时间支持的控件,根本没有提到的)。

 

我的试验中提到的内容,不知道是否都是必要的,我也没有在这方面再去尝试。

 

(1)修改控件类的继承列表,如果需要有事件支持,需要继承以下的类:

 

IDispatchImpl

IProvideClassInfo2Impl

IConnectionPointContainerImpl

 

 

继承这些类,有可能还需要在 BEGIN_COM_MAP 和END_COM_MAP加入相应的宏。

加入连接点支持,需要使用 BEGIN_CONNECTION_POINT_MAP、CONNECTION_POINT_ENTRY和END_CONNECTION_POINT_MAP加入连接点的宏

 

 

 

 

 

(2)IDL中声明事件支持,在Library一节中,CoClass上面,如下方式说明:

 

    importlib("stdole32.tlb");
    importlib("stdole2.tlb");
    [
        uuid(95F04622-2670-4206-AB52-03CDE8CA8074),
        helpstring("_IMyCtrlEvents Interface")
    ]

    dispinterface _IMyCtrlEvents
    {
        properties:
        methods:
        [id(1), helpstring("method OnOK")] HRESULT OnOK();
        [id(2), helpstring("method OnError")] HRESULT OnError(LONG err,BSTR msg);
    };

 

 

 

(3)实现代理类,在其中实现触发事件的代码,例如:

 


template <class T>
class CProxy_IMyCtrlEvents : public IConnectionPointImpl<T, &DIID__IMyCtrlEvents, CComDynamicUnkArray>
{
    //Warning this class may be recreated by the wizard.
public:
    HRESULT __stdcall Fire_OnOK()
    {
        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();
            IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
            if (pDispatch != NULL)
            {
                VariantClear(&varResult);
                DISPPARAMS disp = { NULL, NULL, 0, 0 };
                pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
            }
        }
        return varResult.scode;
   
    }


    HRESULT __stdcall Fire_OnError(LONG err,BSTR msg)
    {
        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();
            IDispatch* pDispatch = reinterpret_cast<IDispatch*>(sp.p);
            if (pDispatch != NULL)
            {
                VariantClear(&varResult);
                //DISPPARAMS disp = { NULL, NULL, 0, 0 };
                DISPPARAMS dispparams;
               memset(&dispparams, 0, sizeof(dispparams));
               dispparams.cArgs=2;   // 设置参数个数
               dispparams.rgvarg=new VARIANTARG[1]; // 初始化参数数组
               dispparams.rgvarg[0].vt=VT_UI4; // 第一个参数的类型
               dispparams.rgvarg[0].uintVal=(UINT)err;
               dispparams.rgvarg[1].vt=VT_BSTR; // 第一个参数的类型
               dispparams.rgvarg[1].bstrVal=(BSTR)msg;              

                pDispatch->Invoke(0x2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, &varResult, NULL, NULL);
            }
        }
        return varResult.scode;
   
    }
};

 

 

 

我们可以看到,触发事件的FireEvent实现中,特殊之处在于,要把参数进行赋值,再调用定义的事件,0x1、0x2就是定义的事件。

 

 

实现之后,还需要在控件类定义处加入代理类,如(1)中需继承的其他类。

 

(4)有了这些之后,就可以在需要的地方触发事件,例如,在出现某种错误或情况时触发onError事件

 

(5)请注意,不要在控件所起的线程中触发事件,如果需要的话,请在线程中需要触发事件的地方给控件窗口(仅限控件有窗口的情况)发送自定义的消息,由控件窗口接收到消息后触发事件。要使用线程消息,需要在控件类定义时,在BEGIN_MSG_MAP和END_MSG_MAP中定义消息处理函数。

 

 

 

(6)一个题外话,控件类还应该继承 IObjectSafetyImpl,并在COM_MAP中声明宏,这样在浏览器中使用时,就不会弹出“在此页上的ActiveX控件和本页上的其它部分交互可能不安全”这样的提示。当然,要更好的解决安全问题,还需要考虑对控件数字签名等等方式。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(VC 控件开发 事件处理)