⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wtl for mfc programmers, part vi.mht

📁 大家知道wtl是window UI库
💻 MHT
📖 第 1 页 / 共 5 页
字号:
</B>};</PRE>
      <P><CODE>CComObjectRootEx</CODE> and <CODE>CComCoClass</CODE> =
together=20
      make <CODE>CMainDlg</CODE> a COM object. The template parameters =
to=20
      <CODE>IDispEventSimpleImpl</CODE> are a sink ID, the name of our =
class,=20
      and the IID of the connection point interface. The ID can be any =
positive=20
      <CODE><SPAN class=3Dcpp-keyword>unsigned</SPAN> <SPAN=20
      class=3Dcpp-keyword>int</SPAN></CODE>. The connection point =
interface is=20
      <CODE>DIID_DWebBrowserEvents2</CODE>, which you can find in the =
docs on=20
      the browser control, or by looking in exdisp.h.</P>
      <H3><A name=3Dwritesinkmap></A>Writing the sink map</H3>
      <P>The next step is to add an event sink map to =
<CODE>CMainDlg</CODE>.=20
      This map lists which events we are interested in, and the names of =
our=20
      event handlers. The first event we'll look at is=20
      <CODE>DownloadBegin</CODE>. This event is fired when the browser =
begins=20
      downloading a page, and in response we can show a "please wait" =
message so=20
      the user knows that the browser is busy. Looking up=20
      <CODE>DWebBrowserEvents2::DownloadBegin</CODE> in MSDN, we find =
that the=20
      prototype for the event is</P><PRE>  <SPAN =
class=3Dcpp-keyword>void</SPAN> DownloadBegin();</PRE>
      <P>meaning the event sends no parameters and expects no return =
value. To=20
      convey this prototype to the sink map, we write an=20
      <CODE>_ATL_FUNC_INFO</CODE> struct that contains the return value, =
the=20
      number of parameters, and their types. Since events are based on=20
      <CODE>IDispatch</CODE>, all of the types involved are ones that =
can be=20
      contained in a <CODE>VARIANT</CODE>. The list of types is rather =
long, but=20
      here are the most common ones:</P>
      <BLOCKQUOTE>
        <P><CODE>VT_EMPTY</CODE>: <CODE><SPAN=20
        class=3Dcpp-keyword>void</SPAN></CODE><BR><CODE>VT_BSTR</CODE>: =
String in=20
        <CODE>BSTR</CODE> format.<BR><CODE>VT_I4</CODE>: 4-byte signed =
integer,=20
        used for a <CODE><SPAN class=3Dcpp-keyword>long</SPAN></CODE>=20
        parameter<BR><CODE>VT_DISPATCH</CODE>:=20
        <CODE>IDispatch*</CODE><BR><CODE>VT_VARIANT</CODE>:=20
        <CODE>VARIANT</CODE><BR><CODE>VT_BOOL</CODE>: =
<CODE>VARIANT_BOOL</CODE>=20
        (possible values are <CODE>VARIANT_TRUE</CODE> and=20
        <CODE>VARIANT_FALSE</CODE>)</P></BLOCKQUOTE>
      <P>In addition, the flag <CODE>VT_BYREF</CODE> can be added to a =
type to=20
      turn it into a pointer. For example, =
<CODE>VT_VARIANT|VT_BYREF</CODE>=20
      corresponds to a type of <CODE>VARIANT*</CODE>. Here is the =
definition of=20
      <CODE>_ATL_FUNC_INFO</CODE>:</P><PRE><SPAN =
class=3Dcpp-preprocessor>#define _ATL_MAX_VARTYPES 8</SPAN>
=20
<SPAN class=3Dcpp-keyword>struct</SPAN> _ATL_FUNC_INFO
{
  &nbsp; CALLCONV cc;
    VARTYPE  vtReturn;
    SHORT &nbsp;  nParams;
    VARTYPE  pVarTypes[_ATL_MAX_VARTYPES];
};</PRE>
      <P>The members are:</P>
      <DL>
        <DT><CODE>cc</CODE>=20
        <DD>The calling convention of our handler. This must always be=20
        <CODE>CC_STDCALL</CODE>, indicating the <CODE>__stdcall</CODE>=20
        convention.=20
        <DT><CODE>vtReturn</CODE>=20
        <DD>The type of the handler's return value=20
        <DT><CODE>nParams</CODE>=20
        <DD>The number of parameters that the handler takes=20
        <DT><CODE>pVarTypes</CODE>=20
        <DD>The types of the parameters, in left-to-right order. =
</DD></DL>
      <P>Knowing all this, we can write an <CODE>_ATL_FUNC_INFO</CODE> =
struct=20
      for our <CODE>DownloadBegin</CODE> handler:</P><PRE>_ATL_FUNC_INFO =
DownloadInfo =3D { CC_STDCALL, VT_EMPTY, <SPAN =
class=3Dcpp-literal>0</SPAN> };</PRE>
      <P>Now, on to the sink map. We put one =
<CODE>SINK_ENTRY_INFO</CODE> macro=20
      in the map for each event we want to handle. Here is the macro for =
our=20
      <CODE>DownloadBegin</CODE> handler:</P><PRE><SPAN =
class=3Dcpp-keyword>class</SPAN> CMainDlg : <SPAN =
class=3Dcpp-keyword>public</SPAN> ...
{
...
<B>  BEGIN_SINK_MAP(CMainDlg)
    SINK_ENTRY_INFO(<SPAN class=3Dcpp-literal>37</SPAN>, =
DIID_DWebBrowserEvents2, DISPID_DOWNLOADBEGIN,
                    OnDownloadBegin, &amp;DownloadInfo)
  END_SINK_MAP()
</B>};</PRE>
      <P>The macro parameters are the sink ID (37, the same number that =
we used=20
      when adding <CODE>IDispEventSimpleImpl</CODE> to the inheritance =
list),=20
      IID of the event interface, dispatch ID of the event (you can find =
these=20
      in MSDN or the exdispid.h header file), the name of our handler, =
and a=20
      pointer to the <CODE>_ATL_FUNC_INFO</CODE> struct that describes =
the=20
      handler.</P>
      <H3><A name=3Dwritehandler></A>Writing event handlers</H3>
      <P>And now, at long last (whew!), we can write our event =
handler:</P><PRE><SPAN class=3Dcpp-keyword>void</SPAN> __stdcall =
CMainDlg::OnDownloadBegin()
{
  <SPAN class=3Dcpp-comment>// show "Please wait" here...</SPAN>
}</PRE>
      <P>Now let's look at a more complex event, like=20
      <CODE>BeforeNavigate2</CODE>. The prototype for this event =
is:</P><PRE><SPAN class=3Dcpp-keyword>void</SPAN> BeforeNavigate2 (=20
    IDispatch* pDisp, VARIANT* URL, VARIANT* Flags,
    VARIANT* TargetFrameName, VARIANT* PostData,
    VARIANT* Headers, VARIANT_BOOL* Cancel );</PRE>
      <P>This method has 7 parameters, and for the =
<CODE>VARIANT</CODE>s, you=20
      should consult MSDN to see what type of data is actually being =
passed. The=20
      one we're interested in is <CODE>URL</CODE>, which is a string in=20
      <CODE>BSTR</CODE> format.</P>
      <P>The <CODE>_ATL_FUNC_INFO</CODE> description of our=20
      <CODE>BeforeNavigate2</CODE> handler is:</P><PRE>_ATL_FUNC_INFO =
BeforeNavigate2Info =3D
    { CC_STDCALL, VT_EMPTY, <SPAN class=3Dcpp-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 }
};</PRE>
      <P>As before, the return type is <CODE>VT_EMPTY</CODE> which means =
no=20
      value is returned. The <CODE>nParams</CODE> member is 7, meaning =
there are=20
      7 parameters. Following that is an array of parameter types. These =
types=20
      were covered earlier, for example <CODE>VT_DISPATCH</CODE> =
corresponds to=20
      <CODE>IDispatch*</CODE>.</P>
      <P>The sink map entry is pretty similar to the previous =
one:</P><PRE>  BEGIN_SINK_MAP(CMainDlg)
    SINK_ENTRY_INFO(<SPAN class=3Dcpp-literal>37</SPAN>, =
DIID_DWebBrowserEvents2, DISPID_DOWNLOADBEGIN,
                    OnDownloadBegin, &amp;DownloadInfo)
    <B>SINK_ENTRY_INFO(<SPAN class=3Dcpp-literal>37</SPAN>, =
DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2,
                    OnBeforeNavigate2, &amp;BeforeNavigate2Info)</B>
  END_SINK_MAP()</PRE>
      <P>The handler looks like:</P><PRE><SPAN =
class=3Dcpp-keyword>void</SPAN> __stdcall CMainDlg::OnBeforeNavigate2 (
    IDispatch* pDisp, VARIANT* URL, VARIANT* Flags,=20
    VARIANT* TargetFrameName, VARIANT* PostData,=20
    VARIANT* Headers, VARIANT_BOOL* Cancel )
{
CString sURL =3D URL-&gt;bstrVal;
=20
  <SPAN class=3Dcpp-comment>// ... log the URL, or whatever you'd like =
...</SPAN>
}</PRE>
      <P>I bet you appreciate ClassWizard more now! ClassWizard does all =
this=20
      work for you automatically when you insert an ActiveX control into =
an MFC=20
      dialog.</P>
      <P>A couple of notes on the topic of making <CODE>CMainDlg</CODE> =
a COM=20
      object. First, the global <CODE>Run()</CODE> function must be =
changed. Now=20
      that <CODE>CMainDlg</CODE> is a COM object, we have to use=20
      <CODE>CComObject</CODE> to create a =
<CODE>CMainDlg</CODE>:</P><PRE><SPAN class=3Dcpp-keyword>int</SPAN> =
Run(LPTSTR <SPAN class=3Dcpp-comment>/*lpstrCmdLine*/</SPAN> =3D NULL, =
<SPAN class=3Dcpp-keyword>int</SPAN> nCmdShow =3D SW_SHOWDEFAULT)
{
    CMessageLoop theLoop;
    _Module.AddMessageLoop(&amp;theLoop);
=20
<B>CComObject&lt;CMainDlg&gt; dlgMain;
=20
    dlgMain.AddRef();</B>
=20
    <SPAN class=3Dcpp-keyword>if</SPAN> ( dlgMain.Create(NULL) =3D=3D =
NULL )
        {
        ATLTRACE(_T(<SPAN class=3Dcpp-string>"Main dialog creation =
failed!\n"</SPAN>));
        <SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>0</SPAN>;
        }
=20
    dlgMain.ShowWindow(nCmdShow);
=20
    <SPAN class=3Dcpp-keyword>int</SPAN> nRet =3D theLoop.Run();
=20
    _Module.RemoveMessageLoop();
    <SPAN class=3Dcpp-keyword>return</SPAN> nRet;
}</PRE>
      <P>An alternative would be to use the <CODE>CComObjectStack</CODE> =
class,=20
      instead of <CODE>CComObject</CODE>, and remove the=20
      <CODE>dlgMain.AddRef()</CODE> line. <CODE>CComObjectStack</CODE> =
has=20
      trivial implementations of the three <CODE>IUnknown</CODE> methods =
(they=20
      just return immediately) because they are unnecessary&nbsp;-- such =
a COM=20
      object will remain present regardless of its reference count, just =
by the=20
      nature of being created on a stack.</P>
      <P>However, that isn't a perfect solution. =
<CODE>CComObjectStack</CODE> is=20
      meant for short-lived temporary objects, and unfortunately it =
asserts when=20
      any of the <CODE>IUnknown</CODE> methods are called. Since the=20
      <CODE>CMainDlg</CODE> object will be <CODE>AddRef</CODE>'ed when =
it starts=20
      listening for events, <CODE>CComObjectStack</CODE> is unusable in =
this=20
      situation.</P>
      <P>The solution would be to either stick with =
<CODE>CComObject</CODE>, or=20
      write a <CODE>CComObjectStack2</CODE> class that inherits from=20
      <CODE>CComObjectStack</CODE> and allows <CODE>IUnknown</CODE> =
calls. The=20
      unnecessary reference counting that happens with =
<CODE>CComObject</CODE>=20
      is miniscule&nbsp;-- nothing that a person would ever =
notice&nbsp;-- but=20
      if you just <I>have</I> to save those CPU cycles, you can use the=20
      <CODE>CComObjectStack2</CODE> class that's included in the sample=20
      project.</P>
      <H2><A name=3Dsampleoverview></A>Overview of the Sample =
Project</H2>
      <P>Now that we've seen how event sinking works, let's check out =
the=20
      complete IEHoster project. It hosts the web browser control, as =
we've been=20
      discussing, and handles six events. It also shows a list of the =
events, so=20
      you can get a feel for how custom browsers can use them to provide =

      progress UI. The app handles these events:</P>
      <UL>
        <LI><CODE>BeforeNavigate2</CODE> and =
<CODE>NavigateComplete2</CODE>:=20
        These events let the app watch navigation to URLs. You can =
cancel=20
        navigation if you want in response to =
<CODE>BeforeNavigate2</CODE>.=20
        <LI><CODE>DownloadBegin</CODE> and =
<CODE>DownloadComplete</CODE>: The=20
        app uses these events to control the "wait" message, which =
indicates=20
        that the browser is working. A more polished app could use an =
animation,=20
        similar to what IE itself uses.=20
        <LI><CODE>CommandStateChange</CODE>: This event tells the app =
when the=20
        Back and Forward navigation commands are available. The app =
enables or=20
        disables the back and forward buttons accordingly.=20
        <LI><CODE>StatusTextChange</CODE>: This event is fired in =
several cases,=20
        for example when the cursor moves over a hyperlink. This event =
sends a=20
        string, and the app responds by showing that string in a static =
control=20
        under the browser window. </LI></UL>
      <P>The app also has four buttons for controlling the browser: =
back,=20
      forward, stop, and reload. These call the corresponding=20
      <CODE>IWebBrowser2</CODE> methods.</P>
      <P>The events and the data accompanying the events are all logged =
to a=20
      list control, so you can see the events as they are fired. You can =
also=20
      turn off logging of any events so you can watch just one or two. =
To=20
      demonstrate some non-trivial event handling, the=20
      <CODE>BeforeNavigate2</CODE> handler checks the URL, and if it =
contains=20
      "doubleclick.net", the navigation is cancelled. Ad and popup =
blockers that=20
      run as IE plugins, instead of HTTP proxies, use this method. Here =
is the=20
      code that does this check.</P><PRE><SPAN =
class=3Dcpp-keyword>void</SPAN> __stdcall CMainDlg::OnBeforeNavigate2 (
    IDispatch* pDisp, VARIANT* URL, VARIANT* Flags,=20
    VARIANT* TargetFrameName, VARIANT* PostData,=20
    VARIANT* Headers, VARIANT_BOOL* Cancel )
{
USES_CONVERSION;
CString sURL;
=20
    sURL =3D URL-&gt;bstrVal;
=20
    <SPAN class=3Dcpp-comment>// You can set *Cancel to VARIANT_TRUE to =
stop the </SPAN>
    <SPAN class=3Dcpp-comment>// navigation from happening. For example, =

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -