📄 wtl for mfc programmers, part vi.mht
字号:
src=3D"http://www.codeproject.com/wtl/WTL4MFC6/appwiz.png" =
width=3D477=20
align=3Dbottom border=3D0></P>
<P>Checking that box makes our main dialog derive from=20
<CODE>CAxDialogImpl</CODE> so it can host ActiveX controls. There =
is=20
another checkbox on page 2 labeled <I>Host ActiveX Controls</I>, =
however=20
checking that seems to have no effect on the resulting code, so =
you can=20
click Finish from page 1.</P>
<H3><A name=3Dappwizcode></A>The generated code</H3>
<P>In this section, I'll cover some new pieces of code that we =
haven't=20
seen before from the AppWizard. In the next section, I'll cover =
the=20
ActiveX hosting classes in detail.</P>
<P>The first file to check out is stdafx.h, which has these =
includes:</P><PRE><SPAN class=3Dcpp-preprocessor>#include =
<atlbase.h></SPAN>
<SPAN class=3Dcpp-preprocessor>#include <atlapp.h></SPAN>
=20
<SPAN class=3Dcpp-keyword>extern</SPAN> CAppModule _Module;
=20
<SPAN class=3Dcpp-preprocessor>#include <atlcom.h></SPAN>
<SPAN class=3Dcpp-preprocessor>#include <atlhost.h></SPAN>
<SPAN class=3Dcpp-preprocessor>#include <atlwin.h></SPAN>
<SPAN class=3Dcpp-preprocessor>#include <atlctl.h></SPAN>
<SPAN class=3Dcpp-comment>// .. other WTL headers ...</SPAN></PRE>
<P>atlcom.h and atlhost.h are the important ones. They contain the =
definitions of some COM-related classes (like the smart pointer=20
<CODE>CComPtr</CODE>), and the window class used to actually host=20
controls.</P>
<P>Next, look at the declaration of <CODE>CMainDlg</CODE> in=20
maindlg.h:</P><PRE><SPAN class=3Dcpp-keyword>class</SPAN> CMainDlg : =
<SPAN class=3Dcpp-keyword>public</SPAN> CAxDialogImpl<CMainDlg>,
<SPAN class=3Dcpp-keyword>public</SPAN> =
CUpdateUI<CMainDlg>,
<SPAN class=3Dcpp-keyword>public</SPAN> CMessageFilter, =
<SPAN class=3Dcpp-keyword>public</SPAN> CIdleHandler</PRE>
<P><CODE>CMainDlg</CODE> is now derived from =
<CODE>CAxDialogImpl</CODE>,=20
which is the first step in enabling the dialog to host ActiveX=20
controls.</P>
<P>Finally, there's one new line in =
<CODE>WinMain()</CODE>:</P><PRE><SPAN class=3Dcpp-keyword>int</SPAN> =
WINAPI _tWinMain(...)
{
<SPAN class=3Dcpp-comment>//...</SPAN>
_Module.Init(NULL, hInstance);
=20
<B>AtlAxWinInit();</B>
=20
<SPAN class=3Dcpp-keyword>int</SPAN> nRet =3D Run(lpstrCmdLine, =
nCmdShow);
=20
_Module.Term();
<SPAN class=3Dcpp-keyword>return</SPAN> nRet;
}</PRE>
<P><CODE>AtlAxWinInit()</CODE> registers a window class called=20
<CODE>AtlAxWin</CODE>. This is used by ATL when it creates the =
host window=20
for an ActiveX control.</P>
<H2><A name=3Daddreseditor></A>Adding Controls with the Resource =
Editor</H2>
<P>ATL lets you add ActiveX controls to a dialog using the =
resource=20
editor, just as you can in an MFC app. First, right-click in the =
dialog=20
editor and select <I>Insert ActiveX control</I>:</P>
<P><IMG height=3D345 alt=3D" [Insert menu - 8K] "=20
src=3D"http://www.codeproject.com/wtl/WTL4MFC6/insertmenu.png" =
width=3D355=20
align=3Dbottom border=3D0></P>
<P>VC will show a list of the controls installed on your system. =
Scroll=20
down to <I>Microsoft Web Browser</I> and click <I>OK</I> to insert =
the=20
control into the dialog. View the properties of the new control =
and set=20
its ID to IDC_IE. The dialog should now look like this, with the =
control=20
visible in the editor:</P>
<P><IMG height=3D244 alt=3D" [ IE control in editor - 6K] "=20
src=3D"http://www.codeproject.com/wtl/WTL4MFC6/iectrl.png" =
width=3D350=20
align=3Dbottom border=3D0></P>
<P>If you compile and run the app now, you'll see the web browser =
control=20
in the dialog. It will show a blank page however, since we haven't =
told it=20
to navigate anywhere.</P>
<P>In the next section, I'll cover the ATL classes involved in =
creating=20
and hosting ActiveX controls, and then we'll see how to use those =
classes=20
to communicate with the browser.</P>
<H2><A name=3Datlhostclasses></A>ATL Classes for Control =
Hosting</H2>
<P>When hosting ActiveX controls in a dialog, there are two =
classes that=20
work together: <CODE>CAxDialogImpl</CODE> and =
<CODE>CAxWindow</CODE>. They=20
handle all the interfaces that control containers have to =
implement, and=20
provide some utility functions for common actions such as querying =
the=20
control for a particular COM interface.</P>
<H3><A name=3DCAxDialogImpl></A>CAxDialogImpl</H3>
<P>The first class is <CODE>CAxDialogImpl</CODE>. When you write =
your=20
dialog class, you derive from <CODE>CAxDialogImpl</CODE>, instead =
of=20
<CODE>CDialogImpl</CODE>, to enable control hosting.=20
<CODE>CAxDialogImpl</CODE> has overrides for <CODE>Create()</CODE> =
and=20
<CODE>DoModal()</CODE>, which call through to the global functions =
<CODE>AtlAxCreateDialog()</CODE> and <CODE>AtlAxDialogBox()</CODE> =
respectively. Since the IEHoster dialog is created with=20
<CODE>Create()</CODE>, we'll take a closer look at=20
<CODE>AtlAxCreateDialog()</CODE>.</P>
<P><CODE>AtlAxCreateDialog()</CODE> loads the dialog resource and =
uses the=20
helper class <CODE>_DialogSplitHelper</CODE> to iterate through =
the=20
controls and look for special entries created by the resource =
editor that=20
indicate an ActiveX control needs to be created. For example, here =
is the=20
entry in the IEHoster.rc file for the web browser =
control:</P><PRE>CONTROL <SPAN class=3Dcpp-string>""</SPAN>,IDC_IE,<SPAN =
class=3Dcpp-string>"{8856F961-340A-11D0-A96B-00C04FD705A2}"</SPAN>,
WS_TABSTOP,<SPAN class=3Dcpp-literal>7</SPAN>,<SPAN =
class=3Dcpp-literal>7</SPAN>,<SPAN class=3Dcpp-literal>116</SPAN>,<SPAN =
class=3Dcpp-literal>85</SPAN></PRE>
<P>The first parameter is the window text (an empty string), the =
second is=20
the control ID, and the third is the window class name.=20
<CODE>_DialogSplitHelper::SplitDialogTemplate()</CODE> sees that =
the=20
window class begins with <CODE>'{'</CODE> and knows it's an =
ActiveX=20
control entry. It creates a new dialog template in memory that has =
those=20
special <CODE>CONTROL</CODE> entries replaced by ones that create=20
<CODE>AtlAxWin</CODE> windows instead. The new entry is the =
in-memory=20
equivalent of:</P><PRE>CONTROL <SPAN =
class=3Dcpp-string>"{8856F961-340A-11D0-A96B-00C04FD705A2}"</SPAN>,IDC_IE=
,<SPAN class=3Dcpp-string>"AtlAxWin"</SPAN>,
WS_TABSTOP,<SPAN class=3Dcpp-literal>7</SPAN>,<SPAN =
class=3Dcpp-literal>7</SPAN>,<SPAN class=3Dcpp-literal>116</SPAN>,<SPAN =
class=3Dcpp-literal>85</SPAN></PRE>
<P>The result is that an <CODE>AtlAxWin</CODE> window will be =
created with=20
the same ID, and its window text will be the GUID of the ActiveX =
control.=20
So if you call <CODE>GetDlgItem(IDC_IE)</CODE>, the return value =
is the=20
<CODE>HWND</CODE> of the <CODE>AtlAxWin</CODE> window, not the =
ActiveX=20
control itself.</P>
<P>Once <CODE>SplitDialogTemplate()</CODE> returns,=20
<CODE>AtlAxCreateDialog()</CODE> calls=20
<CODE>CreateDialogIndirectParam()</CODE> to create the dialog =
using the=20
modified template.</P>
<H3><A name=3DAtlAxWin></A>AtlAxWin and CAxWindow</H3>
<P>As mentioned above, an <CODE>AtlAxWin</CODE> is used as the =
host window=20
for an ActiveX control. There is a special window interface class =
that you=20
use with an <CODE>AtlAxWin</CODE>: <CODE>CAxWindow</CODE>. When an =
<CODE>AtlAxWin</CODE> is created from a dialog template, the=20
<CODE>AtlAxWin</CODE> window procedure, =
<CODE>AtlAxWindowProc()</CODE>,=20
handles <CODE>WM_CREATE</CODE> and creates the ActiveX control in =
response=20
to that message. An ActiveX control can also be created at =
runtime,=20
without being in the dialog template, but we'll cover that case =
later.</P>
<P>The <CODE>WM_CREATE</CODE> handler calls the global=20
<CODE>AtlAxCreateControl()</CODE>, passing it the =
<CODE>AtlAxWin</CODE>'s=20
window text. Recall that this was set to the GUID of the web =
browser=20
control. <CODE>AtlAxCreateControl()</CODE> calls a couple more =
functions,=20
but eventually the code reaches =
<CODE>CreateNormalizedObject()</CODE>,=20
which converts the window text to a GUID, and finally calls=20
<CODE>CoCreateInstance()</CODE> to create the ActiveX control.</P>
<P>Since the ActiveX control is a child of the =
<CODE>AtlAxWin</CODE>, the=20
dialog can't directly access the control. However, =
<CODE>CAxWindow</CODE>=20
has methods for communicating with the control. The one you'll use =
most=20
often is <CODE>QueryControl()</CODE>, which calls=20
<CODE>QueryInterface()</CODE> on the control. For example, you can =
use=20
<CODE>QueryControl()</CODE> to get an <CODE>IWebBrowser2</CODE> =
interface=20
from the web browser control, and use that interface to navigate =
the=20
browser to a URL.</P>
<H2><A name=3Dcallmethods></A>Calling the Methods of a =
Control</H2>
<P>Now that our dialog has a web browser in it, we can use its COM =
interfaces to interact with it. The first thing we'll do is make =
it=20
navigate to a new URL using its <CODE>IWebBrowser2</CODE> =
interface. In=20
the <CODE>OnInitDialog()</CODE> handler, we can attach a=20
<CODE>CAxWindow</CODE> variable to the <CODE>AtlAxWin</CODE> that =
is=20
hosting the browser.</P><PRE>CAxWindow wndIE =3D =
GetDlgItem(IDC_IE);</PRE>
<P>Next, we declare an <CODE>IWebBrowser2</CODE> interface pointer =
and=20
query the browser control for that interface, using=20
=
<CODE>CAxWindow::QueryControl()</CODE>:</P><PRE>CComPtr<IWebBrowser2&g=
t; pWB2;
HRESULT hr;
=20
hr =3D wndIE.QueryControl ( &pWB2 );</PRE>
<P><CODE>QueryControl()</CODE> calls <CODE>QueryInterface()</CODE> =
on the=20
web browser, and if that succeeds, the <CODE>IWebBrowser2</CODE> =
interface=20
is returned to us. We can then call =
<CODE>Navigate()</CODE>:</P><PRE> <SPAN class=3Dcpp-keyword>if</SPAN> =
( pWB2 )
{
CComVariant v; <SPAN class=3Dcpp-comment>// empty =
variant</SPAN>
=20
pWB2->Navigate ( CComBSTR(<SPAN =
class=3Dcpp-string>"http://www.codeproject.com/"</SPAN>),=20
&v, &v, &v, &v );
}</PRE>
<H2><A name=3Dsinkevents></A>Sinking Events Fired by a =
Control</H2>
<P>Getting an interface from the web browser is pretty simple, and =
it lets=20
us communicate in one direction - <I>to</I> the control. =
There is=20
also a lot of communication <I>from</I> the control, in the =
form of=20
events. ATL has classes that encapsulate connection points and =
event=20
sinking, so that we can receive the events fired by the browser. =
To use=20
this support, we do four things:</P>
<OL>
<LI>Make <CODE>CMainDlg</CODE> into a COM object=20
<LI>Add <CODE>IDispEventSimpleImpl</CODE> to =
<CODE>CMainDlg</CODE>'s=20
inheritance list=20
<LI>Write an <I>event sink map</I> that indicates which events =
we want=20
to handle=20
<LI>Write handlers for those events </LI></OL>
<H3><A name=3Dchangemaindlg></A>Changes to CMainDlg</H3>
<P>The reason why we make <CODE>CMainDlg</CODE> a COM object is =
that an=20
event sink is based on <CODE>IDispatch</CODE>. In order for=20
<CODE>CMainDlg</CODE> to expose COM interfaces, it has to be a COM =
object.=20
<CODE>IDispEventSimpleImpl</CODE> provides an implementation of=20
<CODE>IDispatch</CODE> and handles the calls needed to set up a =
connection=20
point. <CODE>IDispEventSimpleImpl</CODE> also calls our event =
handlers=20
when events that we want to handle are received.</P>
<P>Here are the classes to add to the <CODE>CMainDlg</CODE> =
inheritance=20
list, along with the <CODE>COM_MAP</CODE> that lists the =
interfaces that=20
<CODE>CMainDlg</CODE> exposes:</P><PRE><B>#include =
<exdisp.h> <SPAN class=3Dcpp-comment>// browser control =
definitions</SPAN>
<SPAN class=3Dcpp-preprocessor>#include <exdispid.h> // browser =
event dispatch IDs</SPAN>
</B>=20
<SPAN class=3Dcpp-keyword>class</SPAN> CMainDlg : <SPAN =
class=3Dcpp-keyword>public</SPAN> CAxDialogImpl<CMainDlg>,
<SPAN class=3Dcpp-keyword>public</SPAN> =
CUpdateUI<CMainDlg>,
<SPAN class=3Dcpp-keyword>public</SPAN> CMessageFilter, =
<SPAN class=3Dcpp-keyword>public</SPAN> CIdleHandler,
<B><SPAN class=3Dcpp-keyword>public</SPAN> =
CComObjectRootEx<CComSingleThreadModel>,
<SPAN class=3Dcpp-keyword>public</SPAN> =
CComCoClass<CMainDlg>,
<SPAN class=3Dcpp-keyword>public</SPAN> =
IDispEventSimpleImpl<<SPAN class=3Dcpp-literal>37</SPAN>, CMainDlg, =
&DIID_DWebBrowserEvents2>
</B>{
...<B>
BEGIN_COM_MAP(CMainDlg)
COM_INTERFACE_ENTRY2(IDispatch, IDispEventSimpleImpl)
END_COM_MAP()
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -