任何基于 CDialog 或 CCmdTarget 的类,如我自定义的CWebMethod类,都可以通过传入实例化ActiveX的类对象的指针地址(通常为自动生成的CExplorer1类的对象),通过取地址符&给你的类内成员赋值或直接用临时指针调用如下参数即可.
[code=C/C++]EnableAutomation();
IUnknown* pUnk;
pUnk = pWebBrowser->GetControlUnknown();
CComQIPtr<IWebBrowser2> pWeb;
pUnk->QueryInterface(IID_IWebBrowser2,(void**)&pWeb);
IConnectionPointContainer* pCPContainer;
DWORD m_dwCookie = 0;
HRESULT hr = pWeb->QueryInterface(IID_IConnectionPointContainer,(void**)&pCPContainer);
IConnectionPoint* m_pConnectionPoint;
hr = pCPContainer->FindConnectionPoint(DIID_DWebBrowserEvents2,
&m_pConnectionPoint);
hr = m_pConnectionPoint->Advise(GetInterface(&IID_IUnknown), &m_dwCookie);
return hr;[/code]
如果你的pWeb 浏览器指针非一个可视的对话框内控件 也可通过shellwindow等方式获得 进而可以省略如下部分
[code=C/C++]CComQIPtr<IWebBrowser2> pWeb;
pUnk->QueryInterface(IID_IWebBrowser2,(void**)&pWeb);[/code]
在此类内加入事件的处理
[code=C/C++]BEGIN_DISPATCH_MAP(CWebMethod,CCmdTarget)
DISP_FUNCTION_ID(CWebMethod,"随意内容",259,OnDocumentCompleteExplorer1,VT_EMPTY, VTS_DISPATCH VTS_PVARIANT)
END_DISPATCH_MAP()[/code]
当然你还需要在.h内声明DISPATCH_MAP ,即在.h内加入
[code=C/C++]DECLARE_DISPATCH_MAP()[/code]
具体声明的 DECLARE_DISPATCH_MAP() 可否被 DECLARE_EVENTSINK_MAP()所代替,有待各位深入进行测试.不过看起来是没有必要的.
处理了这样一个"回调函数"的引入后 定义回调函数为:
[code=C/C++]void CWebMethod::OnDocumentCompleteExplorer1(LPDISPATCH pDisp, VARIANT* URL)
{
IUnknown* pUnk;
LPDISPATCH lpWBDisp;
HRESULT hr;
pUnk = my_pWebBrowserCtr->GetControlUnknown();
ASSERT(pUnk);
hr = pUnk->QueryInterface(IID_IDispatch, (void**)&lpWBDisp);
ASSERT(SUCCEEDED(hr));
if (pDisp == lpWBDisp )
{
my_WebIsBusy=false;
TRACE("CCreateHTMLImage::DocumentComplete\n");
//::MessageBox(0,L"页面加载完成",L"成功",0);
//EndModalLoop(0);
}
lpWBDisp->Release();
//捕捉到网页完全下载/打开成功事件
}[/code]
这个函数 是一个 完全的 彻底的 跨框架的检测方式 只有在网页内的所有Frame/iFrame加载完成,my_WebIsBusy才为false 当然 my_WebIsBusy 是我定义的一个类内变量.
你可以在定义的消息循环内 检测这个my_WebIsBusy的值,从而达到Web页不加载完成不进行下一步动作的判断.这样大大提高了网页内各个元素的提取. 简易声明如下:
[code=C/C++]HRESULT CWebMethod::WaitWebFrame()
{
my_WebIsBusy = true;
long st = 0;
MSG msg;
DWORD pr=timeGetTime();//进度条进度
ZeroMemory( &msg, sizeof(msg) );
while(my_WebIsBusy)
{
pr = timeGetTime();
if(PeekMessage(&msg,0,0U,0U,PM_REMOVE))
{
TranslateMessage (&msg);
DispatchMessage (&msg);//传递消息
}
if(m_ProgressCTL)
{
m_ProgressCTL.SetPos(pr%1000);
}
Sleep(9);
}
if(m_StateEditCtr)
{
ShowText(L"::网页框架读取完成::", m_StateEditCtr);
}
return S_OK;
}[/code]m_ProgressCTL ShowText m_StateEditCtr在你的项目中并不会出现 这只是方便查看进度,状态提示文本的方式,以及一个CEdit 对象
值得一提的是,虽然你封装的类里面声明了你对浏览器或Web控件的事件感兴趣,但如果你在控件的所属对话框内响应事件也是可以得到回调的.
并且 如果你对WebBrowser控件添加了类 把事件添加到生成的这个类内也是无效的,只有通过上述给出的方式才可以激活在你的这个控件产生的类内使用.
我的项目只是在对话框上的web控件暂时做做文章,后续会用到外部IE程序的跟踪,以达到外置挂载程序的实现.
如果你也需要做到这一步 你还需要告诉程序 你对 pWeb的事件不感兴趣了. 具体如何操作 请参阅网络资料.
然而这些资料并不好找,以上代码也是我结合网络文献得来的思路. 你也可关注本博客 随时等候更新.