在Windows操作系统中,当用户按下"PrintScreen"按钮后,Windows自动将当前屏幕的图像拷贝到系统剪贴板中,这时可以通过"画笔"这个小程序将剪贴板中的内容保存成图像文件,可以看出,如果需要将当前屏幕保存下来还是比较麻烦的,能否可以自己编写一个应用程序,自动将当前屏幕的内容保存到一个图像文件中去呢?这个答案是肯定的,本实例应用程序就是利用通用的热键管理DLL库实现的热键功能,在收到热键通知后截取屏幕的内容并保存到bmp文件中。例如我们设置图片保存路径为c:\,热键为F9 + Control,然后按Change按钮设置好热键,那么当我们按下CTRL+F9后,当前界面将以BMP图像文件的格式被保存在C:\目录下。程序编译运行后的界面效果如图一所示:
}i_[wq{E&
} {/4sll 一、实现方法
Q6?+# } U@[P.y~J 热键管理DLL实际上是一个键盘钩子,由它来监视系统的键盘事件。如果有和程序登记符合的按键组合就通知该程序的窗口。为了应用方便,本实例把它做成了一个标准的管理库来为其它的程序通过热键服务,它有两个输出函数:AddHotkey()和DeleteHotkey(),程序只需要调用这两个函数就可以了,如果编译之后不用改变热键,则只需要AddHotkey就可以了。DLL中的所有的全局变量都放在一个共享段中,定义如下:
/1++ 8= }|f\'S #pragma data_seg("shareddata")
QQ@, v@j5 HHOOK hHook =NULL; //钩子句柄
R7T"fN UINT nHookCount =0; //挂接的程序数目
`xe[\Z2 static UCHAR HotKey[MAX_KEY] = {0}; //热键虚拟键码
SqY;2: static UCHAR HotKeyMask[MAX_KEY] = {0}; //组合掩码, control=4,alt=2,shift=1
>>krH'79 static HWND hCallWnd[MAX_KEY] = {0}; //window handle associated with hotkey
Y%qhgzz?/ static int KeyCount =0;
Rf2/[ static UCHAR MaskBits =0; //00000 Ctrl=4 & Alt=2 & Shift=1
><OdHRh@# #pragma data_seg()
] d| -r:4 i2b\`
805 关于共享段,有几点重要的说明:一是必须在链接选项里指定该段为共享:一种方法是在project->settings->link->object/library中加上/section:shareddata,rws;第二种方法是在def文件的sections里加上一句shareddata read write shared;第三种指定共享段的方法在程序里加上一句#pragma comment(linker,"section:shareddata,rws")。二是所有的变量必须初始化,否则链接程序会把它放到普通数据段。三是如果不初始化变量,需要在段外用"__declspec(allocate("shareddata")) 变量类型 变量名"的方式定义。
]>E9v&X0 hXS'*vO" DLL中的两个输出函数分别用来添加/删除热键,函数代码如下:
3hR7 ./ G/(oQA BOOL __declspec(dllexport) __stdcall AddHotkey(HWND hWnd,UCHAR
Jf`;F : cKey,UCHAR cMask)
P>euUVMPz4 {
tZ>>aiI3 BOOL bAdded=FALSE;
'N=' B<^;% for(int index=0;index<MAX_KEY;index++){
?t)y/@eG if(hCallWnd[index]==0){
/0SG hCallWnd[index]=hWnd;
h0n0Dc{4 HotKey[index]=cKey;
&.kg8|s{ HotKeyMask[index]=cMask;
F-k1yZ?^ bAdded=TRUE;
i#lvt#2J0 KeyCount++;
M;zRf3S break;
c%pW'UE& }
fFXs:( }
9YHSL[ return bAdded;
V2w[0^L }
2EE/xnwX //删除热键
U'-MMwE] BOOL __declspec(dllexport) __stdcall DeleteHotkey(HWND hWnd,UCHAR cKey,UCHAR cMask)
l=9& {
voQ, K9 BOOL bRemoved=FALSE;
%9mB4Fc6b) for(int index=0;index<MAX_KEY;index++){
:;0?;dpO if(hCallWnd[index]==hWnd){
mm#U a/~1u if(HotKey[index]==cKey&&HotKeyMask[index]==cMask){
j K[VEhs hCallWnd[index]=NULL;
k`)LO`)) HotKey[index]=0;
U
YUIpe HotKeyMask[index]=0;
F]yclXf(' bRemoved=TRUE;
N_pJE? KeyCount--;
n4."}DO break;
Cy6[p }
G&C)`}; }
e7#=F6 }
U\VwJ2
{i return bRemoved;
(!X:[Ah*$ }
7C2/^x P NBMY1Xgj 0SKt8pL` DLL中的钩子函数如下:
@D"|Jq=6P 8QC:ro LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
;SC|VcbyH {
-m'a%aog BOOL bProcessed=FALSE;
{zF if(HC_ACTION==nCode)
#i2q}/w5`C {
ixKQh};5/ if((lParam&0xc0000000)==0xc0000000){// 有键松开
gP1$#KgU switch(wParam)
u]dpA {
`}Q;2 F case VK_MENU:
Wnf`Rf)1z MaskBits&=~ALTBIT;
RuRJ jcnY break;
J.?p?-" case VK_CONTROL:
?J6hiQvL MaskBits&=~CTRLBIT;
IL%&*B break;
yp*kMC,3 case VK_SHIFT:
nAts.pVy" MaskBits&=~SHIFTBIT;
xD1wHp!+ break;
.!6ufaf$ default: //judge the key and send message
n,HWVo>([ break;
-5>g 0o2 }
pwZ &2&| for(int index=0;index<MAX_KEY;index++){
[e^i". if(hCallWnd[index]==NULL)
`7
B
[< continue;
}<Me%`x" if(IsWindow(hCallWnd[index])&&(HotKey[index]==wParam)&&(HotKeyMask[index]==MaskBits))
R6:N`S]&d[ {
*@BBlkcx SendMessage(hCallWnd[index],WM_HOTKEY,wParam,WM_KEYUP);
Xif`gb6` bProcessed=TRUE;
[FCNW0NV }
A|a\pL` @ }
Hd2_Cg FB }
G:&Q)_ else if((lParam&0xc000ffff)==1){ //有键按下
(6A{6_p switch(wParam)
U)l>#gf8 {
ftH
0aI case VK_MENU:
B " B MaskBits|=ALTBIT;
1)Ag|4 break;
.-34g5 case VK_CONTROL:
>qgBu_ MaskBits|=CTRLBIT;
e| l?NXRX break;
cM"I3 case VK_SHIFT:
s3knh&'zb MaskBits|=SHIFTBIT;
~g,QwaA[ break;
G?V3lQI1n default: //judge the key and send message
%FRkvqV* break;
W ][IHy< }
;s!H for(int index=0;index<MAX_KEY;index++){
G^OSXf5 if(hCallWnd[index]==NULL)
hg&AQk continue;
|JDJ{;o if(IsWindow(hCallWnd[index])&&(HotKey[index]==wParam)&&(HotKeyMask[index]==MaskBits))
aOuon0 {
a3)#tt=rA SendMessage(hCallWnd[index],WM_HOTKEY,wParam,WM_KEYDOWN);
\ lKQ'_ bProcessed=TRUE;
ynd}w
G' }
s1FBz)yCY= }
KWY G\#S0] }
N1$P6ZF if(!bProcessed){ //一般按键事件,为监视键盘的程序留出余地
Lr~K3nb for(int index=0;index<MAX_KEY;index++){
[/kO> if(hCallWnd[index]==NULL)
lt]U?VZ continue;
sd6Wmmo if(IsWindow(hCallWnd[index])&&(HotKey[index]==0)&&(HotKeyMask[index]==0))
e8# 3Y+Tc SendMessage(hCallWnd[index],WM_HOTKEY,WM_HOTKEY,lParam);
sg y //lParam的意义可看MSDN中WM_KEYDOWN部分
&ar}6eO }
4~/6d9f }
g:)vthOs }
][mc^eI0s| return CallNextHookEx( hHook, nCode, wParam, lParam );
utE:HD.PN }
a]k&$ ;0*^9 8K 抓图程序是一个基于对话框的程序,它在建立对话框的时候调用前面的DLL,登记热键,因此需要将hook.lib添加到工程里,在程序里给出两个DLL函数的定义,也可以写个头文件,再包含进来以下代码:
5a&wM {MtB!x BOOL __declspec(dllexport)__stdcall AddHotkey(HWND,UCHAR key,UCHAR mask);
R;yAqr29 BOOL __declspec(dllexport)__stdcall DeleteHotkey(HWND,UCHAR key,UCHAR mask);
Rt|Hma "10VN*)J} 为了添加应用程序对热键通知事件的响应,程序中用的办法是重载WindowProc()函数,该函数代码如下:
L_r &'B {P5@2u6S LRESULT CCaptureDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
P(;c` {
C"{on% if(message==WM_HOTKEY&&lParam==WM_KEYDOWN)
SMFW]I2T/ {
$^!w`>0C //lParam表示是按下还是松开,如果有多个热键,由wParam来区分
dNobvK SaveBmp();
r;C\eN return FALSE;
t;]egk }
-(.7/G'Vk> …… //其它处理及默认处理
aiHr2x6 }
h"ylpv+ fz3*oJ' =1VH5pVr} 将屏幕图像保存到BMP文件中的任务是由函数SaveBmp()来完成的,具体实现参见代码部分。另外为了顺利实现屏幕抓图,程序中还实现了"托盘"功能,由于这一部分本书在实例中已经专门介绍过了,所以不再赘述,读者朋友可以参考相关实例。
D2p6&HNT \gkajY-? 最后需要提醒读者朋友们注意的是,源程序的编译与使用时要先编译hook.dll并将其放在系统目录(win2000/NT是system32,98/ME是system),然后编译对话框程序运行即可。
m=}X$QF`^ x,dv~QU 二、编程步骤
G#:!wI ;Y^RF?un 1、 启动Visual C++,生成一个DLL项目和一个基于对话框的应用程序项目,并将两个项目分别命名为"Hook"和"Capture";
5m42Bqy" 0 l@P]_qq` 2、 在"Hook"项目中导出AddHotkey()、DeleteHotkey()函数;
HH?*"cKF~ 6~ `bAe`} 3、 在"Capture"项目中按照图一所示设置对话框的界面,具体设置参见代码部分;
~S6 {VK. o3qv945 4、 使用Class Wizard在"Capture"项目中添加按钮的鼠标单击消息响应函数,并重载对话框的WindowProc()函数;
]Qr8 wa>Z u2]g1XjeG 5、 添加代码,编译运行程序。
\T9UbkR hH}/v0_ jb 三、程序代码
L@r.R_*H?s VWy:U#;+8 ///////////////////////////////////// Hook.h : main header file for the HOOK DLL
yXx62J #if !defined(AFX_HOOK_H__AEBFF705_C93A_11D5_B7D6_0080C82BE86B__INCLUDED_)
2H[aY%1T #define AFX_HOOK_H__AEBFF705_C93A_11D5_B7D6_0080C82BE86B__INCLUDED_
ssITe.,ny #if _MSC_VER > 1000
QJWES%m` #pragma once
[Z\1"m #endif // _MSC_VER > 1000
->I{
:# #ifndef __AFXWIN_H__
zI! R-Nb #error include 'stdafx.h' before including this file for PCH
>-lL-%N_ #endif
2TEeP7 #include "resource.h" // main symbols
~q(C j"7 class CHookApp : public CWinApp
J.2]km {
tcEf
~|3 public:
t[,T}BCy. CHookApp();
' u};z:t // Overrides
3b@1Zahz // ClassWizard generated virtual function overrides
Os 2YZ<t //{{AFX_VIRTUAL(CHookApp)
K)UOx#xe1 public:
sFCs_u1tNN virtual BOOL InitInstance();
y~c[sW virtual int ExitInstance();
0Y*gJ!a //}}AFX_VIRTUAL
w9<FX>@ //{{AFX_MSG(CHookApp)
X]MM7hMuR // NOTE - the ClassWizard will add and remove member functions here.
YCBML!L // DO NOT EDIT what you see in these blocks of generated code !
,
)pt_"-XA //}}AFX_MSG
>ha Ixs`9 DECLARE_MESSAGE_MAP()
ioa_AG6B };
lkWeQ)V LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam, LPARAM lParam);
dzNaow*0&V BOOL __declspec(dllexport)__stdcall AddHotkey(HWND,UCHAR key,UCHAR mask);
{C0^D*U: BOOL __declspec(dllexport)__stdcall DeleteHotkey(HWND,UCHAR key,UCHAR mask);
soQzIx BOOL InitHotkey();
=tRe3o0( BOOL UnInit();
Y_3YO2K] #endif
Ox*T:5 F"f}vl //////////////////////////////////////// Hook.cpp : Defines the initialization routines for the DLL.
Doc'7P #include "stdafx.h"
G%8)6m'3 #include "hook.h"
< Gy!i/ #include <windowsx.h>
Xa%&.&V #ifdef _DEBUG
h8= MVh(I #define new DEBUG_NEW
nB Iv{ #undef THIS_FILE
7U
)qC}( static char THIS_FILE[] = __FILE__;
+fCyR #endif
*o\Y~U-so #define MAX_KEY 100
>`?+FDOJ, #define CTRLBIT 0x04
'j<:FUDJ #define ALTBIT 0x02
b:5%} #define SHIFTBIT 0x01
\
NSw<. #pragma data_seg("shareddata")
(:bCOEZ HHOOK hHook =NULL;
=']}; UINT nHookCount =0;
aU]O$Pg{ static UCHAR HotKey[MAX_KEY] = {0}; //hotkey
:f
!=_^} static UCHAR HotKeyMask[MAX_KEY] = {0}; //flag for hotkey, value is VK_CONTRL or VK_NEMU or VK_SHIFT
$s(4?^GP static HWND hCallWnd[MAX_KEY] = {0}; //window associated with hotkey
Hs`#{W{. static int KeyCount =0;
]7R&m)16 static UCHAR MaskBits =0; //00000 Ctrl Alt Shift
yZ~eLWz #pragma data_seg()
vb. Y8[ HINSTANCE hins;
vH?+JN"A void VerifyWindow();
uZ`d&CEh BEGIN_MESSAGE_MAP(CHookApp, CWinApp)
'UXj\vJ3E //{{AFX_MSG_MAP(CHookApp)
D7_Hu'y<o // NOTE - the ClassWizard will add and remove mapping macros here.
07LL)v~ // DO NOT EDIT what you see in these blocks of generated code!
0@e}hv; //}}AFX_MSG_MAP
-2jBs-z END_MESSAGE_MAP()
;'Vipj A]O5+"mc CHookApp::CHookApp()
.";tnC!e {
L3=YlX`UL // TODO: add construction code here,
,ORG"]_F // Place all significant initialization in InitInstance
gC_s\WU }
<j1d~XU} 3VRZM@i CHookApp theApp;
ZG\ I1 LRESULT CALLBACK KeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)
ZaUcP6[h {
j
zmSFK g* BOOL bProcessed=FALSE;
K,C$J
I if(HC_ACTION==nCode)
qvfAG 0p {
@a.6?.<L if((lParam&0xc0000000)==0xc0000000){// Key up
o~.o^0Y switch(wParam)
0q>NE<L {
cxP9n8CuT case VK_MENU:
'^oGDlkr H MaskBits&=~ALTBIT;
8NAWA3^B break;
S `m-5 case VK_CONTROL:
X.T\=dm%v MaskBits&=~CTRLBIT;
H6PXx break;
6Er0o{iI case VK_SHIFT:
) (?UA$" MaskBits&=~SHIFTBIT;
vA"`0 break;
+.gf]| default: //judge the key and send message
VNxpOoV=S break;
=@1R ozt }
5;K-,"UQ for(int index=0;index<MAX_KEY;index++){
UP~WP@0F if(hCallWnd[index]==NULL)
I"F
.%re continue;
-|k&L}\OB0 if(IsWindow(hCallWnd[index])&&(HotKey[index]==wParam)&&(HotKeyMask[index]==MaskBits))
J^g,jBk {
1 ~B< SendMessage(hCallWnd[index],WM_HOTKEY,wParam,WM_KEYUP);
q(jkit~`A bProcessed=TRUE;
%g]$Vfpy }
M]4 =(Vv+5 }
LpiHoavv }
aX[1H6&=7 else if((lParam&0xc000ffff)==1){ //Key down
_']%qd"% switch(wParam)
#A|D\IhF {
}o!#_N0T case VK_MENU:
6<qwP?WN MaskBits|=ALTBIT;
Re kb?|{z
break;
r{_B: case VK_CONTROL:
c+Q.?vJ MaskBits|=CTRLBIT;
m<FWv2)^ break;
(/"K+$8' case VK_SHIFT:
{XDY:`vZ} MaskBits|=SHIFTBIT;
~<2 IIR$H break;
Z92iil;t default: //judge the key and send message
$IxU6=ajn break;
$TD~k; }
^6)GS%R for(int index=0;index<MAX_KEY;index++)
7g A08M[O {
5q(]1|Sei if(hCallWnd[index]==NULL)
|2
YubAIZ( continue;
WNn[L=f if(IsWindow(hCallWnd[index])&&(HotKey[index]==wParam)&&(HotKeyMask[index]==MaskBits))
X6LhM {
gnQo1q{ 4 SendMessage(hCallWnd[index],WM_HOTKEY,wParam,WM_KEYDOWN);
H{\.g=01 bProcessed=TRUE;
tb:,Uf>E }
VxkCK02k }
O6m.t%* }
E[|s>Xv~ if(!bProcessed){
:mXGIRi for(int index=0;index<MAX_KEY;index++){
Snc;p if(hCallWnd[index]==NULL)
biK)&6|`sa continue;
.>mH]/]m if(IsWindow(hCallWnd[index])&&(HotKey[index]==0)&&(HotKeyMask[index]==0))
&v5G92 SendMessage(hCallWnd[index],WM_HOTKEY,WM_HOTKEY,lParam);
V;h=8C 5J }
^CZCZ,v }
HE#,(;1i }
&a%WM return CallNextHookEx( hHook, nCode, wParam, lParam );
uipq=Yp. }
mR{CVU csH2_+uG BOOL InitHotkey()
Zg
-]sp] {
{]wIM^$6+ if(hHook!=NULL){
\#4m@ nHookCount++;
R)0N0gH return TRUE;
r4O|() }
$&cz$jyY else
Y3#Nux% hHook=SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,hins,0);
Q~Ay8L+ if(hHook!=NULL)
NH7`5mF$ nHookCount++;
J|VK P7 return (hHook!=NULL);
Z[",$Lt }
BjJ,"sT BOOL UnInit()
(GCG/8s {
z;i4N3-: if(nHookCount>1){
OFc\fW# nHookCount--;
D`6iDit return TRUE;
1HMUHZT }
n[!;yO BOOL unhooked = UnhookWindowsHookEx(hHook);
zVSx$6eiU if(unhooked==TRUE){
^>C11v nHookCount=0;
"jMqt9ysN hHook=NULL;
RFw(]o,9cR }
3Jk[/.h return unhooked;
otO
j^xU }
*HR+a#o B[r<m J BOOL __declspec(dllexport) __stdcall AddHotkey(HWND hWnd,UCHAR cKey,UCHAR cMask)
.%q$d d>> {
'"y}#h__T BOOL bAdded=FALSE;
B$rTwR"(- for(int index=0;index<MAX_KEY;index++){
|,b2b2v? if(hCallWnd[index]==0){
3y99O
$EAc hCallWnd[index]=hWnd;
"!O1j
r; HotKey[index]=cKey;
Q]@c&* _| HotKeyMask[index]=cMask;
m`z7fi7u bAdded=TRUE;
~W<CE_/]k KeyCount++;
\)OEBN`9# break;
hk%k(^ekU] }
*i^`Dw^~y }
F|>05>8 return bAdded;
GF8 -_X }
T-'~? [v # ly@;!M BOOL __declspec(dllexport) __stdcall DeleteHotkey(HWND hWnd,UCHAR cKey,UCHAR cMask)
F$Hx`hoy {
gV8"VZg2 BOOL bRemoved=FALSE;
Y25^]ON*\^ for(int index=0;index<MAX_KEY;index++){
.wmnnvtl, if(hCallWnd[index]==hWnd){
8V9[a*9 if(HotKey[index]==cKey&&HotKeyMask[index]==cMask){
ks*Y9D*= hCallWnd[index]=NULL;
r`jWp\z HotKey[index]=0;
,>n%
~'gb HotKeyMask[index]=0;
`0{ S3v bRemoved=TRUE;
{CtR+4KD KeyCount--;
44\cI]!{ break;
=7%oE[ }
P ZxFZvE }
@Y}uZ'jt' }
Tkw;pb return bRemoved;
WNs}sNSf }
sYqgXE. 1svi8wh void VerifyWindow()
y+9h~,:A {
} XR:2 for(int i=0;i<MAX_KEY;i++){
9@K.cdRjQ if(hCallWnd
!=NULL){ 2#vv$YD
if(!IsWindow(hCallWnd)){ 7AwgJb hn
hCallWnd=NULL; !lTda<;]
HotKey=0; e$e#NoN
HotKeyMask=0; 5|I55CTx
KeyCount--; Ub_4yN;
} 2 rN ,D(
} c>HK9z{
} l>6@:nq|R
} $g10vF3
`y^sITr
BOOL CHookApp::InitInstance() EpYy3^5d
{ +oc
>S
AFX_MANAGE_STATE(AfxGetStaticModuleState()); ?+0GfIV
hins=AfxGetInstanceHandle(); )J D(`
InitHotkey(); qQ0C ?
return CWinApp::InitInstance(); x%N\5 V1
} a|4~NL
_Ov;4nt!
int CHookApp::ExitInstance() +GF#?X0^
{ zR;X*q"T$4
VerifyWindow(); #!2k<Q*5uT
UnInit(); ]y@F8$D!
return CWinApp::ExitInstance(); S'LZk9E
} |Ic`,>XM
({^9<Us
////////////////////////////////////////////////////////////////////// CaptureDlg.h : header file 8RW&r
#if !defined(AFX_CAPTUREDLG_H__97B51708_C928_11D5_B7D6_0080C82BE86B__INCLUDED_) 5J2=`=FK
#define AFX_CAPTUREDLG_H__97B51708_C928_11D5_B7D6_0080C82BE86B__INCLUDED_ /9pM>Cd*Z
#if _MSC_VER > 1000 zYY$D.
#pragma once ])DX%$f
#endif // _MSC_VER > 1000 CZ<~3bEF
q.#[TI ^
class CCaptureDlg : public CDialog 3+Qxg+<
{ 69K{+|
// Construction L!Cz'm"Nl
public: )~J>X{hy
BOOL bTray; . @q-B+Eg
BOOL bRegistered; GoM
ip8'u
BOOL RegisterHotkey(); 8.J(r(;>
UCHAR cKey; |RkcDrB~
UCHAR cMask; )E#2J$TD
void DeleteIcon(); i_Re*
void AddIcon(); 3REx45M2
UINT nCount; ulFzZHJ
void SaveBmp(); O,2~"~kF
CCaptureDlg(CWnd* pParent = NULL); // standard constructor '*^yAlgtt
// Dialog Data B+d<F[|
//{{AFX_DATA(CCaptureDlg) 7dG79H
enum { IDD = IDD_CAPTURE_DIALOG }; j%u-dr
CComboBox m_Key; Kv'n:z7Md
BOOL m_bControl; '_\;jFAM
BOOL m_bAlt; $rF=_D6
BOOL m_bShift; j(:I7%3&(*
CString m_Path; 'ly?P8h
CString m_Number; im7nJQ^H$q
//}}AFX_DATA 1=sXdcy;
// ClassWizard generated virtual function overrides VV4Gjc
//{{AFX_VIRTUAL(CCaptureDlg) {GDmVWG0q
public: 1OLqL
virtual BOOL PreTranslateMessage(MSG* pMsg); SzwQOs*
protected: bzDIhnw
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support `\`> 0hlu
virtual LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam); S{4z?Ri, '
//}}AFX_VIRTUAL GZS1zTwBL
// Implementation Q|Y0,1eVp|
protected: Jj~EiA
HICON m_hIcon; RIu~ @
// Generated message map functions NjPDX>R\K
//{{AFX_MSG(CCaptureDlg) f<=Fsl
virtual BOOL OnInitDialog(); W?yd#j
afx_msg void OnSysCommand(UINT nID, LPARAM lParam); \!IMaB]
afx_msg void OnPaint(); ~R$~&x