First let's take a look at how the WTL is packaged in the application thread.
Similar to the ATL, WTL uses a _Module global variable to save global data, and reference the application level code. In WTL, this variable is an instance of CAPPMODULE or CSERVERAPPMODULE. The latter is usually used as an application of COM servers.
Each application has one or more interface threads. First analysis, how WTL is managed (except MUTLI-SDI applications).
Single interface thread package
First look at the application's entry function.
CAPPModule_Module;
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
// com in A Random RPC thread.
// HRESULT HRES = :: CoinitializeEx (null, coinit_multithread);
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
// Initialization module
HRES = _Module.init (null, hinstance);
Atlassert ("succeeded (hres));
// Program Logic, call global function run ()
Int nret = Run (LPSTRCMDLINE, NCMDSHOW);
// Termination Module
_Module.Term ();
:: Couninitialize ();
Return nret;
}
In the code above, _Module is a global variable, here is an instance of CAPPModule. The CappModule class is packaged for applications. It encapsulates functions such as initialization modules. A _Module also maintains a message loop MAP.
The entrance function name is _twinmain (). When using Unicode, the compiler will replace it to wwinmain (), otherwise, for WinMain (). The entrance function is actually the starting point of the main thread (_Module).
In this function, the most critical logic is called a full-class function run (), which is the core program logic.
Let's take a look at this function.
INT Run (LPTSTR / * LPSTRCMDLINE * / = null, int ncmdshow = sw_showdefault)
{
CMessageloop theloop;
_Module.AddMessageloop (& Theloop);
CMAINFRAME WNDMAIN;
IF (WNDMAIN.CREATEX () == NULL)
{
ATLTRACE (_T ("Main Window Creation Failed! / N")));
Return 0;
}
WNDMAIN.SHOWINDOW (NCMDSHOW);
INT nret = theloop.run ();
_Module.RemoveMessageloop ();
Return nret;
}
This function creates a CMessageLoop instance that contains the message loop of this thread. These messages loops are placed in the global message loop of the module, and the ID of the thread is indexed. In this way, other code of the thread can be accessed.
Each application maintains a message loop queue MAP, each thread in the application passes "_Module.AddMessageloop (& TheLoop), and adds the message loop to _Module's message loop MAP.
The message loop object contains message filtering and idle processing. Each thread can be added to the idle processing code and message filtering.
We will explore the message loop in more detail later. Here, you only need to know that the thread is packaged by creating a message loop object CMessageLoop.
Multiple interface thread packages
Then look at it, when an application has multiple interface threads, how WTL manages these threads.
Typically a MUTLI-SDI application contains multiple interface threads. IE IE browser is the application. Each main window is run in a separate thread, and each such thread has a message processing.
WTL App Wizard is implemented by creating an instance of a Thread Manager class for the primary thread.
Class CthreadManager {
PUBLIC:
// Thread Init Param
Struct _Rundata
{
LPTSTR LPSTRCMDLINE;
Int ncmdshow;
}
Static DWORD WINAPI RUNTHREAD (LPVOID LPDATA);
DWORD M_DWCOUNT; // Count of Threads
Handle M_ARRTHREADHANDLES [Maximum_Wait_Objects - 1];
CthreadManager (): m_dwcount (0) {}
DWORD AddThread (LPTSTSTSTR LPSTRCMDLINE, INT NCMDSHOW);
Void RemoveThread (DWORD DWIndex);
INT Run (LPTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTRCMDLINE, INT NCMDSHOW);
}
In this class, M_ARRTHREADHANDLES [maximum_wait_objects-1] is an array of handle of the running thread. The m_dwcount is the count value of how much threads currently.
Addthread () and RemoveThread () two functions are used to increase or delete threads from the array of storage threads.
Runthread () is the logic of threads. Its main job is to create a message queue and add it to the message queue MAP of the primary thread (_Module). It also creates the main window of the thread.
The logic of this function is similar to the global function Run () described above. In fact, it should be conceivable. Because the front _module is the main thread of the application.
Below is the code of the function:
Static DWORD WINAPI RUNTHREAD (LPVOID LPDATA)
{
CMessageloop theloop;
_Module.AddMessageloop (& Theloop);
_Rundata * pdata = (_Rundata *) LPDATA;
CMAINFRAME WNDFRAME;
IF (WNDFrame.createex () == NULL)
{
ATLTRACE ("Frame Window Creation Failed! / N")))))
Return 0;
}
Wndframe.showwindow ;: setForegroundWindow (WNDFRAME); // Win95 Needs this
DELETE PDATA;
INT nret = theloop.run ();
_Module.RemoveMessageloop ();
Return nret;
}
When you create a thread using the AddThread () function, you create a thread of a RUNTHREAD ().
The following is the code of addthread ().
DWord AddThread (LPTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTRCMDLINE, INT NCMDSHOW)
{
IF (m_dwcount == (Maximum_Wait_Objects - 1))
{
:: Messagebox (Null, _T ("Error: Cannot Create Any More Threads !!!"),
_T ("test2"), MB_OK);
Return 0;
}
_Rundata * pdata = new _rundata;
PDATA-> LPSTRCMDLINE = lpstrcmdline;
PDATA-> ncmdshow = ncmdshow;
DWORD DWTHREADID;
Handle hthread = :: CreateThread (Null, 0, Runthread, PDATA, 0,
& dwthreadid);
IF (hthread == null)
{
:: Messagebox (Null, _T ("Error: Cannot Create Thread !!!), _T (" Application "), MB_OK;
Return 0;
}
M_ARRTHREADHANDLES [M_DWCOUNT] = HTHREAD;
m_dwcount ;
Return DwthreadId;
}
In the above code:
Handle hthread = :: CreateThread (NULL, 0, RUNTHREAD, PDATA, 0, & DWTHREADID);
In the middle, Runthread is passed to CreateThread () as a parameter.
So how do the app use the CTHREADMANAGER class to manage multiple threads?
Let's take a look at the logic of the entry function (main thread) of the application:
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 on ON
// a random rpc thread.
// HRESULT HRES = :: CoinitializeEx (null, coinit_multithread);
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);
Atlassert ("succeeded (hres));
INT nret = 0;
// Block: Run Application
// Note the difference between the site and the previous application.
// Create an instance of a CTHReadManager class here, then call the Run () function of the class.
{
CthreadManager Mgr;
NRET = mgr.run (lpstrcmdline, ncmdshow);
}
_Module.Term ();
:: Couninitialize ();
Return nret;
}
In this entry function (main thread), an instance of a CTHReadManager class is created. Then call the RUN () function of the class.
Look at Run () what is done.
int Run (LPTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTSTRCMDLINE, INT NCMDSHOW)
{
MSG msg;
// force message queue to be created
:: PeekMessage (& MSG, NULL, WM_USER, WM_USER, PM_NOREMOVE);
// Create an interface thread. The first interface thread for the application. The main body of this interface thread
// Logic Runthread () is a master window and create a message loop object.
AddThread (LPSTRCMDLINE, NCMDSHOW);
INT nret = m_dwcount;
DWORD DWRET;
While (m_dwcount> 0)
{
dwret = :: msgwaitformultipleObjects (m_dwcount,
M_ARRTHREADHANDLES, FALSE, INFINITE, QS_ALLINPUT;
IF (dwret == 0xffffff)
:: Messagebox (Null,
_T ("Error: Wait for Multiple Objects FaiLed !!!"),
_T ("test2"), MB_OK);
Else IF (dwret> = WAIT_OBJECT_0 &&
DWRET <= (Wait_Object_0 M_dwcount - 1)))
RemoveThread (dwret - wait_object_0);
Else if (dwret == (wait_Object_0 m_dwcount))
{
:: GetMessage (& MSG, NULL, 0, 0);
IF (msg.Message == WM_USER)
Addthread ("", sw_shownormal);
Else
:: MessageBeep ((uint) -1);
}
Else
:: MessageBeep ((uint) -1);
}
Return nret;
}
This function first creates an interface thread, which is the first interface thread of this application. Specify the main logic of RunThread () as thread by calling addthread (). Its main task is to create a frame window and then create a message loop object and add the object to the message loop MAP of the main thread _Module.
Subsequently, the function calls MsgWaitFormultiPleObjects (), enters the waiting state (in Infinite as a time parameter). There are three situations that can be returned. The "Wait for Multiple Objects FaiLed" is an error condition.
• One is the end of a query. The removethread () is called to delete the thread handle.
• The last one is a thread to receive the WM_USER message. At this point, call AddThread () Create another interface thread.
The above logic has been running until all interface threads end.
Now, do you have a certain understanding of how to encapsulate the Windows interface program? If so, we will discuss the WTL message loop.