首先,我为你列出以下这个C++程序:
<code>01.</code><code>#include <iostream></code>
<code>02.</code><code>using</code> <code>namespace</code> <code>std;</code>
<code>03.</code><code>class</code> <code>Parent</code>
<code>04.</code><code>{</code>
<code>05.</code><code>public</code><code>:</code>
<code>06.</code><code>void</code> <code>func(</code><code>void</code><code>) { cout << </code><code>"func of Parent"</code> <code><< endl; }</code>
<code>07.</code><code>};</code>
<code>08.</code><code>class</code> <code>Child : </code><code>public</code> <code>Parent</code>
<code>09.</code><code>{</code>
<code>10.</code><code>public</code><code>:</code>
<code>11.</code><code>void</code> <code>func(</code><code>void</code><code>) { cout << </code><code>"func of Child"</code> <code><< endl; }</code>
<code>12.</code><code>};</code>
<code>13.</code><code>void</code> <code>main()</code>
<code>14.</code><code>{</code>
<code>15.</code><code>Parent p;</code>
<code>16.</code><code>Child c;</code>
<code>17.</code><code>p.func();</code>
<code>18.</code><code>c.func();</code>
<code>19.</code><code>}</iostream></code>
现在我来解说一下。这段代码中我定义了两个C++类:父类和子类,并且子类是继承自父类的;它们有一个具有相同名称的成员函数func。在main函数中,我分别构造了父类和子类的对象,并调用了它们各自的成员函数func。结果如下:
<code>1.</code><code>func of Parent</code>
<code>2.</code><code>func of Child</code>
简单说来,这段代码就是子类根据自己的需要改写了func成员函数。而Win32的子类化的原理也与此类似,只不过子类化实际上并没有像C++一样重载哪个函数,而是靠拦截Windows系统中的某些消息来自己进行处理罢了。举例来说,请大家看以下这段简单的窗口回调过程:
<code>01.</code><code>LRESULT</code> <code>CALLBACK ProcMain(</code><code>HWND</code> <code>hDlg, </code><code>UINT</code> <code>Msg, </code><code>WPARAM</code> <code>wParam, </code><code>LPARAM</code><code>lParam)</code>
<code>02.</code><code>{</code>
<code>03.</code><code>switch</code> <code>(Msg)</code>
<code>05.</code><code>case</code> <code>WM_CLOSE:</code>
<code>06.</code><code>EndDialog(hDlg, 0);</code>
<code>07.</code><code>break</code><code>;</code>
<code>08.</code><code>case</code> <code>WM_DESTROY:</code>
<code>09.</code><code>PostQuitMessage(0);</code>
<code>10.</code><code>break</code><code>;</code>
<code>11.</code><code>}</code>
<code>12.</code><code>return</code> <code>0;</code>
<code>13.</code><code>}</code>
在这个回调之中,我手动处理了两个消息:在单击了“关闭”按钮(WM_CLOSE)的时候,我将对话框关闭(EndDialog);在对话框销毁(WM_DESTROY)的时候,我向系统消息队列中发送了退出的消息来完成结束工作(PostQuitMessage)。也就是说,如果把WM_CLOSE的响应代码改成:
<code>1.</code><code>case</code> <code>WM_CLOSE:</code>
<code>2.</code><code>ShowWindow(hDlg, SW_MINIMIZE);</code>
<code>3.</code><code>break</code><code>;</code>
这样一来,这个对话框就会和MSN一样,在单击了“关闭”之后,就会完成最小化的工作了。那么,对于窗口过程已定义好的系统控件,将如何手动响应它的消息呢?
我们可以用函数指针的办法,将我们感兴趣的消息拦截下来,处理完之后再让预定义的窗口过程处理。这个过程大致如下:
<code>1.</code><code>WNDPROC OldProc;</code>
<code>2.</code><code> OldProc = (WNDPROC)SetWindowsLong(hWnd, GWL_WNDPROC, (</code><code>LONG</code><code>)NewProc);</code>
当然,这里的新窗口过程NewProc是预先由你实现好的。上述代码执行以后,系统在处理hWnd的窗口消息时,就会先进入你实现的NewProc回调过程,然后在处理过你感兴趣的消息之后,通过CallWindowProc函数和你预先保存的OldProc再次回到原来的回调过程中完成剩余的工作。
以上就是窗口子类化的原理分析,下面我通过一个实例来实际解说如何对窗口进行子类化。
这个例子的界面如下:

界面上方的编辑框是用来限制浮点输入的,下面则是一个普通的超级链接。
好了,下面我开始按步骤完成对这两个窗口的子类化:
第一步,在主窗口对话框初始化的时候,保存原有的窗口过程,并设置新的窗口过程。代码如下:
<code>1.</code><code>case</code> <code>WM_INITDIALOG:</code>
<code>2.</code><code>EditProc = (WNDPROC)SetWindowLong(GetDlgItem(hDlg, IDC_EDIT), GWL_WNDPROC, (</code><code>LONG</code><code>)ProcFloat);</code>
<code>3.</code><code>StaticProc = (WNDPROC)SetWindowLong(GetDlgItem(hDlg, IDC_ST_HOMEPAGE), GWL_WNDPROC, (</code><code>LONG</code><code>)ProcLink);</code>
<code>4.</code><code>break</code><code>;</code>
第二步,实现浮点编辑框的窗口过程:
<code>01.</code><code>LRESULT</code> <code>CALLBACK ProcFloat(</code><code>HWND</code> <code>hWnd, </code><code>UINT</code> <code>Msg, </code><code>WPARAM</code> <code>wParam, </code><code>LPARAM</code><code>lParam)</code>
<code>03.</code><code>if</code> <code>(Msg == WM_CHAR && wParam != </code><code>''</code><code>.</code><code>''</code> <code>&& (wParam <= </code><code>''</code><code>0</code><code>''</code> <code>|| wParam >= </code><code>''</code><code>9</code><code>''</code><code>) && wParam != VK_BACK)</code>
<code>05.</code><code>MessageBeep(MB_OK);</code>
<code>06.</code><code>return</code> <code>0;</code>
<code>07.</code><code>}</code>
<code>08.</code><code>else</code>
<code>09.</code><code>return</code> <code>CallWindowProc(EditProc, hWnd, Msg, wParam, lParam);</code>
<code>10.</code><code>}</code>
这里需要解释的是,由于控件本身的需求,所以只需要拦截一个消息,就是接收字符的WM_CHAR。当用户输入的字符不是小数点、0~9以及退格键(注意不要少了退格键,否则你将会发现你的编辑框无法删除输入错误的数字)的时候,就发出一声声音以提示输入错误。至于其它的消息,则调用原有的回调函数进行处理。
第三步,实现超级链接的窗口过程:
<code>01.</code><code>LRESULT</code> <code>CALLBACK ProcLink(</code><code>HWND</code> <code>hWnd, </code><code>UINT</code> <code>Msg, </code><code>WPARAM</code> <code>wParam, </code><code>LPARAM</code><code>lParam)</code>
<code>05.</code><code>case</code> <code>WM_SETCURSOR:</code>
<code>06.</code><code>SetCursor(LoadCursor(NULL, IDC_HAND));</code>
<code>08.</code><code>case</code> <code>WM_LBUTTONDOWN:</code>
<code>11.</code><code>default</code><code>:</code>
<code>12.</code><code>return</code> <code>CallWindowProc(StaticProc, hWnd, Msg, wParam, lParam);</code>
<code>14.</code><code>return</code> <code>0;</code>
<code>15.</code><code>}</code>
这段代码很容易明白:它完成了两件事,其一是设置光标指针为手形(注意:对于较早的Windows系统,是没有预定义的IDC_HAND指针的,这个时候你需要在EXE的资源中自己画一个手形指针,比如Delphi之中的手形指针就是自己画的),其二是当单击了鼠标左键的时候打开你想打开的网页链接。
其实对于超级链接,它更主要的东西是在子类化之外实现的――就是它的字体颜色(注意这段代码是在主窗口对话框的回调过程中实现的):
<code>1.</code><code>case</code> <code>WM_CTLCOLORSTATIC:</code>
<code>2.</code><code>if</code> <code>(GetDlgItem(hDlg, IDC_ST_HOMEPAGE) == (</code><code>HWND</code><code>)lParam)</code>
<code>3.</code><code>{</code>
<code>4.</code><code>SetTextColor((</code><code>HDC</code><code>)wParam, 0xff0000);</code>
<code>5.</code><code>SetBkMode((</code><code>HDC</code><code>)wParam, TRANSPARENT);</code>
<code>6.</code><code>return</code> <code>(</code><code>LRESULT</code><code>)CreateSolidBrush(GetSysColor(COLOR_BTNFACE));</code>
<code>7.</code><code>}</code>
<code>8.</code><code>break</code><code>;</code>
还有几点要说明的是:
1、你的这个Static超链接必须拥有一个唯一的资源ID,比如我的这个就是IDC_ST_HOMEPAGE,这样才能用GetDlgItem获得它的句柄来完成子类化;
2、你必须为它设置SS_NOTIFY样式,以保证在单击它的时候它能够通知父窗口对话框;
3、单击它打开网页的处理也可以放在子类化之外,处理主窗口对话框的WM_COMMAND消息也可以实现这一功能。
关于Win32的窗口子类化就介绍到这里了,你可以 点这里下载本文的配套源代码 ,代码中有详细的注释。