Article source: blog.csdn.net/ybb_y1b1b1/…

For many people, VC development Windows interface program, generally based on MFC interface library, the current MFC is not the only VC interface library under Windows, but because of historical reasons, its users are absolutely the most. We are not going to study the MFC library, just the direct relationship between threads, interfaces, and messages in Windows.

 

What is a thread? A thread is a flow of execution that can be scheduled for execution by the CPU. To be more accurate, it should be said that it is scheduled by the operating system, because the computer hardware system only triggers an interrupt, and how to deal with the interrupt is the underlying processing of the operating system, and the interrupt of the time slice generally triggers the process switch of the operating system, so it should be said that it is scheduled by the operating system.

 

Through the above simple description of process scheduling, it can be found that processes and threads, which are scheduled by the operating system, are essentially data, including code data, data data, register data. If we fully understand the Windows storage mechanism, memory data meaning, in theory, we can directly modify the memory and the system to produce a process, but the practical work is too complicated.

 

Of course, some of the above narrative fuzzy place, such as repeatedly mentioned process, thread together, but essentially, thread process is a set of process of a vehicle, more like a set of meanings, it feels a bit like home and person’s relations, a home at least one person, each person is an individual, and you have a contact, belongs to a family, Have access to all the resources at home.

 

In this way, the actual body of execution in a process is a thread, and the process stack used to mean essentially a thread stack, with each thread having its own stack. The above content is easier to understand, operating system similar courses are also more.

 

 

So the next concept, what is a message in Windows?

I think this section is a bit stiff, mostly because a lot of things can only be documented rather than proven in practice, but we’ll try to make it clear.

 

To understand the premise, build a Win32 project, i.e., a Windows application project, not an MFC project. Once you’ve built it, you can see the core piece of code inside,

// Main message loop:

while (GetMessage(&msg, NULL, 0, 0))

{

if (! TranslateAccelerator(msg.hwnd, hAccelTable, &msg))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

It is used to get the message from the message queue and process the message.

 

Here are three questions:

1. Where is the message queue?

2. Where does the information come from?

3. How can the event corresponding to the message be triggered?

 

Here, the simplest is the third problem, because a considerable part of the code of this problem is completed by ourselves, the first two problems are completed with the system interaction, so it is not easy to analyze, then we start from the simple.

 

How can the event corresponding to the message be triggered to execute?

Generally speaking, the core of a Win32 program is the message processing function, generally established by default the following such a function.

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

 

It is a message processing function that needs to be registered. From the point of view of the code, it is subordinate to a certain or several threads. As can be seen from the parameters, the message processing function is independent of the window, and messages of multiple Windows can be processed by a message processing function.

 

The processing inside the message processing is relatively simple, just analyzing the message type, getting the message parameters, and then performing the actions in response to the corresponding window.

 

More importantly, how can the message function be called? Called by whom?

We add a breakpoint at the entry of the WndProc function. If this function is called, it indicates that message sending is working. When we run the program for the first time, we will find that the breakpoint stops with the following call stack:

del_.exe! WndProc(HWND__ * hWnd=0x001b1818, unsigned int message=0x00000024, unsigned int wParam=0x00000000, Long lParam=0x0012f734) line 143 C++

user32.dll! 77d18734()

[The following frame may be incorrect and/or missing, not loading symbols for user32.dll]

user32.dll! 77d18816()

user32.dll! 77d28ea0()

user32.dll! 77d2d08a()

ntdll.dll! 7c92e473()

user32.dll! 77d2e389()

user32.dll! 77d2e34f()

user32.dll! 77d2e442()

user32.dll! 77d2d0d6()

del_.exe! InitInstance(HINSTANCE__ * hInstance=0x00400000, int nCmdShow=0x00000001) line 111 + 0x31 bytes

This means that the first time the function is called, in InitInstance, the function creates the window statement that triggers the call.

hWnd = CreateWindow(/*szWindowClass*/_T(“xxxx”), szTitle, WS_OVERLAPPEDWINDOW,

      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

This means that during the creation of a form, the system will call the message handler function by default, which is equivalent to sending multiple processing messages. But this is just speculation, and if true, then there is a mechanism to skip the message loop and call the message handler directly. Windows provides two message-sending functions, SendMessage and PostMessage, so let’s test these two functions. If the message is sent after the form is created, and the message handler is called when the statement is executed, then the message is sent and does not enter the message queue at all. It’s called directly.

When tested, SendMessage calls the message handler directly, while PostMessage waits for the message queue loop to run before sending a message, a delayed call.

 

So the question now is how does SendMessage work? I guess PostMessage’s job is simply to put a message into a message queue, and the message queue loop will retrieve the message and then perform the call to the message handler function, which is the same as the call to the sendMessage handler function at the bottom, only the calling function we see is different.

 

In order to prove that I have the same implementation of SendMessage and DispatchMessage, I changed the message sending mode sendMessage to DispatchMessage after creating the form, and the result is consistent with my expectation, and can be directly called. But does that mean the underlying implementation is basically the same? There seems to be a problem, but we looked at another piece of evidence, which should make it more conclusive.

First look at the DispatchMessage call stack

WndProc(HWND__ * hWnd=0x00161896, unsigned int message=0x00000403, unsigned int wParam=0x00000000, Long lParam=0x00000000) line 150 C++

user32.dll! 77d18734()

[The following frame may be incorrect and/or missing, not loading symbols for user32.dll]

user32.dll! 77d18816()

user32.dll! 77d189cd()

user32.dll! 77d18a10()

del_.exe! InitInstance(

Then look at the sendMessage call stack

WndProc(HWND__ * hWnd=0x00161896, unsigned int message=0x00000401, unsigned int wParam=0x00000000, Long lParam=0x00000000) line 150 C++

user32.dll! 77d18734()

[The following frame may be incorrect and/or missing, not loading symbols for user32.dll]

user32.dll! 77d18816()

user32.dll! 77d2927b()

user32.dll! 77d292e3()

del_.exe! InitInstance(

 

What we found, three functions at the top of the stack pointer is the same, this means that the two types of messages last three layers of function call is the same, on the whole, called from the message is sent to the message handler is called the function call stack, there are only two function pointer is different, because the message is sent the function itself, So at least one function pointer must be different, that is, sendMessage and DispatchMessage are not implemented the same way in their bodies, but they all call the same message-handling functions.

 

Now that we know how to send a message, we wonder, what’s going on behind SendMessage so that the message handler can be called directly? If the storage a window type to the message handling function mapping relationship, so do direct call would be nothing strange, from the point of the code, we also registered a thing indeed, and processed form tag type and message processing function pointer, if we will form registration type and create the form of the form type, will find that the message is sent failed.

 

So our new question is, where is this mapping stored? Does it belong to the thread? Or is it a process? I think this piece of data belongs to the process and is in the public space. How can I prove it? If it is thread private, thread A registers A message handler and thread B could theoretically register another message handler for the same window type, but the test fails, meaning that the data block is global to the process, and only one message handler can be registered for A window type.

 

Now, does this mean that thread A registers the message handler and creates the form, and function B calls the message handler registered in thread A directly via SendMessage? The answer is no. I ran the following tests,

1. Thread A registers message processing functions, thread A creates forms, and thread B sends messages;

2. Thread A registers message processing functions, thread B creates forms, and thread A sends messages.

3. Thread A registers message processing functions, thread B creates forms, and thread B sends messages.

The above test did not register the A thread handler, then create the form, then send the message, because this must be possible, it is usually done. Create a window for the above three tests, 1, 2 found the message failed, 3 succeeded, from another point of view support register message handler function is global, but why 1, 2 send message failed? I think the reason is that the form belongs to thread-private data. For example, in test 1, when A created the form, A had the right to manage the form data, while thread B did not obtain the form type information, so it could not know the address of the form’s message handling function. But not being able to get a form type through a form handle always feels wrong. Really? Or is it another reason that thread B fails to send messages?

To verify the above idea, I need to do a test using the existing API to see if I can successfully send a message from another thread to another thread’s form. Currently, PostMessage does not work. Finally, my code looks like this:

TCHAR className[300];

GetClassName(hWndX,className,300);

 

WNDCLASSEXW exw;

if(GetClassInfoEx(hInstXX,className,&exw))

{

Exw. LpfnWndProc (hWndX, WM_USER + 0, 5);

}

 

First through forms to obtain form type names, and then through the type name for registration form type information, type information in the form of a registration message processing pointer fields, I direct calls to this function, the result is very good, indeed the call is successful, if I will write this code package to a function, such as SendMessageBB, That should look like a successful message sent from the call, right?

 

What is the conclusion? Sending messages from one thread to another should be technically fine, but Windows does not support this, so there must be some reason for not knowing, which I don’t know how to analyze.

 

 

 

Understand message loops?

Windows programs are based on messaging, no doubt, but the message loop doesn’t have to be in one place. You can add message loop processing to any code. Let’s take a look at how to use this mechanism to implement modal and modeless dialogs.

Modeless dialog box is when this dialog box pops up, you can continue to operate other dialog boxes;

When a modal dialog box pops up, it cannot continue to operate other dialogs.

 

Obviously, a modal dialog box is a message loop that processes messages from multiple dialogs simultaneously, and a modal dialog box is a message loop that only processes messages related to the form after a dialog box is created.

 

In another case, writing message loops is more valuable. When an intensive computation is executed, how to display the progress, get the starting thread, one for the computation, one for the interface display, one thread is ok? Yes, as long as the message loop processing code is inserted at the right place in the intensive computation, the interface can respond to the computation as well.

\