您的位置:首页 > 其它

windows消息处理(强烈推荐,收藏)

2012-08-22 17:30 387 查看
由于看了一下,比较好理解,暂时先放到这里,待有空再翻译。只是在每节后大致介绍一下讲的内容。

感觉写的比较全,无论从消息的原理还是从MFC操作上来说,值得一看,我也在此做个收藏。
(一)
说明:以下首先对消息进行介绍,然后在消息处理中,使用类向导创建消息循环,这个操作是在vc6.0(或者之下版本)操作的。

Introduction

PerhapsoneofthemostimportantmeansofcommunicationinwindowsisMessages.Thetraditionalprogramstartsatyour
main()
function,movesdownline-by-lineinyourcode,andeventuallyexits.
TheWindowsconceptisdifferent.Thewayyouprograminwindowsisbyrespondingtoevents.Theseeventsarecalledmessages.

Messagescansignalmanyevents,causedbytheuser,theoperatingsystem,oranotherprogram.Aneventcouldbecausedbyamousemove,akey-press,orbyyourwindowgettingresized.Thereare2kindsofmessages:awindowmessage,orathreadmessage.
SinceThreadsareanadvancedissue,I'llreferonlytowindowmessages.

WindowMessages:

Ingeneral,amessagemustbesenttoawindow.AllthemessagessenttoyouarestoredinaMessageQueue,aplaceinthememorywhichstoresmessagewhicharetransferredbetweenapplications.

MessageLoop:

thewayyouretrievemessagesfromtheMessageQueueisbycreatingaMessageLoop.AMessageLoopisaloopthatchecksformessagesintheMessageQueue.onceamessageisreceived,theMessageLoopdispatchesthemessagebycallingaMessageHandler,
afunctiondesignedtohelptheMessageLoopatprocessingthemessage.

TheMessageLoopwillendwhena
WM_QUIT
messageisreceived,signalingtheapplicationtoend.ThismessagecouldbesentbecausetheuserselectedExitfromyourFilemenu,clickedonthe
closebutton(theXsmallbuttonintheupperrightcornerofyourwindow),orpressedAlt+F4.WindowshasdefaultMessageHandlersforalmostallthemessages,givingyourwindowthedefaultwindowbehavior.Infact,allthestandardcontrolsaresimplywindows
withMessagehandlers.TakeaButtonforexample.Whenitgetsa
WM_PAINT
messageitwilldrawthebutton.WhenyouLeft-clickthebutton,itgetsa
WM_LBUTTONDOWN

message,anditdrawsthepressed-button.Whenyouletgoofthemousebuttonitreceivesa
WM_LBUTTONUP
message,andrespectivelydrawsthebutton.

Windowsdefinesmanydifferentmessagetypes(whicharestoredasUINTs).Theyusuallybeginwiththeletters"WM"andanunderscore,asin
WM_CHAR
and
WM_SIZE
.
Thenamesofthemessageareusuallyagoodindicatorofwhattheyrepresent.
WM_SIZE
forsizingmessages,
WM_CHAR
forcharacterentry
messagesandsoon.ThenamingconventioninMFCformessagehandlerfunctionsistotakeawaythe"WM_"andreplaceitwith"On",sothemessagehandlerfor
WM_SIZE
isusuallycalled
OnSize
.

Amessagecomeswith2parametersthatgiveyoumoreinformationabouttheevent.Eachparameterisa32-bitvalue:lParamandwParam.Forexample:
WM_MOUSEMOVE
willgiveyouthemousecoordinates
inoneparamter,andintheothersomeflagsindicatingthestateoftheALT,Shift,CTRLandmousebuttons.

AMessagemayalsoreturnavaluewhichallowsyoutosenddatabacktothethesendingprogram.Forexample,the
WM_QUERYENDSESSION
messagesentbywindowsbeforethecomputerisshutdown,expects
youtoreturnaBooleanvalue.Ifyourapplicationcanterminateconveniently,itshouldreturnTRUE;otherwise,itshouldreturnFALSE.Othermessagesuchasthe
WM_CTLCOLOR
messagesexpectyou
toreturnan
HBRUSH
.

Note:IntherestofthetutorialIwillfocusonMFCforsimplicityreasons.AlltheinformationaboveappliestobothSDKprograms,andMFCprograms.

MessageHandlers:

Fortunately,MFCwillgiveallthecodeneededforthemessageloop,Oneofthe
CWinApp
memberfunctionscalledbyWinMain—Run—providesthemessageloopthatpumpsmessagestotheapplication's
window.TheonlythingyouneedtodosoyoucanreceivemessagesistocreateMessageHandlers,andinformMFCofthem.So,howdoyoucreateaMessageHandler?OnceyouhaveanMFCC++classthatencapsulatesawindow,youcaneasilyuseClassWizardtocreate
MessageHandlers.

UsingClassWizardtocreateMessageHandlers:

PressCtrl+WtostarttheClassWizard,orrightclicktheAddbuttonandselectClassWizardfromthecontextmenu.OpenClassWizard,selectMessageMapstab.InClassnameselectthenameofyourC++class.onObjectIDsselecteithertheIDofamenu
item(formessagescausedbytheuserinteractingwithamenu),theIDofacontrol(formessagescausedbytheuserinteractingwithacontrol),orthefirstoptiontohandlemessagesothermessages.ChoosethemessagefromtheMessageslist,
WM_SIZE

forexample,andClickonAddFunction.ClickOK,thenclickEditCode.ClassWizardwillwriteanewemptyfunction(
OnSize
forexample)withtheproperprototypeintheclassheader.Thecode
generatedshouldlooksimilartothis:

voidCAboutWindow::OnSize(UINTnType,intcx,intcy)
{
CDialog::OnSize(nType,cx,cy);
//TODO:Addyourmessagehandlercodehere

//Hereiswhereyoucanresizecontrolsinyourwindow,change
//thesizeofabitmapinit,ordowhateveryoucanthinkof.
}

That'sit,nowyoucanhandlemessages.Ifyouwanttohandleamessageandthenletthedefaultmessagehandlerhandlethemessage,youshouldcallthebaseclassmemberfunctionthatcorrespondswiththemessage.Takethefollowing
WM_CLOSE

MessageHandlerasanexample:

voidCAboutWindow::OnClose()
{
//TheUseroranotherprogramistryingtocloseourwindow...
//Ifyoudon'taddcodetoclosethewindow,yourwindowwillneverclose
}

Ifyouwantwindowstogetashotatthemessage,youshouldcallthebaseclassmemberfunctionOnClose:

voidCAboutWindow::OnClose()
{
MessageBox(_T("Closingthewindow!"))
//CalltheBaseclassmemberfunction,whichwillclosethewindow.
CWnd::OnClose()
}

Youcouldusethisbehaviortoscreen-outevents.Forexample,aprogramthatpromptstheuserifheissurethathewantstoclosethewindow:

voidCAboutWindow::OnClose()
{
intRet=MessageBox(_T("Areyousureyouwanttoclosethewindow?"),
_T("CloseWindow?"),MB_YESNO);
if(Ret==IDYES){
//TheUserissure,closethewindowbycallingthebaseclass
//member
CWnd::OnClose()
}
else{
//Theuserpressedno,screenoutthemessagebynotcalling
//thebaseclassmember

//Donothing
}
}

SendingMessages:

Besidesreceivingmessages,youwilloftenfindyourselfsendingmessages.Youmightwanttosendmessagestocommunicatebetweentowindowsinyourprogram,ortocommunicatebetweendifferentprograms.Inordertosendamessageyouneedapointerto
ac++windowclass.Thiscanberetrievedusingvariousfunctions,including
CWnd::FindWindow,GetDlgItem(),GetParent(),
andmore.The
CWnd
classhasa
SendMessage()

memberfunctionwhichallowsyoutosendmessagestoit'swindow.Forexample,Let’ssayyouhaveaCWndpointertotheCalculator,andyouwanttocloseit.Whatyoushoulddoissenda
WM_CLOSE
message,whichwillnotifytheCalculatorthatitshouldclose.Youcanusethefollowingcode.InordertogetapointertoCalculator,Iusethestatic
CWnd::FindWindow()

functionandpassthetitleofthewindow,whichinourcaseis"Calculator".

CWnd*pCalc;
//Getapointertothe"Calculator"Window
pCalc=CWnd::FindWindow(NULL,_T("Calculator));
if(pCalc==NULL){
//Couldn'tfindCalculator
}
else{
pCalc->SendMessage(WM_CLOSE);
//Presto!TheCalculatorshouldclose.
}


(二)

HowdoMFCmessagehandlerswork?

Wheneveryourwindowreceivesamessage,MFCwillcallamemberfunctionofyourclass.ButhowdoesMFCknowwhatfunctiontocall?

MFCusesatechniquecalledMessageMaps.AMessageMapisatablethatassociatesmessageswithfunctions.Whenyoureceiveamessage,MFCwillgothroughyourMessageMapandsearchforacorrespondingMessageHandler.IhaveshowedinPart1howyouadd
aMessageHandlertotheMessageMapbyusingClassWizard,butwhatreallyhappenscode-wise?

MFCusesalargesetofrathercomplicatedmacrosthataddtheMessageMaptoyourclasses.WhenyouuseClassWizardtocreateaMessageHandler,itwillfirstaddthefunctiontoyourclass,andaddthecorrespondingmacrotoyourMessageMap.Forexample,
examinethefollowingClassWizardgenerated
WM_CLOSE
handler:

第一步:消息映射---MessageMap:locatedintheclassimplementation

BEGIN_MESSAGE_MAP(CAboutDlg,CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
ON_WM_CLOSE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

第二步:函数声明---FunctionDeclaration:locatedintheclassdeclaration.

protected:
//{{AFX_MSG(CAboutDlg)
afx_msgvoidOnClose();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()

第三步:函数执行---FunctionImplementation:locatedintheclassimplementation

voidCAboutDlg::OnClose()
{
//TODO:Addyourmessagehandlercodehereand/orcalldefault

CDialog::OnClose();
}


Byaddinga
DECLARE_MESSAGE_MAP
statementtotheclassdeclaration,MFCaddstherequiredcodetodeclarethemessagemap.The
BEGIN_MESSAGE_MAP

tellsMFCwheretheMessageMapbegins,andidentifiesyourclassandit'sbaseclass.ThereasonitneedsthebaseclassisbecauseMessageHandlersarepassedthroughc++inheritance,justlikeanyotherfunction.
END_MESSAGE_MAP

obviously,tellsMFCwheretheMessageMapends.InbetweenthesetwomacrosiswhereyourdeclaretheMessageMapentryforyourMessageHandler.MFChasmanypredefinedmacros,whichassociatemessageswithyourmemberfunction.Takethethe
ON_WM_CLOSE

macroasanexample:Itassociatesthe
WM_CLOSE
messagewithyour
OnClose()
memberfunction.Themacrotakesnoparameterssinceit
alwaysexpectsafunctioncalled
OnClose()
whichisprototypedas
afx_msgvoidOnClose()
.Thismethodgivesyou2advantages:

ItiseasytokeeptrackofMessageHandlersandthemessagestheyhandle
MFCscreensoutanyirrelevantandwillbreakuplParamandwParamtoparametersrelevanttothemessage.

Alsothereturnvalueissimplified,andtheMessageHandlerisprototypedaccordingtothemessage.Forexample:Ifthevalueshouldalwaysbezero,MFCsimplifiestheprocessandallowsyoutodeclarethefunctionasa
void
,
andMFCwillberesponsibleforreturning0.TofindthenameofthemessagehandlerthatcorrelateswithagivenMessageHandlermacroyoushouldlookitupintheMFCdocumentation.

TherearesomemessagesthatClassWizarddoesn'tsupport,butyoucanmanualyaddyourmessagehandlerbyaddingthefunctionandMessageMapmacroasdescribedabove.Ifyouaddmessage-mapentriesmanually,youmaynotbeabletoeditthemwithClassWizard
later.Ifyouaddthemoutsidethebracketingcomments
//{{AFX_MSG_MAP(classname)
and
//}}AFX_MSG_MAP
,ClassWizardcannoteditthem
atall.NotethatbythesametokenClassWizardwillnottouchanyentriesyouaddoutsidethecomments,sofeelfreetoaddmessagesoutsidethecommentsifyoudonotwantthemtobemodified.MessagesthatarenotrecognizedbyClassWizard,suchasmessage-map
ranges,mustbeaddedoutsidethecomments.

TheallmightyON_MESSAGE

SometimesyouwillfindyourselftryingtohandleamessagethatClassWizarddoesn'tsupport,anditdoesn'thaveaMessageMapmacro.MFChasagenericmacrojustforthiskindofsituation
ON_MESSAGE
.
ON_MESSAGE

allowsyoutohandleanymessagethatexists.TheprototypeofMessageHandlersthatuse
ON_MESSAGE
is

afx_msgLRESULTOnMessage(WPARAMwParam,LPARAMlParam);


where
OnMessage
isthenameofyourhandlerfunction.The
ON_MESSAGE
macrotakes2parameters:Theaddressofthehandler,and
themessageitshouldhandle.Forexample:ThefollowingstatementMaps
WM_GETTEXTLENGTH
to
OnGetTextLength()
:

ON_MESSAGE(WM_GETTEXTLENGTH,OnGetTextLength)

OnGetTextLength
isprototypedas

afx_msgLRESULTOnGetTextLength(WPARAMwParam,LPARAMlParam);


说明:以上部分还是接第一部分的类向导,通过ctrl+w快捷键进入,选择之后,vc6会自动把上文标注的三步骤放到相应的位置,下边自定义的消息(在类向导中没有的消息,自己定义的消息),则可以参考上文的三步骤,在和它们对应的位置添加,注意这个需要定义一个消息宏(下面红色标注),WM_APP现在应该为WM_USER,自定义消息只需要在WM_USER之上加上一个数字,因为一般WM_USER之前的消息,是系统消息(ctrl+w中看到的鼠标点击消息、键盘消息】重绘消息等)。

User-definedmessages

Sometimes,youwillneedtocommunicatebetween2windowsinyourapplicationorbetween2windowsfromdifferentapplications.AneasywaytodothisisbyusingUser-definedmessages.Thename"User-defined"canbeconfusingatfirst;youdefineaUser-defined
messageandnottheuserofyourprogram.IhavestatedinPart1thatmessagesareidentifiedbynumbers,andthatWindowspredefinesstandardmessages.Thewayofusingpredefinedmessagesistosimplyuseanumber.Tomakesurethatyoudon'tconflictwith
thesystemdefinedmessagesyoushoulduseanumberintherangeof
WM_APP
through0xBFFF:

#defineWM_DELETEALLWM_APP+0x100
//...
pYourDialog->SendMessage(WM_DELETEALL,0,0);

Handlingauser-definedmessageisdonewiththe
ON_MESSAGE
macro:

#defineWM_DELETEALLWM_APP+0x100
//...
//MessageMapentry:
ON_MESSAGE(WM_DELETEALL,OnDeleteAll)
//OnDeleteAllisprototypedas
afx_msgLRESULTOnDeleteAll(WPARAMwParam,LPARAMlParam);
//Andisimplementedas
LRESULTOnDeleteAll(WPARAMwParam,LPARAMlParam){
//DoWhateveryouwant
returnsomevalue;
}

RegisteredWindowsMessages

The
RegisterWindowMessage
functionisusedtodefineanewwindowmessagethatisguaranteedtobeuniquethroughoutthesystem.Themacro
ON_REGISTERED_MESSAGE

isusedtohandlethesemessages.Thismacroacceptsanameofa
UINT
variablethatcontainstheregisteredWindowsmessageID.Forexample:

classCMyWnd:publicCMyParentWndClass
{
public:
CMyWnd();

//{{AFX_MSG(CMyWnd)
afx_msgLRESULTOnFind(WPARAMwParam,LPARAMlParam);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()
};

staticUINTWM_FIND=RegisterWindowMessage("YOURAPP_FIND_MSG");

BEGIN_MESSAGE_MAP(CMyWnd,CMyParentWndClass)
//{{AFX_MSG_MAP(CMyWnd)
ON_REGISTERED_MESSAGE(WM_FIND,OnFind)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

Therangeofuserdefinedmessagesusingthisapproachwillbeintherange0xC000to0xFFFF.Andyousenditusingtheregular
SendMessage()
method:

staticUINTWM_FIND=RegisterWindowMessage("YOURAPP_FIND_MSG");
//...
pFindWindow->SendMessage(WM_FIND,lParam,wParam);


(三)

说明:这部分才是消息处理的底层部分,前面MFC只是在这部分之上包了一层,所以你在那层看不到消息处理的本质。

HandlingMessagesinSDKapplications

ThisarticleassumesyouarefamiliarwithcreatingawindowinanSDKprogram.TheDialogpartassumesyouarefamiliarwithcreatingmodalandmodelessdialoginaSDKprogram.

HandlingmessagesinSDKapplicationsisatotallydifferentprocessthanMFC.NoClassWizardormacrostohelpyou.No
CWinApp
toimplementtheMessageLoopforyou.It'salluptoyou.

WindowsClassesandWindowProcedures

Window"classes"intraditionalprogrammingforWindowsdefinethecharacteristicsofa"class"(notaC++class)fromwhichanynumberofwindowscanbecreated.Thiskindofclassisatemplateormodelforcreatingwindows.InWindows,everywindowhas
aWindowClassthatdefinestheattributesofawindowsuchasthewindow'sicon,thewindow'sbackgroundandthewindow'sprocedure.TocreateaWindowclass,youcall
RegisterClass
thataccepts
a
WNDCLASS
structuredefiningthepropertiesoftheWindowclass.Everywindowmusthaveawindowclass,sotypically,
RegisterClass
is
calledin
WinMain
.

Usually,theMessageLoopisimplementedasabasic
while
loop:

MSGmsg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
returnmsg.wParam;

The
MSG
structureisastructurethatholdsalltheinformationaboutthemessage:Thewindowitwassentto,themessageidentifier,the2
lParam
/
wParam

parametersthatcomewiththemessage,thetimeatwhichthemessagewassent,andthepositionofthemousewhenthemessagewassent.

Thecallto
GetMessage
tellswindowstoretrievethefirstmessageintheMessageQueue.IfthereisnomessageintheMessageQueue,
GetMessage

willnotreturnuntilthereis.Thereturnvaluefrom
GetMessage
dependsonthemessageitretrieved:Ifitwasa
WM_QUIT
message
itwillreturn
FALSE
,ifitwasn't,itwillreturn
TRUE
.The
TranslateMessage

functiontranslatesvirtual-keymessagesintocharactermessages.Thecharactermessagesarepostedtothecallingthread'sMessageQueue,tobereadthenexttimethethreadcallsthe
GetMessage

function.Forexample,ifyougeta
WM_KEYDOWN
message,
TranslateMessage
willadda
WM_CHAR

messagetoyourMessageQueue.Thisisveryusefulbecausethe
WM_KEYDOWN
willonlytellyouwhatkeyhasbeenpressed,notthecharacteritself.A
WM_KEYDOWN

for
VK_A
couldmean"a"or"A",dependingonthestateoftheCapsLockandShiftkey.
TranslateMessage
willdotheworkofchecking
ifitshouldbecapitalforyou.Thecallto
DispatchMessage
willcalltheWindowProcedureassociatedwiththewindowthatreceivedthemessage.That'stheSDKMessageLoopinanutshell.

AWindowProcedureisafunctioncalledbytheMessageLoop.Wheneveramessageissenttoawindow,theMessageLooplooksatthewindow'sWindowClassandcallstheWindowProcedurepassingthemessage'sinformation.AWindowProcedureisprototypedas:

LRESULTCALLBACKWindowProc(
HWNDhwnd,//handletowindow
UINTuMsg,//messageidentifier
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
);

The
HWND
isthehandletothewindowthatreceivedthemessage.Thisparameterisimportantsinceyoumightcreatemorethanonewindowusingthesamewindowclass.
uMsg

isthemessageidentifier,andthelast2parametersaretheparameterssentwiththemessage.

Typically,aWindowProcedureisimplementedasasetof
switch
statements,andacalltothedefaultwindowprocedure:

LRESULTCALLBACKWndProc(HWNDhwnd,UINTuMsg,
WPARAMwParam,LPARAMlParam){
switch(uMsg)
{
caseWM_CREATE:
//Dosomeinitialization,Playasoundorwhateveryouwant
return0;
caseWM_PAINT:
//HandletheWM_PAINTmessage
return0;
caseWM_DESTROY:
PostQuitMessage(0);
return0;
}
returnDefWindowProc(hwnd,message,wParam,lParam);
}

The
switch
-
case
blockinspectsthemessageidentifierpassedinthe
uMsg

parameterandrunsthecorrespondingmessagehandler.The
PostQuitMessage
callwillsenda
WM_QUIT
messagetotheMessageLoop,causing
GetMessage()

toreturn
FALSE
,andtheMessageLooptohalt.

DefWindowProc

AsIstatedinPart1,Windowsshouldhandleanymessageyoudon'thandle.Thecallto
DefWindowProc()
givesWindowsashotatthemessage.Somemessagessuch
as
WM_PAINT
and
WM_DESTROY
mustbehandledinyourWindowProcedure,andnotin
DefWindowProc
.

Puttingitalltogether:AllToGether.C(这个是核心的处理函数,入口函数)

#include<windows.h>
//DeclaretheWindowProcedure
LRESULTCALLBACKWndProc(HWND,UINT,WPARAM,LPARAM);

intWINAPIWinMain(
HINSTANCEhInstance,//handletocurrentinstance
HINSTANCEhPrevInstance,//handletopreviousinstance
LPSTRlpCmdLine,//pointertocommandline
intnCmdShow//showstateofwindow
){
staticTCHARlpszClassName[]=TEXT("AllTogether");
HWNDhwndMainWindow;
MSGmsg;
WNDCLASSwndclass;//窗口类,窗口显示的样式
//FillintheWindowclassdata
wndclass.cbClsExtra=0;
wndclass.cbWndExtra=0;
//Thedefaultwindowbackground
wndclass.hbrBackground=COLOR_WINDOW;
//Thesystem,IDC_ARROWcursor
wndclass.hCursor=LoadCursor(NULL,IDC_ARROW);
//ThesystemIDI_APPLICATIONicon
wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndclass.hInstance=hInstance;
wndclass.lpfnWndProc=WndProc;
//Thenameoftheclass,neededforCreateWindow
wndclass.lpszClassName=lpszClassName;
wndclass.lpszMenuName=NULL;
wndclass.style=CS_HREDRAW|CS_VREDRAW;

RegisterClass(&wndclass);//注册类,下面是创建窗体
hwndMainWindow=
CreateWindow(lpszClassName,//pointertoregisteredclassname
TEXT("LetsPutitalltogether"),//pointertowindowname
WS_OVERLAPPEDWINDOW,//windowstyle
CW_USEDEFAULT,CW_USEDEFAULT,//positionofwindow
CW_USEDEFAULT,CW_USEDEFAULT,//sizeofwindow
NULL,//handletoparentorownerwindow
NULL,//handletomenu
hInstance,//handletoapplicationinstance
NULL);//pointertowindow-creationdata
ShowWindow(hwnd,nCmdShow);
UpdateWindow(hwnd);

while(GetMessage(&msg,NULL,0,0))//消息循环
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
returnmsg.wParam;
}

LRESULTCALLBACKWndProc(HWNDhwnd,UINTuMsg,
WPARAMwParam,LPARAMlParam){
switch(uMsg)
{
caseWM_CREATE:
//Dosomeinitialization,Playasoundorwhateveryouwant
return0;
caseWM_PAINT:
//HandletheWM_PAINTmessage
return0;
caseWM_DESTROY:
PostQuitMessage(0);
return0;
}
returnDefWindowProc(hwnd,message,wParam,lParam);
}

SendingMessages

Besidesreceivingmessages,youwilloftenfindyourselfsendingmessages.Youmightwanttosendmessagestocommunicatebetweentwowindowsinyourprogram,ortocommunicatebetweendifferentprograms.Inordertosendamessage,youneedahandleto
thetargetwindow.Thiscanberetrievedusingavarietyoffunctions,including
FindWindow()
,
GetDlgItem()
,
GetParent()
,
EnumWindows()
andmanymore.TheSDKhasa
SendMessage()

functionwhichallowsyoutosendmessagestoawindow.Forexample,let'ssayyouhaveahandletotheCalculator,andyouwanttocloseit.Whatyoushoulddoissenda
WM_CLOSE
message,which
willnotifytheCalculatorthatitshouldclose.Youcanusethefollowingcode.InordertogetapointertoCalculator,Iusethe
FindWindow()
functionandpassthetitleofthewindow,which
inourcaseis"Calculator":

HWNDhWndCalc;
//Getahandletothe"Calculator"Window
hWndCalc=FindWindow(NULL,TEXT("Calculator));
if(hWndCalc==NULL)
{
//Couldn'tfindCalculator
}
else
{
SendMessage(hWndCalc,WM_CLOSE,0,0);
//Presto!TheCalculatorshouldclose.
}

LOWORDandHIWORDmacros:SplituplParamandwParam

Often,oneormoreofthe32-bit
lParam
and
wParam
parametersareactuallymadeoftwo16-bitparameters.Onecaseisthe
WM_MOUSEMOVE

message.MSDNstatesthatthe
lParam
forthismessageisactually2values:theXpositionofthemouse,andtheYpositionofthemouse.Buthowdoyouretrievethevaluesfromthe
lParam
?
TheSDKhas2macrosdesignedforexactlythispurpose:
LOWORD()
and
HIWORD()
.The
LOWORD
macroretrievesthelow-orderwordfromthegiven32-bitvalue,andthe
HIWORD()
macroretrievesthehigh-orderword.So,given
an
lParam
of
WM_MOUSEMOVE
,youcanretrievethecoordinatesusingthefollowingcode:

WORDxPos=LOWORD(lParam);//horizontalpositionofcursor
WORDyPos=HIWORD(lParam);//verticalpositionofcursor

MAKELPARAMandMAKEWPARAMmacros:concatenatetwo16-bitvalues

LOWORD
and
HIWORD
arefineifyouwanttosplituptheparameters,butwhatifyouwanttocreatea32-bitvalueforuseasan
lParam
or
wParam

parameterinamessage?TheSDKhas2macrosforthissituationalso:
MAKELPARAM
and
MAKEWPARAM
bothcombinetwo16-bitvaluesinto
a32-bitvalue,thatisusableformessages.Forexample,thefollowingcodesendsa
WM_MOUSEMOVE
messagetoawindow(
HWNDhWndTarget
)
withthe
fFlags
parameterasthe
wParam
,andthex/ycoordinatesasthe
lParam
:

SendMessage(hWndTarget,WM_MOUSEMOVE,fFlags,MAKELPARAM(x,y));

Dialogs

Handlingamessageinadialogisverysimilartohandlingamessageinanormalwindow.WindowshaveWindowProcedures,DialogshaveDialogProcedures.Onemajordifferenceisthatyoudon'tspecifyawindowclassforadialog.Whenyoucreateadialog
usingoneofthe
CreateDialog...
functionsorthe
DialogBox...
functions,youpassaDialogProcedureasoneoftheparameters.A
DialogProcedureisprototypedas:

BOOLCALLBACKDialogProc(
HWNDhwndDlg,//handletodialogbox
UINTuMsg,//message
WPARAMwParam,//firstmessageparameter
LPARAMlParam//secondmessageparameter
);

YoumighthavenoticedthattheDialogProcedurelooksverysimilartotheWindowProcedure,butitisn'tarealWindowProcedure.TheWindowProcedureforthedialogislocatedinsidewindows.ThatWindowProcedurecallsyourDialogProcedurewhenvarious
messagesaresenttoyourwindow.Becauseoftheabove,therearemessagesthatyouwillreceiveinaWindowProcedurethatyouwon'treceiveinaDialogProcedure.ThereareafewmajordifferencesbetweenaWindowProcedureandaDialogProcedure:

ADialogProcedurereturnsa
BOOL
,aWindowProcedurereturnsa
LRESULT
.
ADialogProceduredoesn'tneedtohandle
WM_PAINT
or
WM_DESTROY
.
ADialogProceduredoesn'treceivea
WM_CREATE
message,butrathera
WM_INITDIALOG
message
AWindowProcedurecalls
DefWindowProc()
formessagesitdoesnothandle.ADialogProcedureshouldreturn
TRUE
ifithandled
themessageor
FALSE
ifnotwithoneexception:ifyousettheinputfocustoacontrolin
WM_INITDIALOG
,youshouldreturn
FALSE
.

User-definedmessages

Sometimes,youwillneedtocommunicatebetween2windowsinyourapplicationorbetween2windowsfromdifferentapplications.AneasywaytodothisisbyusingUser-definedmessages.Thename"User-defined"canbeconfusingatfirst;youdefineaUser-defined
messageandnottheuserofyourprogram.IhavestatedinPart1thatmessagesareidentifiedbynumbers,andthatWindowspredefinesstandardmessages.Thewayofusinguser-definedmessagesistosimplyuseanumber.Tomakesurethatyoudon'tconflict
withthesystemdefinedmessages,youshoulduseanumberintherangeof
WM_APP
through0xBFFF:

#defineWM_DELETEALLWM_APP+0x100
//...
SendMessage(hWndYourDialog,WM_DELETEALL,0,0);

Youhandleauser-definedmessagejustlikeyouhandlearegularmessage:

#defineWM_DELETEALLWM_APP+0x100
//WindowProcedure
LRESULTCALLBACKWndProc(HWNDhwnd,UINTuMsg,WPARAMwParam,LPARAMlParam)
{
switch(uMsg)
{
caseWM_DELETEALL:
//We'vegottheuser-definedmessage,letsDeleteAll
return0;
caseWM_CREATE:
//Dosomeinitialization,Playasoundorwhateveryouwant
return0;
caseWM_PAINT:
//HandletheWM_PAINTmessage
return0;
caseWM_DESTROY:
PostQuitMessage(0);
return0;
}
returnDefWindowProc(hwnd,message,wParam,lParam);
}

RegisteredWindowsMessages

The
RegisterWindowMessage
functionisusedtodefineanewwindowmessagethatisguaranteedtobeuniquethroughoutthesystem.Likeuser-definedmessages,RegisteredMessagesarehandledlike
regularmessages:

staticUINTWM_FIND=RegisterWindowMessage(TEXT("YOURAPP_FIND_MSG");
//WindowProcedure
LRESULTCALLBACKWndProc(HWNDhwnd,UINTuMsg,WPARAMwParam,LPARAMlParam)
{
switch(uMsg)
{
caseWM_FIND:
//We'vegottheregisteredmessage,letsstartFinding.
return0;
caseWM_CREATE:
//Dosomeinitialization,Playasoundorwhateveryouwant
return0;
caseWM_PAINT:
//HandletheWM_PAINTmessage
return0;
caseWM_DESTROY:
PostQuitMessage(0);
return0;
}
returnDefWindowProc(hwnd,message,wParam,lParam);
}

Theregisteredmessageidentifiersusingthisapproachwillbeintherangeof0xC000to0xFFFF.Andyousenditusingtheregular
SendMessage()
method:

staticUINTWM_FIND=RegisterWindowMessage(TEXT("YOURAPP_FIND_MSG"));
//...
SendMessage(hWndFindWindow,WM_FIND,lParam,wParam);


(四)

这部分没研究,可以看看,原文:http://www.codeproject.com/Articles/600/Windows-Message-Handling-Part-4

Introduction

Subclassingisatechniquethatallowsanapplicationtointerceptandprocessmessagessentorpostedtoaparticularwindowbeforethewindowhasachancetoprocessthem.ThisistypicallydonebyreplacingtheWindowProcedureforawindowwithapplication-defined
windowprocedure.Iwilldevidethisarticleinto3:

Whysubclass?

Sometimesyouwanttotakethefunctionalityofacontrolandmodifyitslightly.Oneexampleisreplacingthemenuinaneditcontrol,Anotherisaddingacontextmenuwhenyoupressonabutton.OneofthemostcommonquestionsIencounteris"HowdoI
screenoutcharactersfromaneditcontrol?".IwillshowthesolutiontothisfromanMFCapproachandfromanSDKapproach,whileItrytoexplainSubclassing.

TheneedforsubclassingcomesfromthefactthatthecodefortheWindowscontrolsiswithinWindows,meaningyoucannoteditthecode.Althoughyoucannoteditthecodeofthecontrolitself,youinterceptthemessagessenttoit,andhandlethemyour
self.Youdosobysubclassingthecontrol.SubclassinginvolvesreplacingtheMessageHandlersofthecontrol,andpassinganyunprocessedmessagetothecontrolsMessageHandler.

SDKSubclassing

AlthoughtheMessageProcedureforthecontrolislocatedwithinwindows,youcanretrieveapointertoitbyusingthe
GetWindowLong
functionwiththe
GWL_WNDPROC

identifier.Likewise,youcancall
SetWindowLong
andspecifyanewWindowProcedureforthecontrol.ThisprocessiscalledSubclassing,andallowsyoutohookintoawindow/controlandintercept
anymessageitgets.SubclassingistheWindowstermforreplacingtheWindowProcedureofawindowwithadifferentWindowProcedureandcallingtheoldWindowProcedurefordefault(superclass)functionality.Remember
DefWindowProc()
?
insteadofcalling
DefWindowProc
fordefaultMessageHandlingyouusetheoldWindowProcedureasthedefaultMessageHandler.

ImplementingSDKSubclassing

So,letstrytosolvetheclassicquestion"HowdoIscreenoutcharactersfromaneditcontrol?",or"HowdoIcreatealetter-onlyeditcontrol?"

Firstletsanalyzehowaneditcontrolworks:

Aneditcontrolisawindow.It'swindowprocedurelieswithinwindows.Amongotherthings,wheneveritgetsa
WM_CHAR
messageitaddsthecharactertothetextitcontains.Nowthatweknow
that,wecansimplysubclasstheeditcontrol,andinterceptthe
WM_CHAR
messages.Wheneverthe
WM_CHAR
messageisaletterorakey
likespacebarorbackspacewe'llpassthemessagetotheeditcontrol.Ifitisn'toneoftheabove,we'lljust"Swallow"themessage,blockingitfromreachingtheEditControl.

Thefirststeptosubclassingistoaddaglobal/static
WNDPROC
variablethatwillstoretheaddressoftheeditcontrol'sWindowProcedure.


WNDPROCg_OldEdit;

ThesecondstepistocreateanewWindowProcedurefortheeditcontrol:


LRESULTCALLBACKNewEditProc(HWNDhwnd,UINTmessage,
WPARAMwParam,LPARAMlParam)
{

TCHARchCharCode;
switch(message)
{
caseWM_CHAR:
chCharCode=(TCHAR)wParam;
if(chCharCode>0x20&&!IsCharAlpha(chCharCode))
return0;
break;
}
returnCallWindowProc(g_OldEdit,hwnd,message,wParam,lParam);
}

The
IsCharAlpha
functiondetermineswhetheracharacterisanalphabeticcharacter.Thisdeterminationisbasedonthesemanticsofthelanguageselectedbytheuserduringsetuporbyusing
ControlPanel.

Moreinterestingisthe
CallWindowProc
function.The
CallWindowProc
functionpassesmessageinformationtothespecifiedWindow
Procedure.Acallto
CallWindowProc
willallowyoutocalltheoldWindowProcedurewithanymessageyoureceive,thusprovidingdefaultmessagehandling

ThethirdstepistoreplacetheWindowProcedurefortheeditcontrol,andtostoretheoldoneing_OldEdit.Forexample,ifyouwanttosubclassaneditcontrolthatresidesinadialog(hDlg)andhastheIDof
IDC_EDIT1

youwouldusethefollowingcode:


hwndhWndEdit=GetDlgItem(hDlg,IDC_EDIT1);<BR>//ReplacetheWindowProcedureandStoretheOldWindowProcedure
g_OldEdit=(WNDPROC)SetWindowLong(hWndEdit,GWL_WNDPROC,(LONG)NewEditProc);

Thecontrolisnowsubclassed.Anymessagetotheeditcontrolwillfirstgothroughthe
NewEditProc
WindowProcedure,whichwilldecideifthemessageshouldgototheeditcontrol'sWindow
Procedure.

MFCSubclassing

SubclassinginbothMFCandSDKprogramsisdonebyreplacingthemessagehandlersofacontrol.ItisrathereasytosubclassinaMFCprogram.Firstyouinherityourclassfromaclassthatencapsulatesthefunctionalityofathecontrol.InClassWizard,
clickon"AddClass",then"New".Forthebaseclass,choosetheMFCControlclassyouarederivingfrom,inourcase,
CEdit
.



UsingMFCrelievesyoufromhavingtocalltheoldMessageHandlers,sinceMFCwilltakecareofitforyou.

ThesecondstepistoaddMessageHandlerstoyournewclass.Ifyouhandleamessageandyouwantthecontrol'smessagehandlertogetashotatit,youshouldcallthebaseclassmemberfunctionthecorrespondswiththemessage.thisisthesubclassed
WM_CHAR

handler:

voidCLetterEdit::OnChar(UINTnChar,UINTnRepCnt,UINTnFlags)
{
if(nChar<=0x20||IsCharAlpha((TCHAR)nChar))<BR>{
//Callbaseclassmember,whichwillcall<BR>//thecontrol'smessagehandler
CEdit::OnChar(nChar,nRepCnt,nFlags);
//Ifnot,themessageisblocked<BR>}
}

Thethirdandfinalstageistoassociatethewindowwithaninstanceofournewclass.InadialogthisisdonesimplybyusingClassWizardtocreateacontrolmembervariableofyourclassinthewindow'sparent,andassociateitwiththe
control.



Inanon-dialogparentyoushouldaddamembervariableinstanceofyourcontroltoit,andcalloneofthetwo
CWnd
Subclassingfunctions:
CWnd::SubclassWindow
orCWnd::SubclassDlgItem
.Bothroutinesattacha
CWnd
objecttoanexistingWindows
HWND
.
SubclassWindow

takesthe
HWND
directly,and
SubclassDlgItem
isahelperthattakesacontrolIDandtheparentwindow(usuallyadialog).
SubclassDlgItem
is
designedforattachingC++objectstodialogcontrolscreatedfromadialogtemplate.

FurtherReading

ForamoreindepthtreatmentofMFCsubclassingseeChrisMaunder'sarticle:"Createyourowncontrols-theartofsubclassing".

ReflectedMessages-MFC4.0+

Whenyousubclassacontrol,besideshandlingthemessageitreceives,inMFCyoucanalsohandlethenotificationsitsendstoit'sparentwindow.ThistechniqueiscalledMessageReflecting.Windowscontrolsoftensendnotificationstotheirparents,for
example,aButtonwillsenda
WM_COMMAND
messagetellingit'sparentithasbeenclicked.Usuallyitistheparent'sjobtohandlethesemessages,butMFCwillalsoallowyoutohandlethemin
thecontrolitself.InClassWizardthesemessagesappearwithan"="signbeforethem,indicatingtheyareReflectedMessages.Youhandlethemjustlikeanyothermessage.Themacrosforthesemessagesaresimilartotheregularmessages,buthave
_REFLECT

addedtotheendofthemacro.Forexample,
ON_NOTIFY()
becomes
ON_NOTIFY_REFLECT()
.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: