Author: Jiang Jiang QQ: 457283E-mail: jznsmail@163.netATLAPP.H contains the message loop class, some basic class definition interface classes, and generate the necessary application.
The class is defined as follows:
CMessageFilter class --- for message filtering
CIDEHANDLER class --- for free message processing
CMessageloop class --- for message loop
CAPPModule Class --- Application Basic Class
CserveRAppModule class --- Application class for COM service architecture
There are also three full-class functions:
ATLGETDEFAULTGUIFONT () Get the default display font
ATLCREATEBOLDFONT () generates a bold font
AtlinitcommonControls () Initialize the common DLL you need to control some controls
WTL program structure
The creation of a window program to the destruction process is mainly due to the following stages.
1. Registration window class
2. Create a window
3. Enter the message loop
If a person written by Cezen Win32 window program will remember the following structure:
// Window process handler
LResult Callback WndProc (HWND HWND, UINT MESSAGE, WPARAM WPARAM, LPARAM LPARAM);
Int WinApi WinMain (Hinstance Hinstance, Hinstance Hprevinstance, LPSTR SZCMDLINE, INT ICMDSHOW)
{
HWND HWND = NULL;
MSG msg;
...
WNDCLASS WNDCLASS;
WNDCLASS.Style = CS_HREDRAW | CS_VREDRAW;
WNDCLASS.LPFNWNDPROC = WNDPROC;
...
// Register window
IF (! registerclass (& wndclass))
{
MessageBox (Null, Text ("Porgram Requires Windows NT!"), SZAPPNAME, MB_ICONERROR);
Return 0;
}
// Create a window
HWND = CREATEWINDOW (Szappname, Text ("My Application"),
WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, HINSTANCE, NULL;
ShowWindow (hwnd, icmdshow);
UpdateWindow (HWND);
// Enter the message loop
While (GetMessage (& MSG, NULL, 0, 0))
{
TranslateMessage (& MSG);
DispatchMessage (& MSG);
}
Return msg.wparam;
}
Then where you may ask the WTL's Winmain function? If you guide an application via the WTL / ATL, you will find the following code in the .cpp file with the same name as the project name:
Int WinApi _twinmain (Hinstance Hinstance, Hinstance / * Hprevinstance * /, LPTSTR LPSTRCMDLINE, INT NCMDSHOW)
{
HRESULT HRES = :: Coinitialize (NULL);
// if you are running on nt 4.0 or higher you can use the following call instead to
// Make The Exe Free Threaded. This Means That Calls Come in A Random Rpc thread.// HRESULT HRES = :: CoinitializeEx (NULL, COINIT_MULTITHREADED);
Atlassert ("succeeded (hres));
// this Resolves ATL WINDOW THUNKING Problem When Microsoft Layer For Unicode (MSLU) IS Used
:: DefWindowProc (NULL, 0, 0, 0L);
AtlinitcommonControls (ICC_COOL_CLASS | ICC_BAR_CLASSES); // Add Flags to Support Other Controls
HRES = _Module.init (NULL, HINSTANCE); //, analyzes its implementation atlassert (succeeded (hres));
INT nret = run (lpstrcmdline, ncmdshow); // key points for programs
_Module.Term ();
:: Couninitialize ();
Return nret;
}
From this _twinmain function, you can find that the key part of the program is the Run () function that I am labeled in my purple. This function is a custom function, but if you automatically generate such a Run () function, let's first analyze this automatically generated RUN function.
INT Run (LPTSTR / * LPSTRCMDLINE * / = null, int ncmdshow = sw_showdefault)
{
CMessageloop theloop; // Define message loop
_Module.AddMessageLoop (& TheLoop); // Add messages to message loop
CMAINFRAME WNDMAIN; // Application Framework
// Generate a framework
IF (WNDMAIN.CREATEX () == NULL)
{
ATLTRACE (_T ("Main Window Creation Failed! / N")));
Return 0;
}
// Display framework
WNDMAIN.SHOWINDOW (NCMDSHOW);
// Run the message loop
INT nret = theloop.run ();
// Clear the message
_Module.RemoveMessageloop ();
Return nret;
}
Through this Run function we can see the following procedures in the function:
1. Generate a message loop object (theloop)
2. Add this message loop in the global _module
3. Generate an application framework object
4. Display application framework
5. Start message loop
6. End message loop
7. Return to WinMain functions, end the program
Implementation analysis
In this article I don't want too much to analyze the details of the application framework and window, which will be placed in detail in several articles, this paper mainly analyzes some of the processes implemented in atlapp.h header files.
First start from the global variable _module.
_Module maintains the main thread that generates the application, controls the message loop queue of the program, is an object of a CappModule. This cappmodule inherits from atl :: ccommodule.
In WTL :: CappModule, 8 public member functions are defined, respectively:
AddMessageloop () Add a message loop to enter the message loop queue. RemoveMessageLoop () Removes the message loop queue.
GetMessageloop () gets the message loop.
INITSETTINGENGENOTIFY () initialization environment
AddSettingChangeNotify () Add a window handle.
RemoveSettingChangeNotify () Clean up the environment
In addition to eight public member functions, this class also defines three public member variables.
m_dwmainthreadid is responsible for saving the main thread ID of the application
M_PMSGLOOPMAP is responsible for the storage message loop
M_PSettingChangeNotify is responsible for the storage window handle
The implementation of several major member functions is analyzed separately:
Bool AddMessageloop (CMessageloop * PMSGLOOP)
{
CStaticDataInitCriticalSECTIONLOCK LOCK;
/ / Lock the key pieces, due to the relationship of the process synchronization! ! !
IF (filed (Lock.lock ())))
{
Atltrace2 (ATLTraceui, 0, _T ("Error: Unable to lock critical section in capmodule :: addMESSAGELOOP./N"));
Atlassert (false);
Return False;
}
Atlassert (PMSGLOOP! = NULL);
Atlassert (m_pmsgloopmap-> lookup (:: getcurrentthreadid ()) == null); // not in map yet
Bool Bret = m_pmsgloopmap-> add (: getcurrentthreadid (), pmsgloop;
Lock.unlock ();
Return Bret;
}
The key part I use the red font to marke out, what is it? Sign up by the ID of the current thread to indicate a message loop, stored in m_pmsgloopmap.
Bool removemessageloop ()
{
CStaticDataInitCriticalSECTIONLOCK LOCK;
IF (filed (Lock.lock ())))
{
ATLTRACE2 (Atltraceui, 0, _T ("Error: Unable to lock critical section in capmodule :: removemedageloop./n"));
Atlassert (false);
Return False;
}
Bool Bret = m_pmsgloopmap-> remove (:: getCurrentThreadId ());
Lock.unlock ();
Return Bret;
}
The key part also marks the red font, um, if you are just like the addMessageloop function, the function is also looking for a message loop to remove the message through the thread ID.
CMessageloop * getMessageloop (DWORD DWTHREADID = :: getcurrentthreadid ()) Const
{
CStaticDataInitCriticalSECTIONLOCK LOCK;
IF (filed (Lock.lock ())))
{
ATLTRACE2 (Atltraceui, 0, _T ("Error: Unable to lock critical section in capmodule :: getMessageloop./n")));
Atlassert (false);
Return NULL;
}
CMessageloop * ploop = m_pmsgloopmap-> lookup (dwthreadid); lock.unlock (); LOCK.UNLOCK
Return plop;
}
This function looks for a corresponding message loop through the thread ID in the m_pmsgloopmap message queue, and then finds it back.
Bool INITSETTINGENGENGENTIFY (DLGPROC PFNDLGPROC = _SETTINGCHANGPROC)
{
CStaticDataInitCriticalSECTIONLOCK LOCK;
IF (filed (Lock.lock ())))
{
ATLTRACE2 (Atltraceui, 0, _T ("Error: Unable to lock critical section in capmodule :: initsettingchange./N"))
Atlassert (false);
Return False;
}
IF (m_psettingchangenotify == null)
{
Typedef atl :: CsimpleArray
ATLTRY (m_psettingchangenotify = new _notifyclass);
Atlassert (m_psettingchangenotify! = Null);
}
BOOL BRET = (m_psettingchangenotify! = Null);
IF (Bret && M_PsettingChangeNotify-> getSize () == 0)
{
// init everything
_ATL_EMPTY_DLGTEMPLATE TEMPL;
// Add a modular dialog
HWND HNTFWND = :: Createdialogindirect (getModuleInstance (), & Templ, NULL, PFNDLGPROC;
Atlassert (:: iswindow (hnts);
IF (: iswindow (hntfwnd))
{
// NEED CONDITIONAL CODE BECAUSE TYPES DON '' Match in Winuser.h
#ifdef_win64
:: SetWindowlongPtr (Hntfwnd, GWLP_USERDATA, (long_ptr) this);
#ELSE
:: SetWindowlongptr (Hntfwnd, GWLP_USERDATA, PTRTOLONG (THIS));
#ENDIF
/ / Add to this window handle
Bret = m_psettingchangenotify-> add (hntfwnd);
}
Else
{
Bret = false;
}
}
Lock.unlock ();
Return Bret;
}
This function is used to initialize an object that stores the window handle.
Bool INITSETTINGENGENGENTIFY (DLGPROC PFNDLGPROC = _SETTINGCHANGPROC)
{
CStaticDataInitCriticalSECTIONLOCK LOCK;
IF (filed (Lock.lock ())))
{
ATLTRACE2 (Atltraceui, 0, _T ("Error: Unable to lock critical section in capmodule :: initsettingchange./N"))
Atlassert (false);
Return False;
}
IF (m_psettingchangenotify == null) {
Typedef atl :: CsimpleArray
ATLTRY (m_psettingchangenotify = new _notifyclass);
Atlassert (m_psettingchangenotify! = Null);
}
BOOL BRET = (m_psettingchangenotify! = Null);
IF (Bret && M_PsettingChangeNotify-> getSize () == 0)
{
// init everything
//? ? Empty ATL DIALOG TEMPLATE?
_ATL_EMPTY_DLGTEMPLATE TEMPL;
// Add a modular dialog
HWND HNTFWND = :: Createdialogindirect (getModuleInstance (), & Templ, NULL, PFNDLGPROC;
Atlassert (:: iswindow (hnts);
IF (: iswindow (hntfwnd))
{
// NEED CONDITIONAL CODE BECAUSE TYPES DON '' Match in Winuser.h
#ifdef_win64
:: SetWindowlongPtr (Hntfwnd, GWLP_USERDATA, (long_ptr) this);
#ELSE
:: SetWindowlongptr (Hntfwnd, GWLP_USERDATA, PTRTOLONG (THIS));
#ENDIF
Bret = m_psettingchangenotify-> add (hntfwnd);
}
Else
{
Bret = false;
}
}
Lock.unlock ();
Return Bret;
}
// Clean the message
Void TermSettingChangeNotify ()
{
CStaticDataInitCriticalSECTIONLOCK LOCK;
IF (filed (Lock.lock ())))
{
ATLTRACE2 (Atltraceui, 0, _T ("Error: Unable to lock critical section in capmodule :: termsettingchangenotify./N"))
Atlassert (false);
Return;
}
IF (m_psettingchangenotify! = null && m_psettingchangenotify-> getSize ()> 0)
// Destroy the window
:: DestroyWindow ((* m_psettingchangenotify) [0]);
Delete m_psettingchangenotify;
M_PSettingChangenotify = NULL;
Lock.unlock ();
}
Bool AddsettingChangeNotify (HWND HWND)
{
CStaticDataInitCriticalSECTIONLOCK LOCK;
IF (filed (Lock.lock ())))
{
ATLTRACE2 (Atltraceui, 0, _T ("Error: Unable to lock critical section in capmodule :: addsettingchangenotify./n"));
Atlassert (false);
Return False;
}
Atlassert (:: iswindow (hwnd); bool bret = false;
IF (INITSETTINGENGENGENOTIFY ()! = false)
Bret = m_psettingchangenotify-> add (hwnd);
Lock.unlock ();
Return Bret;
}
Bool RemoveSettingChangeNotify (HWND HWND)
{
CStaticDataInitCriticalSECTIONLOCK LOCK;
IF (filed (Lock.lock ())))
{
ATLTRACE2 (Atltraceui, 0, _T ("Error: Unable to lock critical section in capmodule :: removesettingchangenotify./n"));
Atlassert (false);
Return False;
}
BOOL BRET = FALSE;
IF (m_psettingchangenotify! = null)
Bret = m_psettingchangenotify-> remove (hwnd);
Lock.unlock ();
Return Bret;
}
Now returns to the Run () function that just mentioned, the first place defines a CMessageLoop loop object, then join the _Module object Add to the loop queue until _Module calls the REMOVEMESSAGELOOP removal cycle queue, program End the loop and return to the WinMain function.
There is also a more important class here, that is, cMessageloop, is the message he maintaining the system, maintaining the life cycle of the program. Then let's take a look at the definitions and specific implementation methods of this class.
CMessageLoop contains some member functions and member variables
Member variables
// Processing a message
Atl :: csimpleArray
// Process the free handle
Atl :: CSIMPLEARRAY
// Win32API message structure
MSG M_MSG;
Member function (functions with red tags is virtual function)
AddMessageFilter Add a message filtering
RemoveMessageFilter removes a message filtering
AddIdleHandler joins an free handle
RemoveidleHandler removes an free handle
Addupdateui designs for compatible ATL
RemoveUpdateui designs for compatible ATL
IsidleMessage filtering some messages such as WM_MOUSEMOVE
RUN message loop. Important section! ! !
PretranslateMessage Message Filter
OnIDLE idle processing
Then I don't prepare a detailed analysis of each function, mainly analyze the core functions run, CMessageLoop by it to maintain the system's message loop.
The function is as follows:
Int run ()
{
//idle?
Bool bdoidle = true;
// idle counter
INT NIDECOUNT = 0;
// Return to the logo
BOOL BRET;
// Start the message loop! ! !
For (;;)
{
// When bdoidle is True, and cannot take the message from the message queue, then start free operation! // pm_noremove: Remove the message from the queue after the PEEKMESSAGE function is processed
While (BDOIDLE &&! :: PeekMessage (& M_MSG, NULL, 0, 0, PM_NOREMOVE))
{
IF (! OnIdle (NidleCount ))
BDOIDLE = FALSE;
}
// Get a message from the current thread
// Return -1 indicates an error
// Returns 0 indicates that a WM_QUIT is submitted, and the program will exit.
// Successfully get a message, return a value equal to 0
Bret = :: getMessage (& M_MSG, NULL, 0, 0);
IF (BRET == -1)
{
Atltrace2 (Atltraceui, 0, _t (":: getMessage returned -1 (error) / n"));
Continue; // Error, Don't Process
}
Else if (! BRET)
{
ATLTRACE2 (Atltraceui, 0, _T ("cMessageloop :: run - exiting / n"));
Break; // wm_quit, EXIT Message Loop
}
// If you are familiar with the use of C language to write Win32 programmer, it is found that the statement that processes the message loop in WinMain is here! ! !
IF (! PretranslateMessage)
{
// Translates Virtual-Key Messages Into Character Messages.
:: TranslateMessage (& M_MSG);
// dispatches a message to a window procedure
:: DispatchMessage (& M_MSG);
}
/ / Judgment whether it is an idle message?
/ / Exclusions WM_MOUSEMOVE WM_NCMOUSEMOVE WM_SYSTIMER message
IF (ISIDEMESSAGE (& M_MSG))
{
BDOIDLE = True;
Nidlecount = 0;
}
}
Return (int) m_msg.wparam;
}
The above is the analysis of several important classes in Atlapp.h, and there are several other classes. I will put in future articles.
(to be continued...)