📄 atl under the hood - part 5.mht
字号:
lParam)
{
<SPAN class=3Dcpp-keyword>switch</SPAN> (uMsg)
{
<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);
}
};
</PRE>
<P>You have to give the address of callback function in one field =
of=20
<CODE>WNDCLASS</CODE> or <CODE>WNDCLASSEX</CODE> structure. And =
you give=20
it something like this after creating the object of =
<CODE>ZWindow</CODE>=20
class. </P><PRE> ZWindow zwnd;
WNDCLASS wnd;
wnd.lpfnWndProc =3D wnd.WndProc;
</PRE>
<P>But when you compile this program this will give error =
something like=20
this. </P><PRE>cannot convert from '<SPAN =
class=3Dcpp-keyword>long</SPAN> (__stdcall ZWindow::*)(<SPAN =
class=3Dcpp-keyword>struct</SPAN> HWND__ *,
<SPAN class=3Dcpp-keyword>unsigned</SPAN> <SPAN =
class=3Dcpp-keyword>int</SPAN>,<SPAN class=3Dcpp-keyword>unsigned</SPAN> =
<SPAN class=3Dcpp-keyword>int</SPAN>,<SPAN =
class=3Dcpp-keyword>long</SPAN>)' to '<SPAN =
class=3Dcpp-keyword>long</SPAN> (__stdcall *)(<SPAN =
class=3Dcpp-keyword>struct</SPAN> HWND__ *,
<SPAN class=3Dcpp-keyword>unsigned</SPAN> <SPAN =
class=3Dcpp-keyword>int</SPAN>, <SPAN =
class=3Dcpp-keyword>unsigned</SPAN> <SPAN =
class=3Dcpp-keyword>int</SPAN>,<SPAN class=3Dcpp-keyword>long</SPAN>)
</PRE>
<P>The reason is you can not pass member function as a call back =
function.=20
Why? Because in case of member function compiler automatically =
passes one=20
parameter to the function and that parameter is the pointer of =
that class=20
or in other words this pointer. So it means when you pass n =
parameters in=20
the member function then compiler will pass n+1 parameters and the =
additional parameter is this pointer. The error message from the =
compiler=20
shows this too that compiler can't convert member function to =
global=20
function. </P>
<P>So what should we do if we want to use member function as a =
call back=20
function? If we tell the compiler to not pass first parameter to =
the=20
function some how then we can use the member function as a call =
back=20
function. In C++ if we declare member function as a static then =
compiler=20
doesn't pass this pointer, this is in fact the difference between =
static=20
and non static member function. </P>
<P>So we made <CODE>WndProc</CODE> static in ZWindow class. This =
technique=20
is also use in case of threading where you want to use member =
function as=20
a thread function then you make static member function as a thread =
function. </P>
<P>Here is update program which use ZWindow class. </P><B>Program =
67</B> <PRE><SPAN class=3Dcpp-preprocessor>#include =
<windows.h></SPAN>
<SPAN class=3Dcpp-keyword>class</SPAN> ZWindow
{
<SPAN class=3Dcpp-keyword>public</SPAN>:
HWND m_hWnd;
ZWindow(HWND hWnd =3D <SPAN class=3Dcpp-literal>0</SPAN>) : =
m_hWnd(hWnd) { }
<SPAN class=3Dcpp-keyword>inline</SPAN> <SPAN =
class=3Dcpp-keyword>void</SPAN> Attach(HWND hWnd)
{ m_hWnd =3D hWnd; }
<SPAN class=3Dcpp-keyword>inline</SPAN> BOOL ShowWindow(<SPAN =
class=3Dcpp-keyword>int</SPAN> nCmdShow)
{ <SPAN class=3Dcpp-keyword>return</SPAN> ::ShowWindow(m_hWnd, =
nCmdShow); }
<SPAN class=3Dcpp-keyword>inline</SPAN> BOOL UpdateWindow()
{ <SPAN class=3Dcpp-keyword>return</SPAN> ::UpdateWindow(m_hWnd); }
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM =
lParam)
{
<SPAN class=3Dcpp-keyword>switch</SPAN> (uMsg)
{
<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>int</SPAN> WINAPI WinMain(HINSTANCE hInstance, =
HINSTANCE hPrevInstance, LPSTR lpCmdLine, =20
<SPAN class=3Dcpp-keyword>int</SPAN> nCmdShow)
{
<SPAN class=3Dcpp-keyword>char</SPAN> szAppName[] =3D <SPAN =
class=3Dcpp-string>"Hello world"</SPAN>;
HWND hWnd;
MSG msg;
WNDCLASS wnd;
ZWindow zwnd;
=20
wnd.cbClsExtra =3D NULL;
wnd.cbWndExtra =3D NULL;
wnd.hbrBackground =3D (HBRUSH)GetStockObject(WHITE_BRUSH);
wnd.hCursor =3D LoadCursor(NULL, IDC_ARROW);
wnd.hIcon =3D LoadIcon(NULL, IDI_APPLICATION);
wnd.hInstance =3D hInstance;
wnd.lpfnWndProc =3D ZWindow::WndProc;
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>;
}
=20
hWnd =3D CreateWindow(szAppName, <SPAN class=3Dcpp-string>"Hello =
world"</SPAN>, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,=20
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, =
hInstance, NULL);
zwnd.Attach(hWnd);
=20
zwnd.ShowWindow(nCmdShow);
zwnd.UpdateWindow();
<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>This program just shows the usage of <CODE>ZWindow</CODE>. And =
to be=20
very honest this class do nothing special. It is just wrapper on =
Window=20
API, the only advantage you get from this is that now you don't =
need to=20
pass <CODE>HWND</CODE> as a parameter, but instead of this now you =
have to=20
type the object name when calling member function. </P>
<P>Like before this you call function like this </P><PRE> =
ShowWindow(hWnd, nCmdShow);
</PRE>
<P>And now you call something like this </P><PRE> =
zwnd.ShowWindow(nCmdShow);
</PRE>
<P>Not a big advantage till now. </P>
<P>Let's see how we can handle window message in the =
<CODE>WndProc</CODE>.=20
In the previous program we handle only one function i.e.=20
<CODE>WM_DESTROY</CODE>. If we want to handle more messages then =
add more=20
case in the switch statement. Let's modify the =
<CODE>WndProc</CODE> to=20
handle <CODE>WM_PAINT</CODE>. It would be something like this. =
</P><PRE><SPAN class=3Dcpp-keyword>switch</SPAN> (uMsg)
{
<SPAN class=3Dcpp-keyword>case</SPAN> WM_PAINT:
hDC =3D ::BeginPaint(hWnd, &ps);
::GetClientRect(hWnd, &rect);
::DrawText(hDC, <SPAN class=3Dcpp-string>"Hello world"</SPAN>, =
-<SPAN class=3Dcpp-literal>1</SPAN>, &rect, DT_CENTER | DT_VCENTER =
DT_SINGLELINE);
::EndPaint(hWnd, &ps);
<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>;
}
</PRE>
<P>This code is perfectly valid and print "Hello World" in center =
of=20
Window. But why use API's <CODE>BeginPaint</CODE>,=20
<CODE>GetClientRect</CODE> and <CODE>EndPaint</CODE> when it all =
should be=20
a member function of ZWindow class according to our criteria, =
because all=20
of these have <CODE>HWND</CODE> as a first parameter. </P>
<P>Because all those function are not static. And you can't call =
non=20
static member function from static member function. Why? Because =
the=20
difference is this pointer, non static member function has this =
pointer=20
and static function doesn't have it. If we somehow pass this =
pointer to=20
static member function then we can call the non static member =
function=20
from static member function. Let's take a look at the following =
program.=20
</P><B>Program 68</B> <PRE><SPAN class=3Dcpp-preprocessor>#include =
<iostream></SPAN>
using <SPAN class=3Dcpp-keyword>namespace</SPAN> std;
<SPAN class=3Dcpp-keyword>class</SPAN> C=20
{
<SPAN class=3Dcpp-keyword>public</SPAN>:
<SPAN class=3Dcpp-keyword>void</SPAN> NonStaticFunc()=20
{ =20
cout << <SPAN class=3Dcpp-string>"NonStaticFun"</SPAN> =
<< endl;
}
<SPAN class=3Dcpp-keyword>static</SPAN> <SPAN =
class=3Dcpp-keyword>void</SPAN> StaticFun(C* pC)=20
{
cout << <SPAN class=3Dcpp-string>"StaticFun"</SPAN> =
<< endl;
pC->NonStaticFunc();
}
};
<SPAN class=3Dcpp-keyword>int</SPAN> main()
{
C objC;
C::StaticFun(&objC);
<SPAN class=3Dcpp-keyword>return</SPAN> <SPAN =
class=3Dcpp-literal>0</SPAN>;
}
</PRE>
<P>The output of this program is </P><PRE>StaticFun
NonStaticFun
</PRE>
<P>So we can use the same technique here i.e. store the address of =
ZWindow=20
object in a global variable and then call non static member =
function from=20
that pointer. Here is update version of the previous program in =
which we=20
are not calling windowing API directly. </P><B>Program 69</B> =
<PRE><SPAN class=3Dcpp-preprocessor>#include <windows.h></SPAN>
<SPAN class=3Dcpp-keyword>class</SPAN> ZWindow;
ZWindow* g_pWnd =3D NULL;
<SPAN class=3Dcpp-keyword>class</SPAN> ZWindow
{
<SPAN class=3Dcpp-keyword>public</SPAN>:
HWND m_hWnd;
ZWindow(HWND hWnd =3D <SPAN class=3Dcpp-literal>0</SPAN>) : =
m_hWnd(hWnd) { }
<SPAN class=3Dcpp-keyword>inline</SPAN> <SPAN =
class=3Dcpp-keyword>void</SPAN> Attach(HWND hWnd)
{ m_hWnd =3D hWnd; }
<SPAN class=3Dcpp-keyword>inline</SPAN> BOOL ShowWindow(<SPAN =
class=3Dcpp-keyword>int</SPAN> nCmdShow)
{ <SPAN class=3Dcpp-keyword>return</SPAN> ::ShowWindow(m_hWnd, =
nCmdShow); }
<SPAN class=3Dcpp-keyword>inline</SPAN> BOOL UpdateWindow()
{ <SPAN class=3Dcpp-keyword>return</SPAN> ::UpdateWindow(m_hWnd); }
<SPAN class=3Dcpp-keyword>inline</SPAN> HDC BeginPaint(LPPAINTSTRUCT =
ps)
{ <SPAN class=3Dcpp-keyword>return</SPAN> ::BeginPaint(m_hWnd, ps); =
}
<SPAN class=3Dcpp-keyword>inline</SPAN> BOOL EndPaint(LPPAINTSTRUCT =
ps)
{ <SPAN class=3Dcpp-keyword>return</SPAN> ::EndPaint(m_hWnd, ps); }
<SPAN class=3Dcpp-keyword>inline</SPAN> BOOL GetClientRect(LPRECT =
rect)
{ <SPAN class=3Dcpp-keyword>return</SPAN> ::GetClientRect(m_hWnd, =
rect); }
BOOL Create(LPCTSTR szClassName, LPCTSTR szTitle, HINSTANCE =
hInstance,=20
HWND hWndParent =3D <SPAN class=3Dcpp-literal>0</SPAN>, =
DWORD dwStyle =3D WS_OVERLAPPEDWINDOW,=20
DWORD dwExStyle =3D <SPAN class=3Dcpp-literal>0</SPAN>, =
HMENU hMenu =3D <SPAN class=3Dcpp-literal>0</SPAN>)
{
m_hWnd =3D ::CreateWindowEx(dwExStyle, szClassName, szTitle, =
dwStyle,=20
CW_USEDEFAULT, CW_USEDEFAULT, =
CW_USEDEFAULT,=20
CW_USEDEFAULT, hWndParent, hMenu, =
hInstance, NULL);
<SPAN class=3Dcpp-keyword>return</SPAN> m_hWnd !=3D NULL;
}
<SPAN class=3Dcpp-keyword>static</SPAN> LRESULT CALLBACK =
WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
ZWindow* pThis =3D g_pWnd;
HDC hDC;
PAINTSTRUCT ps;
RECT rect;
<SPAN class=3Dcpp-keyword>switch</SPAN> (uMsg)
{
<SPAN class=3Dcpp-keyword>case</SPAN> WM_PAINT:
hDC =3D pThis->BeginPaint(&ps);
pThis->GetClientRect(&rect);
::DrawText(hDC, <SPAN class=3Dcpp-string>"Hello =
world"</SPAN>, -<SPAN class=3Dcpp-literal>1</SPAN>, &rect,=20
DT_CENTER | DT_VCENTER | DT_SINGLELINE);
pThis->EndPaint(&ps);
<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>;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -