Win32 learning notes (16) message type

Posted by mr_zhang on Thu, 06 Jan 2022 14:48:48 +0100

1. Message generation and processing flow:

As shown in the figure above, we introduced this figure in detail in the window creation program. For example, when we click a window, a message will be generated. The operating system will first judge which window the message is on. After finding the window, it will find out which thread the window belongs to according to a member of the window object. Once found, the encapsulated message will be put into a structure and stored in the message queue, and our GetMessage() will keep taking out the messages in the message queue.

In the code we created the window program before (I intercepted it)

MSG msg;
GetMessage(&msg, NULL, 0, 0)

We know that the extracted message is put in MSG Let's go to the definition and look at MSG

Note: there are four situations that can generate messages: 1 Mouse 2 Keyboard 3 Other applications 4 Kernel program of operating system

So how does the operating system handle so many messages? The operating system will divide them into different categories and give them a unique number for each different message. The parameters are described below

UINT        message
 This parameter is UINT Type, which gives each message type a number. For example, if I click the mouse, there will be a unique type number.
HWND        hwnd
 Handle. Messages are window based. Any message is a message of a window.
WPARAM      wParam;
LPARAM      lParam;
These two members describe what your message is like
DWORD       time
 When did the message come into being
POINT       pt
 Where did your message come from

So why don't we take out the message and put it in MSG and process it directly? In fact, we also mentioned it in our previous study. Now let's take a look at MSG. We can only know which window and what type it is. What we get is only an index. We don't know what the window callback is, that is, the window processing function, so we pass the obtained message to DispatchMessage(). It takes the message and HWND and enters the kernel. The kernel will find the window object according to the handle, find the window processing function, and then the kernel initiates the call to execute the window process (processing) function.

After the above introduction, we now know where the four parameters in the message processing function come from.

These are the four parameters of the window procedure 
    _In_ HWND   hwnd,
    _In_ UINT   uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam

We get the message through GetMessage. These four parameters are all in MSG, and then send MSG (message) to the kernel through DispatchMessage(). The kernel will pass these four parameters back when calling the window process (see the top figure for details) Therefore, the four parameters in the window procedure function are the four members in MSG.

Then we try to output the message type (the second parameter in MSG), then we can output uMSG in WindowProc. The example code is as follows

#include "framework.h"
#include "WindowsProject1.h"
LRESULT CALLBACK WindowProc(
    _In_ HWND   hwnd,
    _In_ UINT   uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
)
{
    char szOutBuff[0x80];
    sprintf(szOutBuff, "Message type: %x \n", uMsg);
    OutputDebugStringA(szOutBuff);
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    char szOutBuff[0x80];
    //1. Step 1: define what your window looks like
    TCHAR className[] = TEXT("My First Window");
    WNDCLASS wndclass = { 0 };
    wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
    wndclass.lpszClassName = className;
    wndclass.hInstance = hInstance;
    wndclass.lpfnWndProc = WindowProc;//Instead of calling the function, just tell the function name and the operating system will call it
    RegisterClass(&wndclass);
    //Part II: create and display windows
    HWND hwnd = CreateWindow(
        className,
        TEXT("My first window"),
        WS_OVERLAPPEDWINDOW,
        10,
        10,
        600,
        300,
        NULL,
        NULL,
        hInstance,
        NULL
    );
    if (hwnd == NULL)
    {
        sprintf(szOutBuff, "Error:%d", GetLastError());
        OutputDebugStringA(szOutBuff);
        return 0;
    }
    ShowWindow(hwnd, SW_SHOW);
    //3. Step 3: receive the message and process it
    MSG msg;
    BOOL bRet;
​
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (bRet == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            //Conversion message
            //TranslateMessage(&msg);
            DispatchMessage(&msg);//Distribute messages
        }
    }
    return 0;
}

We see the output through Debugview + +

We can see that there are many messages (there are many without screenshots). Don't think that there are messages only when the mouse or keyboard is clicked. There are messages when our kernel program is executed. We can check these message types specifically, for example, the message types above: 1. Let's turn MSG to the definition in vs2019 (I use 2019) to see if it is a number

We'll look for the documents in it

Yes, so what does this message type mean? We use official documents.

WM_CREATE Message:
When an application requests a CreateWindowEx or CreateWindow Function is sent when a window is created. (send a message before the function returns. The window procedure for a new window receives this message after the window is created, but before the window becomes visible.
​
Window through its WindowProc Function to receive this message.

In short, the operating system generates a message when our window is created. This message type is WM_CREATE. There are many message types. These message types correspond to a code. We can use them when we need them, and ignore them when we don't need them.

For example, when the window is maximized, there will be a message type if we want to maximize
 You can judge by what you do when you generate this value.

In a word, we should know that messages are generated from time to time, mouse, keyboard, other programs, etc.

However, there are some important message types that we should know and make use of. For example, when we execute the previous code, closing the window and executing it again may report an error.

You may have encountered this error when writing the program before. The reason is that the program you opened before is not really closed. We need to terminate the program in the task manager. We also found that when we execute the code given above, we can still find the program in the task manager after closing the window.

So the window is gone and the program is still running. So how to solve it? We can use the message type to capture the message of closing the window, judge whether the message is generated, and then terminate the program through code.

We know that each message type corresponds to a number

This 2 corresponds to closing the window. In fact, we can see from the English meaning that 3 is to move the window and 5 is to change the size. In front, WM means Windows and Message.

Usually, we use the switch statement to write, and we can exit the program with the PostQuitMessage() function

void PostQuitMessage(
  [in] int nExitCode//Fill 0
);

The example code is as follows

#include "framework.h"
#include "WindowsProject1.h"
LRESULT CALLBACK WindowProc(
    _In_ HWND   hwnd,
    _In_ UINT   uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
)
{
    switch (uMsg)//Here is the capture of message types. If the window is closed, the program will be terminated. If other operations are performed, it will not be affected
    {
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        return 0;
    }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    char szOutBuff[0x80];
    //1. Step 1: define what your window looks like
    TCHAR className[] = TEXT("My First Window");
    WNDCLASS wndclass = { 0 };
    wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
    wndclass.lpszClassName = className;
    wndclass.hInstance = hInstance;
    wndclass.lpfnWndProc = WindowProc;//Instead of calling the function, just tell the function name and the operating system will call it
    RegisterClass(&wndclass);
    //Part II: create and display windows
    HWND hwnd = CreateWindow(
        className,
        TEXT("My first window"),
        WS_OVERLAPPEDWINDOW,
        10,
        10,
        600,
        300,
        NULL,
        NULL,
        hInstance,
        NULL
    );
    if (hwnd == NULL)
    {
        sprintf(szOutBuff, "Error:%d", GetLastError());
        OutputDebugStringA(szOutBuff);
        return 0;
    }
    ShowWindow(hwnd, SW_SHOW);
    //3. Step 3: receive the message and process it
    MSG msg;
    BOOL bRet;
​
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (bRet == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            //Conversion message
            //TranslateMessage(&msg);
            DispatchMessage(&msg);//Distribute messages
        }
    }
    
​
​
    return 0;
}
​

output

 

When we close the window, we can look in the task manager and find that we can't find the program, that is, when we close the window, the program also terminates.

Another example is that we want to accept a keyboard message, such as printing a when we press A. what should we do?

As shown in the figure above, the message generated by pressing the keyboard is called KEYDOWN, and when it pops up, it is called KEYUP. Similarly, we put it into the switch statement

The code is as follows (there is no need to modify other places, only WindowProc is modified, so other codes will not be copied)

LRESULT CALLBACK WindowProc(
    _In_ HWND   hwnd,
    _In_ UINT   uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
)
{
    switch (uMsg)
    {
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        return 0;
    }
    case WM_KEYDOWN:
    {
        MessageBox(0,0,0,0);//Don't set it first. Let's see the effect
        return 0;
    }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

But we press the keyboard to output:

As shown in the figure, we can now capture the message of keyboard pressing. Now we want to press the keyboard to know who it is. Let's check the official documents

LRESULT CALLBACK WindowProc(
  HWND  hwnd,        
  WM_IME_KEYDOWN,  
  WPARAM wParam, 
  LPARAM lParam      
);

We know that the second parameter MSG in WindowProc() is only a message type. It does not know which key we pressed. At this time, the third and fourth parameters will be used.

wParam
 Virtual key code for non system keys.

This parameter means that each key corresponds to a virtual code. We print out this virtual code (just change part):

LRESULT CALLBACK WindowProc(
    _In_ HWND   hwnd,
    _In_ UINT   uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
)
{
    switch (uMsg)
    {
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        return 0;
    }
    case WM_KEYDOWN:
    {
        char szOutBuff[0x80];
        sprintf(szOutBuff, "news: %x - %x \n", uMsg, wParam);//The first prints the message type and the second prints the virtual code
        OutputDebugStringA(szOutBuff);
        return 0;
    }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

Output:

As shown in the figure, when we press the A key, the first 100 refers to the message type (KEYDOWN), the second is A, the hexadecimal is 41, and the decimal is 65, that is, the A corresponding to ASCII code.

Let's try the fourth parameter, lParam:

What does that mean? Let's print it out first.

LRESULT CALLBACK WindowProc(
    _In_ HWND   hwnd,
    _In_ UINT   uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
)
{
    switch (uMsg)
    {
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        return 0;
    }
    case WM_KEYDOWN:
    {
        char szOutBuff[0x80];
        sprintf(szOutBuff, "news: %x - %x -%x \n", uMsg, wParam,lParam);
        OutputDebugStringA(szOutBuff);
        return 0;
    }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

Output:

In fact, this is A 32-digit number to represent various information, and the information represented by each digit corresponds to the above figure. For example, the 30th position is 1 if you press the keyboard before (I press and hold A). If you don't press it, it is 0 For example, this 401e0001 is disassembled

0100 0000 0001 1110 0000 0000 0000 0001
 The second number on the left is the 30th. If it is 1, it means it is pressed.

This is 200001

0000 0000 0010 0000 0000 0000 0000 0001
 The second number on the left is 0, indicating that it is not pressed

Each type of message has two parameters, wParam and lParam. The meaning of these two parameters is determined by the message type. Different message types are different.

For example, these two parameters are useful in KEYDOWN, but not in DESTROY. Look at the official documents

 

You can see that these two parameters are not used.

Then, in the above question, we want to press a key to output one. Although the corresponding virtual code is output, it is not perfect. What should we do if we want to directly output the pressed key? Instead of KEYDOWN, we use CHAR to receive character messages on the keyboard. Look at the official documents

This corresponds to the character. Sample code

LRESULT CALLBACK WindowProc(
    _In_ HWND   hwnd,
    _In_ UINT   uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
)
{
    switch (uMsg)
    {
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        return 0;
    }
    case WM_CHAR:
    {
        char szOutBuff[0x80];
        sprintf(szOutBuff, "news: %c \n", wParam);//Print here
        OutputDebugStringA(szOutBuff);
        return 0;
    }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

When we write this, we find that pressing the keyboard will not output a message, because we also need to use TranslateMessage() (I shielded this code before)

Converts a virtual key message to a character message.
BOOL TranslateMessage(
  [in] const MSG *lpMsg
);

This function is used to convert messages. If we don't convert, we won't get characters. What it does is convert the virtual code obtained by pressing the keyboard into characters,

The example code is as follows:

#include "framework.h"
#include "WindowsProject1.h"
LRESULT CALLBACK WindowProc(
    _In_ HWND   hwnd,
    _In_ UINT   uMsg,
    _In_ WPARAM wParam,
    _In_ LPARAM lParam
)
{
    switch (uMsg)
    {
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        return 0;
    }
    case WM_CHAR:
    {
        char szOutBuff[0x80];
        sprintf(szOutBuff, "news: %c \n", wParam);
        OutputDebugStringA(szOutBuff);
        return 0;
    }
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
    _In_opt_ HINSTANCE hPrevInstance,
    _In_ LPWSTR    lpCmdLine,
    _In_ int       nCmdShow)
{
    char szOutBuff[0x80];
    //1. Step 1: define what your window looks like
    TCHAR className[] = TEXT("My First Window");
    WNDCLASS wndclass = { 0 };
    wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
    wndclass.lpszClassName = className;
    wndclass.hInstance = hInstance;
    wndclass.lpfnWndProc = WindowProc;//Instead of calling the function, just tell the function name and the operating system will call it
    RegisterClass(&wndclass);
    //Part II: create and display windows
    HWND hwnd = CreateWindow(
        className,
        TEXT("My first window"),
        WS_OVERLAPPEDWINDOW,
        10,
        10,
        600,
        300,
        NULL,
        NULL,
        hInstance,
        NULL
    );
    if (hwnd == NULL)
    {
        sprintf(szOutBuff, "Error:%d", GetLastError());
        OutputDebugStringA(szOutBuff);
        return 0;
    }
    ShowWindow(hwnd, SW_SHOW);
    //3. Step 3: receive the message and process it
    MSG msg;
    BOOL bRet;
​
    while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
    {
        if (bRet == -1)
        {
            // handle the error and possibly exit
        }
        else
        {
            //*****Conversion message******//
            TranslateMessage(&msg);
            DispatchMessage(&msg);//Distribute messages
        }
    }
    return 0;
}

Output:

succeed

Topics: Windows WIN32