对于第一种情况,一般是在普通程序里头使用这样的COM组件,为了得到这样的通知我们必须要实现一个连接点接口的COM组件。从关于连接点的开发知识我们知道这是一个继承自IDispatch的接口,而我们实现这个组件只是想要获得通知,所以很多东西都可以用简单的方式处理掉,不需要注册等等这些我们不关心的功能。对于这样的一个组件一般而言会是这样的一个类声明:
- class CMathEvent
- : public CComObjectRoot
- , public _IMathEvents
- {
- BEGIN_COM_MAP(CMathEvent)
- COM_INTERFACE_ENTRY(IDispatch)
- COM_INTERFACE_ENTRY(_IMathEvents)
- END_COM_MAP()
- public:
- STDMETHODIMP GetTypeInfoCount(UINT *pctinfo)
- STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
- STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
- STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
- //后面是这个接口的自定义方法
- };
作为一个COM组件那么他派生自CComObjectRoot这个基类,当然你也可以用CComObjectRootEx这个基类,这两个基类有什么区别参考《深入解析ATL》,另外这个类实现了接口_IMathEvents这个连接点。我把类声明当中实现自定义方法这部分删掉了,上面这个声明里头几个函数都是用来实现_IMathEvents的基类IDispatch的方法,由于我们只是想要达到获得通知这样一个目的,所以对于我们来说最关心的应该是Invoke这个方法,对于其他的方法我们只要简单的返回E_NOTIMPL即可。关于Invoke这个方法,参数列表里头的DISPID表明了被Dispatch的方法的ID,根据这个ID我们可以调用具体的自定义方法实现。riid和LCID分别表示接口的IID和组件的CLSID,另外两个重要的参数是pDispParam和pVarResult,前一个表示了被Dispatch的方法调用时传递进来的参数列表,对他做一些必要的检查和转换后把用它来调用真正的方法实现,而后面那个表示如果这个方法有返回值那么使用这个指针来返回。下面是一个简单的Invoke实现,只包括了很少的一部分检查
- STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
- {
- if (dispIdMember == 0x01)
- {
- VARIANT varparam;
- VariantInit(&varparam);
- VariantChangeTypeEx(&varparam,&pDispParams->rgvarg[0],lcid,0,VT_I4);
- ComputeResult(varparam.lVal);
- return S_OK;
- }
- return S_FALSE;
- }
ComputeResult这个函数是我定义的一个事件,具体实现很简单显示一个对话框而已,如下:
- STDMETHODIMP ComputeResult(LONG lResult)
- {
- CString szMsg;
- szMsg.Format(_T("Result = %d"),lResult);
- ::MessageBox(NULL,szMsg,_T("ConnMath Callback"),MB_OK);
- return S_OK;
- }
只说一点,这里的CString并不是通过包含MFC库得到的,VS2005里头的ATL7已经包含了这个类,所以以后操作字符串也可以像MFC一样方便了。到这里我们要的COM组件已经实现完成了,要注意的是,因为我用的是最简单的WIN32控制台程序来实现这些东西,所以没有任何向导可以用,都是手写,没试过MFC项目不知道会不会简单一点,如果你试了请你留言告诉我有没有向导可以用。最后还有一点扫尾工作要做,那就是ATL环境,改好你的项目属性,检查一下stdafx.h里头是不是已经包含了atlbase.h和atlcom.h这两个头文件。然后再你的CPP文件里面的全局变量部分写下下面的代码
- CComModule _Module;
- BEGIN_OBJECT_MAP(ObjectMap)
- END_OBJECT_MAP()
最后在CoInitialize调用后面加上_Module.Init( ObjectMap, 0 )的调用,接着就是你的组件创建还有AtlAdvise的调用了,剩下的不详细讲了,上个简单的代码吧
- int _tmain(int argc, _TCHAR* argv[])
- {
- CoInitialize(NULL);
- _Module.Init( ObjectMap, 0 );
- CComPtr<IMath> mathPtr;
- mathPtr.CoCreateInstance(__uuidof(ConnectMath));
- CComObject<CMathEvent> *MathEventPtr;
- CComObject<CMathEvent>::CreateInstance(&MathEventPtr);
- DWORD dwCookie;
- HRESULT hr = AtlAdvise(mathPtr,MathEventPtr,__uuidof(_IMathEvents),&dwCookie);
- hr = mathPtr->Add(1,2);
- mathPtr->Minus(2,3);
- AtlUnadvise(mathPtr,__uuidof(_IMathEvents),dwCookie);
- mathPtr.Release();
- CoUninitialize();
- return 0;
- }
对第二种情况,也就是创建一些COM组件来对数据进行不同的处理,因为本身就是要创建COM组件的,所以可以方便的使用到VS里面的向导来实现,并没有什么太大的代码量,代码的集中点都是自定义的一些实现,所以在这里也就不描述了