📄 wtl for mfc programmers, part vi - hosting activex controls - wtl.htm
字号:
<PRE><font color="#0033FF">CAxWindow wndIE = GetDlgItem(IDC_IE);</font></PRE>
<P>然后声明一个IWebBrowser2的接口指针并查询浏览器控件的这个接口,使用CAxWindow::QueryControl():</P>
<PRE><font color="#0033FF">CComPtr<IWebBrowser2> pWB2;
HRESULT hr;
hr = wndIE.QueryControl ( &pWB2 );</font></PRE>
<P>QueryControl()调用浏览器控件的QueryInterface()方法,如果成功就会返回IWebBrowser2接口,我们可以调用Navigate():</P>
<PRE> <font color="#0033FF"><SPAN class=cpp-keyword>if</SPAN> ( pWB2 )
{
CComVariant v; <SPAN class=cpp-comment>// empty variant</SPAN>
pWB2->Navigate ( CComBSTR(<SPAN class=cpp-string>"http://www.codeproject.com/"</SPAN>),
&v, &v, &v, &v );
}</font></PRE>
<H2><A name=sinkevents></A><font color="#FFFF66">响应控件触发的事件</font></H2>
<P>从浏览器控件得到接口非常简单,通过它可以单向的与控件通信。通常控件也会以事件的形式与外界通信,ATL有专用的类包装连接点和事件相应,所以我们可以从控件接收到这些事件。为使用对事件的支持需要做四件事:</P>
<OL>
<LI>将CMainDlg变成COM对象
<LI>添加IDispEventSimpleImpl到CMainDlg的继承列表
<LI>填写事件映射链,它指示哪些事件需要处理
<LI>编写事件响应函数</LI>
</OL>
<H3><A name=changemaindlg></A><font color="#FFFF66">CMainDlg的修改</font> </H3>
<P>将CMainDlg转变成COM对象的原因是事件相应是基于IDispatch的,为了让CMainDlg暴露这个接口,它必须是个COM对象。IDispEventSimpleImpl提供了IDispatch接口的实现和建立连接点所需的处理函数,当事件发生时IDispEventSimpleImpl还调用我们想要接收的事件的处理函数。</P>
<P>以下的类需要添加到CMainDlg的集成列表中,同时COM_MAP列出了CMainDlg暴露的接口:</P>
<PRE><B><font color="#0033FF">#include <exdisp.h> <SPAN class=cpp-comment>// browser control definitions</SPAN>
<SPAN class=cpp-preprocessor>#include <exdispid.h> // browser event dispatch IDs</SPAN>
</font></B>
<font color="#0033FF"><SPAN class=cpp-keyword>class</SPAN> CMainDlg : <SPAN class=cpp-keyword>public</SPAN> CAxDialogImpl<CMainDlg>,
<SPAN class=cpp-keyword>public</SPAN> CUpdateUI<CMainDlg>,
<SPAN class=cpp-keyword>public</SPAN> CMessageFilter, <SPAN class=cpp-keyword>public</SPAN> CIdleHandler,
<B><SPAN class=cpp-keyword>public</SPAN> CComObjectRootEx<CComSingleThreadModel>,
<SPAN class=cpp-keyword>public</SPAN> CComCoClass<CMainDlg>,
<SPAN class=cpp-keyword>public</SPAN> IDispEventSimpleImpl<<SPAN class=cpp-literal>37</SPAN>, CMainDlg, &DIID_DWebBrowserEvents2>
</B>{
...<B>
BEGIN_COM_MAP(CMainDlg)
COM_INTERFACE_ENTRY2(IDispatch, IDispEventSimpleImpl)
END_COM_MAP()
</B>};</font></PRE>
<P>CComObjectRootEx类CComCoClass共同使CMainDlg成为一个COM对象,IDispEventSimpleImpl的模板参数是事件的ID,我们的类名和连接点接口的IID。事件ID可以是任意正数,连接点对象的IID是DIID_DWebBrowserEvents2,可以在浏览器控件的相关文档中找到这些参数,也可以查看exdisp.h。</P>
<H3><A name=writesinkmap></A><font color="#FFFF66">填写事件映射链</font></H3>
<P>下一步是给CMainDlg添加事件映射链,这个映射链将我们感兴趣的事件和我们的处理函数联系起来。我们要看的第一个事件是DownloadBegin,当浏览器开始下载一个页面时就会触发这个事件,我们响应这个事件显示“please
wait”信息给用户,让用户知道浏览器正在忙。在MSDN中可以查到DWebBrowserEvents2::DownloadBegin事件的原型</P>
<PRE> <font color="#0033FF"><SPAN class=cpp-keyword>void</SPAN> DownloadBegin();</font></PRE>
<P>这个事件没有参数,也不需要返回值。为了将这个事件的原型转换成事件响应链,我们需要写一个_ATL_FUNC_INFO结构,它包含返回值,参数的个数和参数类型。由于事件是基于IDispatch的,所以所有的参数都用VARIANT表示,这个数据结构的描述相当长(支持很多个数据类型),以下是常用的几个:</P>
<BLOCKQUOTE>
<P>VT_EMPTY: <SPAN
class=cpp-keyword>void</SPAN><BR>VT_BSTR: BSTR 格式的字符串<BR>
VT_I4: 4字节有符号整数,用于long类型的参数<BR>
VT_DISPATCH:
IDispatch*<BR>VT_VARIANT>:
VARIANT<BR>
VT_BOOL: VARIANT_BOOL (允许的取值是VARIANT_TRUE和VARIANT_FALSE)</P>
</BLOCKQUOTE>
<P>另外,标志VT_BYREF表示将一个参数转换成相应的指针。例如,VT_VARIANT|VT_BYREF表示VARIANT*类型。下面是_ATL_FUNC_INFO的定义:</P>
<PRE><SPAN class=cpp-preprocessor><font color="#0033FF">#define _ATL_MAX_VARTYPES 8</font></SPAN>
<font color="#0033FF"><SPAN class=cpp-keyword>struct</SPAN> _ATL_FUNC_INFO
{
CALLCONV cc;
VARTYPE vtReturn;
SHORT nParams;
VARTYPE pVarTypes[_ATL_MAX_VARTYPES];
};</font></PRE>
<P>参数:</P>
<DL>
<DT>cc
<DD>我们的事件响应函数的调用方式约定,这个参数必须是CC_STDCALL,表示是__stdcall方式
<DT>vtReturn
<DD>事件响应函数的返回值类型
<DT>nParams
<DD>事件带的参数个数
<DT>pVarTypes
<DD>相应的参数类型,按从左到右的顺序</DD>
</DL>
<P>了解这些之后,我们就可以填写DownloadBegin事件处理的_ATL_FUNC_INFO结构:</P>
<PRE><font color="#0033FF">_ATL_FUNC_INFO DownloadInfo = { CC_STDCALL, VT_EMPTY, <SPAN class=cpp-literal>0</SPAN> };</font></PRE>
<P>现在,回到事件响应链,我们为每一个我们想要处理的事件添加一个SINK_ENTRY_INFO宏,下面是处理DownloadBegin事件的宏:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">class</font></SPAN><font color="#0033FF"> CMainDlg : <SPAN class=cpp-keyword>public</SPAN> ...
{
...
<B> BEGIN_SINK_MAP(CMainDlg)
SINK_ENTRY_INFO(<SPAN class=cpp-literal>37</SPAN>, DIID_DWebBrowserEvents2, DISPID_DOWNLOADBEGIN,
OnDownloadBegin, &DownloadInfo)
END_SINK_MAP()
</B>};</font></PRE>
<P>这个宏的参数是事件的ID(37,与我们在IDispEventSimpleImpl的继承列表中使用的ID一样),事件接口的IID,事件的dispatch
ID(可以在MSDN或exdispid.h头文件中查到),事件处理函数的名字和指向描述这个事件处理的_ATL_FUNC_INFO结构的指针。</P>
<H3><A name=writehandler></A><font color="#FFFF66">编写事件处理函数</font></H3>
<P>好了,等了这么长时间(吹个口哨!),我们可以写事件处理函数了:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">void</font></SPAN><font color="#0033FF"> __stdcall CMainDlg::OnDownloadBegin()
{
<SPAN class=cpp-comment>// show "Please wait" here...</SPAN>
}</font></PRE>
<P>现在来看一个复杂一点的事件,比如BeforeNavigate2,这个事件的原型是:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">void</font></SPAN><font color="#0033FF"> BeforeNavigate2 (
IDispatch* pDisp, VARIANT* URL, VARIANT* Flags,
VARIANT* TargetFrameName, VARIANT* PostData,
VARIANT* Headers, VARIANT_BOOL* Cancel );</font></PRE>
<P>此方法有7个参数,对于VARIANT类型参数可以从MSDN查到它到底传递的是什么类型的数据,我们感兴趣的是URL,是一个BSTR类型的字符串。</P>
<P>描述BeforeNavigate2事件的_ATL_FUNC_INFO结构是这样的:</P>
<PRE><font color="#0033FF">_ATL_FUNC_INFO BeforeNavigate2Info =
{ CC_STDCALL, VT_EMPTY, <SPAN class=cpp-literal>7</SPAN>,
{ 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 }
};</font></PRE>
<P>和前面一样,返回值类型是VT_EMPTY表示没有返回值,nParams是7,表示有7个参数。接着是参数类型数组,这些类型前面介绍过了,例如VT_DISPATCH表示IDispatch*。</P>
<p>事件响应链的入口与前面的例子很相似:</p>
<PRE> <font color="#0033FF">BEGIN_SINK_MAP(CMainDlg)
SINK_ENTRY_INFO(<SPAN class=cpp-literal>37</SPAN>, DIID_DWebBrowserEvents2, DISPID_DOWNLOADBEGIN,
OnDownloadBegin, &DownloadInfo)
<B>SINK_ENTRY_INFO(<SPAN class=cpp-literal>37</SPAN>, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2,
OnBeforeNavigate2, &BeforeNavigate2Info)</B>
END_SINK_MAP()</font></PRE>
<P>事件处理函数是这个样子:</P>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">void</font></SPAN><font color="#0033FF"> __stdcall CMainDlg::OnBeforeNavigate2 (
IDispatch* pDisp, VARIANT* URL, VARIANT* Flags,
VARIANT* TargetFrameName, VARIANT* PostData,
VARIANT* Headers, VARIANT_BOOL* Cancel )
{
CString sURL = URL->bstrVal;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -