📄 atl under the hood - part 5.mht
字号:
<SPAN class=3Dcpp-keyword>case</SPAN> WM_CREATE:
pThis->OnCreate(wParam, lParam);
<SPAN class=3Dcpp-keyword>break</SPAN>;
<SPAN class=3Dcpp-keyword>case</SPAN> WM_PAINT:
pThis->OnPaint(wParam, lParam);
<SPAN class=3Dcpp-keyword>break</SPAN>;
<SPAN class=3Dcpp-keyword>case</SPAN> WM_LBUTTONDOWN:
pThis->OnLButtonDown(wParam, lParam);
<SPAN class=3Dcpp-keyword>break</SPAN>;
<SPAN class=3Dcpp-keyword>case</SPAN> WM_KEYDOWN:
pThis->OnKeyDown(wParam, lParam);
<SPAN class=3Dcpp-keyword>break</SPAN>;
<SPAN class=3Dcpp-keyword>case</SPAN> WM_DESTROY:
::PostQuitMessage(<SPAN class=3Dcpp-literal>0</SPAN>);
<SPAN class=3Dcpp-keyword>break</SPAN>;
}
<SPAN class=3Dcpp-keyword>return</SPAN> ::DefWindowProc(hWnd, =
uMsg, wParam, lParam);
}
};
<SPAN class=3Dcpp-keyword>class</SPAN> ZDriveWindow1 : <SPAN =
class=3Dcpp-keyword>public</SPAN> ZWindow
{
<SPAN class=3Dcpp-keyword>public</SPAN>:
LRESULT OnPaint(WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
RECT rect;
hDC =3D BeginPaint(&ps);
GetClientRect(&rect);
::SetBkMode(hDC, TRANSPARENT);
::DrawText(hDC, <SPAN class=3Dcpp-string>"ZDriveWindow1"</SPAN>, =
-<SPAN class=3Dcpp-literal>1</SPAN>, &rect,=20
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPaint(&ps);
<SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>0</SPAN>;
}
LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam)
{
::MessageBox(NULL, <SPAN =
class=3Dcpp-string>"ZDriveWindow1::OnLButtonDown"</SPAN>, <SPAN =
class=3Dcpp-string>"Msg"</SPAN>, MB_OK);
<SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>0</SPAN>;
}
};
<SPAN class=3Dcpp-keyword>class</SPAN> ZDriveWindow2 : <SPAN =
class=3Dcpp-keyword>public</SPAN> ZWindow
{
<SPAN class=3Dcpp-keyword>public</SPAN>:
LRESULT OnPaint(WPARAM wParam, LPARAM lParam)
{
HDC hDC;
PAINTSTRUCT ps;
RECT rect;
hDC =3D BeginPaint(&ps);
GetClientRect(&rect);
::SetBkMode(hDC, TRANSPARENT);
::Rectangle(hDC, rect.left, rect.top, rect.right, rect.bottom);
::DrawText(hDC, <SPAN class=3Dcpp-string>"ZDriveWindow2"</SPAN>, =
-<SPAN class=3Dcpp-literal>1</SPAN>, &rect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPaint(&ps);
<SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>0</SPAN>;
}
LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam)
{
::MessageBox(NULL, <SPAN =
class=3Dcpp-string>"ZDriveWindow2::OnLButtonDown"</SPAN>, <SPAN =
class=3Dcpp-string>"Msg"</SPAN>, MB_OK);
<SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>0</SPAN>;
}
};
<SPAN class=3Dcpp-keyword>int</SPAN> WINAPI WinMain(HINSTANCE hInstance, =
HINSTANCE hPrevInstance,=20
LPSTR lpCmdLine, <SPAN =
class=3Dcpp-keyword>int</SPAN> nCmdShow)
{
<SPAN class=3Dcpp-keyword>char</SPAN> szAppName[] =3D <SPAN =
class=3Dcpp-string>"Hello world"</SPAN>;
MSG msg;
WNDCLASS wnd;
ZDriveWindow1 zwnd1;
ZDriveWindow2 zwnd2;
=20
wnd.cbClsExtra =3D NULL;
wnd.cbWndExtra =3D NULL;
wnd.hbrBackground =3D (HBRUSH)GetStockObject(GRAY_BRUSH);
wnd.hCursor =3D LoadCursor(NULL, IDC_ARROW);
wnd.hIcon =3D LoadIcon(NULL, IDI_APPLICATION);
wnd.hInstance =3D hInstance;
wnd.lpfnWndProc =3D ZWindow::StartWndProc;
wnd.lpszClassName =3D szAppName;
wnd.lpszMenuName =3D NULL;
wnd.style =3D CS_HREDRAW | CS_VREDRAW;
=20
<SPAN class=3Dcpp-keyword>if</SPAN> (!RegisterClass(&wnd))
{
MessageBox(NULL, <SPAN class=3Dcpp-string>"Can not register =
window class"</SPAN>, <SPAN class=3Dcpp-string>"Error"</SPAN>,=20
MB_OK | MB_ICONINFORMATION);
<SPAN class=3Dcpp-keyword>return</SPAN> -<SPAN =
class=3Dcpp-literal>1</SPAN>;
}
g_pWnd =3D &zwnd1;
zwnd1.Create(szAppName, <SPAN class=3Dcpp-string>"Hell =
world"</SPAN>, hInstance);
zwnd1.ShowWindow(nCmdShow);
zwnd1.UpdateWindow();
g_pWnd =3D &zwnd2;
zwnd2.Create(szAppName, <SPAN class=3Dcpp-string>"Hello =
world"</SPAN>, hInstance, zwnd1.m_hWnd,=20
WS_VISIBLE | WS_CHILD | ES_MULTILINE, NULL, NULL, <SPAN =
class=3Dcpp-literal>0</SPAN>, <SPAN class=3Dcpp-literal>0</SPAN>, <SPAN =
class=3Dcpp-literal>150</SPAN>, <SPAN class=3Dcpp-literal>150</SPAN>);
<SPAN class=3Dcpp-keyword>while</SPAN> (GetMessage(&msg, NULL, =
<SPAN class=3Dcpp-literal>0</SPAN>, <SPAN class=3Dcpp-literal>0</SPAN>))
{
DispatchMessage(&msg);
}
=20
<SPAN class=3Dcpp-keyword>return</SPAN> msg.wParam;
}
</PRE>
<P>The output of this program shows the same message box no matter =
on=20
which window you click. </P>
<CENTER><IMG=20
=
src=3D"http://www.codeproject.com/atl/atl_underthehood_5/Thunk1.jpg"></IM=
G></CENTER>
<P>You get the same message box no matter you click on any window. =
This=20
means message is not properly propagate to appropriate window. In =
fact=20
each window has their own window procedure, which handles all the =
messages=20
of that window. But here we use the call back function of second =
drive=20
class with the first window, so we can't executer the message =
handler of=20
first window. </P>
<P>Here our main problem is to associate the callback function of =
window=20
with appropriate window. Means <CODE>HWND</CODE> should be =
associated with=20
appropriate Drive class. So message should be go to right window. =
There=20
can be more than one solution of this problem, let's take a look =
at each=20
solution one by one. </P>
<P>The first obvious solution come in mind, which can be easily=20
implemented is to make a global structure, which stores the=20
<CODE>HWND</CODE> with appropriate Drive class address. But there =
are two=20
main problems with this approach. Fist the structure becomes =
larger and=20
larger when more and more window will be added in the program. And =
second=20
problem is that there is of course searching time involve in that =
global=20
structure and it is time consuming to search it when that =
structure=20
becomes very large. </P>
<P>The main purpose of ATL is to make as small as possible and as =
fast as=20
possible. And this technique fails on both criteria. This method =
is not=20
only slow but also consumes lots of memory when there are lots of =
window=20
involves in the program. </P>
<P>The other possible solution is to use <CODE>cbWndExtra</CODE> =
field of=20
<CODE>WNDCLASS</CODE> or <CODE>WNDCLASSEX</CODE> structure. There =
is still=20
one question why not use <CODE>cbClsExtra</CODE> instead of=20
<CODE>cbWndExtra</CODE>? The answer is simple =
<CODE>cbClsExtra</CODE>=20
store the extra bytes for each class and <CODE>cbWndExtra</CODE> =
store=20
extra byes for each window from the class. And you can create more =
than=20
one window from the same class so if you use =
<CODE>cbClsExtra</CODE> then=20
you can't distinguish the different window call back function from =
<CODE>cbClsExtra</CODE> because it is same of all those window =
which are=20
create by the same class. And store the address of appropriate =
drive class=20
in this field. </P>
<P>It seems good solution at lest seems better then the first one. =
But=20
there are still two problems in this solution. The first one is =
that if=20
user wants to use <CODE>cbWndExtra</CODE> then he/she might =
overwrite the=20
data which is written by using this technique. So client of this =
class has=20
to be careful not to lose that information when using=20
<CODE>cbWndExtra</CODE>. Ok fine, you have decided and documented =
that not=20
use cbWndExtra when using your library, but there is still one =
more=20
problem. This method is not very much fast, again against the rule =
of ATL,=20
that ATL should be as small and as fast as possible. </P>
<P>ATL neither use first method nor second. The method which ATL =
used is=20
called Thunk. Thunk is a small set of code to do some work and =
this term=20
is used in different context. It may be possible that you have =
listen two=20
type of Thunking </P><B>Universal Thunking</B>=20
<P>Universal Thunking enables to call 32-bit function from 16-bit =
code.=20
Available on both Win 9x and Win NT/2000/XP. This is also known as =
Generic=20
Thunking. </P><B>General Thunking</B>=20
<P>Generic Thunking enables to call 16-bit function from 32-bit =
code. It=20
is available only on Win 9x because Win NT/2000/XP are true 32 bit =
operating system so there is no logical reason to call 16 bit =
function=20
from 32 bit code. This is also known as Flat Thunking. </P>
<P>ATL doesn't use any of this, because you are not going to mix =
16-bit=20
and 32-bit code in ATL. In fact ATL insert a small code to call =
the=20
correct Window procedure. </P>
<P>Lets star some basic concepts before the study of thunking of =
ATL. Take=20
a look at the following simple program. </P><B>Program 72</B> =
<PRE><SPAN class=3Dcpp-preprocessor>#include <iostream></SPAN>
using <SPAN class=3Dcpp-keyword>namespace</SPAN> std;
<SPAN class=3Dcpp-keyword>struct</SPAN> S
{
<SPAN class=3Dcpp-keyword>char</SPAN> ch;
<SPAN class=3Dcpp-keyword>int</SPAN> i;
};
<SPAN class=3Dcpp-keyword>int</SPAN> main()
{
cout << <SPAN class=3Dcpp-string>"Size of character =3D =
"</SPAN> << <SPAN class=3Dcpp-keyword>sizeof</SPAN>(<SPAN =
class=3Dcpp-keyword>char</SPAN>) << endl;
cout << <SPAN class=3Dcpp-string>"Size of integer =3D "</SPAN> =
<< <SPAN class=3Dcpp-keyword>sizeof</SPAN>(<SPAN =
class=3Dcpp-keyword>int</SPAN>) << endl;
cout << <SPAN class=3Dcpp-string>"Size of structure =3D =
"</SPAN> << <SPAN class=3Dcpp-keyword>sizeof</SPAN>(S) << =
endl;
<SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>0</SPAN>;
}
</PRE>
<P>The output of this program is </P><PRE>Size of character =3D =
<SPAN class=3Dcpp-literal>1</SPAN>
Size of integer =3D <SPAN class=3Dcpp-literal>4</SPAN>
Size of structure =3D <SPAN class=3Dcpp-literal>8</SPAN>
</PRE>
<P>The sum of the sizes of integer and character should be 5 not =
8. Ok=20
lets change a program little bit and add one more member variable =
into the=20
program to see what's going on. </P><B>Program 73</B> <PRE><SPAN =
class=3Dcpp-preprocessor>#include <iostream></SPAN>
using <SPAN class=3Dcpp-keyword>namespace</SPAN> std;
<SPAN class=3Dcpp-keyword>struct</SPAN> S
{
<SPAN class=3Dcpp-keyword>char</SPAN> ch1;
<SPAN class=3Dcpp-keyword>char</SPAN> ch2;
<SPAN class=3Dcpp-keyword>int</SPAN> i;
};
<SPAN class=3Dcpp-keyword>int</SPAN> main()
{
cout << <SPAN class=3Dcpp-string>"Size of character =3D =
"</SPAN> << <SPAN class=3Dcpp-keyword>sizeof</SPAN>(<SPAN =
class=3Dcpp-keyword>char</SPAN>) << endl;
cout << <SPAN class=3Dcpp-string>"Size of integer =3D "</SPAN> =
<< <SPAN class=3Dcpp-keyword>sizeof</SPAN>(<SPAN =
class=3Dcpp-keyword>int</SPAN>) << endl;
cout << <SPAN class=3Dcpp-string>"Size of structure =3D =
"</SPAN> << <SPAN class=3Dcpp-keyword>sizeof</SPAN>(S) << =
endl;
<SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>0</SPAN>;
}
</PRE>
<P>The output of this program is same as previous one. So what is =
going on=20
here? Change a program little bit more to see what is going on =
Under the=20
Hood. </P><B>Program 74</B> <PRE><SPAN =
class=3Dcpp-preprocessor>#include <iostream></SPAN>
using <SPAN class=3Dcpp-keyword>namespace</SPAN> std;
<SPAN class=3Dcpp-keyword>struct</SPAN> S
{
<SPAN class=3Dcpp-keyword>char</SPAN> ch1;
<SPAN class=3Dcpp-keyword>char</SPAN> ch2;
<SPAN class=3Dcpp-keyword>int</SPAN> i;
}s;
<SPAN class=3Dcpp-keyword>int</SPAN> main()
{
cout << <SPAN class=3Dcpp-string>"Address of ch1 =3D "</SPAN> =
<< (<SPAN class=3Dcpp-keyword>int</SPAN>)&s.ch1 << endl;
cout << <SPAN class=3Dcpp-string>"Address of ch2 =3D "</SPAN> =
<< (<SPAN class=3Dcpp-keyword>int</SPAN>)&s.ch2 << endl;
cout << <SPAN class=3Dcpp-string>"Address of
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -