编写事件处理函数
好了,等了这么长时间(吹个口哨!),我们可以写事件处理函数了:
void __stdcall CMainDlg::OnDownloadBegin() { // show "Please wait" here... } |
现在来看一个复杂一点的事件,比如BeforeNavigate2,这个事件的原型是:
void BeforeNavigate2 ( IDispatch* pDisp, VARIANT* URL, VARIANT* Flags, VARIANT* TargetFrameName, VARIANT* PostData, VARIANT* Headers, VARIANT_BOOL* Cancel ); |
此方法有7个参数,对于VARIANT类型参数可以从MSDN查到它到底传递的是什么类型的数据,我们感兴趣的是URL,是一个BSTR类型的字符串。
描述BeforeNavigate2事件的_ATL_FUNC_INFO结构是这样的:
_ATL_FUNC_INFO BeforeNavigate2Info = { CC_STDCALL, VT_EMPTY, 7, { VT_DISPATCH, VT_VARIANT|VT_BYREF, VT_VARIANT|VT_BYREF, VT_VARIANT|VT_BYREF, VT_VARIANT|VT_BYREF, VT_VARIANT|VT_BYREF, VT_BOOL|VT_BYREF } }; |
和前面一样,返回值类型是VT_EMPTY表示没有返回值,nParams是7,表示有7个参数。接着是参数类型数组,这些类型前面介绍过了,例如VT_DISPATCH表示IDispatch*。
事件响应链的入口与前面的例子很相似:
BEGIN_SINK_MAP(CMainDlg) SINK_ENTRY_INFO(37, DIID_DWebBrowserEvents2, DISPID_DOWNLOADBEGIN,OnDownloadBegin, &DownloadInfo) SINK_ENTRY_INFO(37, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2,OnBeforeNavigate2, &BeforeNavigate2Info) END_SINK_MAP() |
事件处理函数是这个样子:
void __stdcall CMainDlg::OnBeforeNavigate2 ( IDispatch* pDisp, VARIANT* URL, VARIANT* Flags, VARIANT* TargetFrameName, VARIANT* PostData, VARIANT* Headers, VARIANT_BOOL* Cancel ) { CString sURL = URL->bstrVal;
// ... log the URL, or whatever you''d like ... } |
我打赌你现在是越来越喜欢ClassWizard了,因为当你向MFC的对话框插入一个ActiveX控件时ClassWizard自动为你完成了所有工作。
将CMainDlg转换成对象需要注意几件事情,首先必须修改全局函数Run(),现在CMainDlg是个COM对象,我们必须使用CComObject创建CMainDlg:
int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT) { CMessageLoop theLoop; _Module.AddMessageLoop(&theLoop);
CComObject<CMainDlg> dlgMain;
dlgMain.AddRef();
if ( dlgMain.Create(NULL) == NULL ) { ATLTRACE(_T("Main dialog creation failed!\n")); return 0; }
dlgMain.ShowWindow(nCmdShow);
int nRet = theLoop.Run();
_Module.RemoveMessageLoop(); return nRet; } |
另一个可替代的方法是不使用CComObject,而使用CComObjectStack类,并删除dlgMain.AddRef()这一行代码,CComObjectStack对IUnknown的三个方法的实现有些微不足道(它们只是简单的从函数返回),因为它们不是必需的--这样的COM对象可以忽略对引用的计数,因为它们仅仅是创建在栈中的临时对象。
当然这并不是完美的解决方案,CComObjectStack用于短命的临时对象,不幸的是只要调用它的任何一个IUnknown方法都会引发断言错误。因为CMainDlg对象在开始监听事件时会调用AddRef,所以CComObjectStack不适用于这种情况。
解决这个问题要么坚持使用CComObject,要么从CComObjectStack派生一个CComObjectStack2类,允许对IUnknow方法调用。CComObject的那个不必要的引用计数并无大碍--人们不会注意到它的发生--但是如果你必须节省那个CPU时钟周期的话,你可以使用本章的例子工程代码中的CComObjectStack2类。
|