Windows编程03_消息队列,键盘消息,鼠标消息,定时器消息,菜单资源

01 消息队列

1.消息队列概念

  • 消息队列是用于存放消息的队列。
  • 消息在队列中先入先出。
  • 所有窗口程序都具有消息队列。
  • 程序可以从队列中获取消息。

2.消息队列分类

  • 系统消息队列 -由系统维护的消息队列。存放系统产生的消息,例如鼠标,键盘等。
  • 程序消息队列 -属于每一个应用程序(线程)的消息队列。由应用程序(线程)维护。

消息先进系统队列,再分给程序消息队列:
在这里插入图片描述
PostMessage到系统队列中:
在这里插入图片描述

3.消息和队列关系

  • 消息和消息队列的关系
    1.当鼠标,键盘产生消息时,会将消息存放到系统消息队列
    2.系统会根据存放的消息,找到对应程序的消息队列。
    3.将消息投递到程序的消息队列中。

  • 根据消息和消息队列之间使用关系,将消息分成两类:
    队列消息 -消息的发送和获取,都是通过消息队列完成。
    非队列消息 -消息的发送和获取,是直接调用消息的窗口处理完成。

  • 队列消息 -消息发送后,首先放入队列,然后通过消息循环,从队列当中获取。
    GetMessage -从消息队列中获取消息
    PostMessage -将消息投递到消息队列
    常见队列消息:WM_PAINT,键盘,鼠标,定时器

  • 非队列消息 -消息发送时,首先查找消息接收窗口的窗口处理函数,直接调用处理函数,完成消息。
    SendMessage -直接将消息发送给窗口的处理函数,并等候处理结果。
    常见消息:WM_CREATE,WM_SIZE等。
    在这里插入图片描述

4.深谈GetMessage原理

  • 在程序(线程)消息队列查找消息,如果队列有消息,检查消息是否满足指定条件(HWND,ID范围),不满足条件就不会取出消息,否则从队列取出消息返回。
  • 如果程序(线程)消息队列没有消息,向系统消息队列获取属于本程序的消息。如果系统队列的当前消息属于本程序,系统会将消息转发到程序消息队列中。
  • 如果系统消息队列也没有消息,检查当前进程的所有窗口的需要重新绘制的区域,如果发现有需要绘制的区域,产生WM_PAINT消息,取得消息返回处理。
  • 如果没有重新绘制区域,检查定时器如果有到时的定时器,产生WM_TIMER,返回处理执行。
  • 如果没有到时的定时器,整理程序的资源,内存等等。
  • GetMessage会继续等候下一条消息。PeekMessage会返回FALSE,交出程序的控制权。
  • 注意:GetMessage如果获取到是WM_QUIT,函数会返回FALSE.

5.WM_PAINT消息

  • 产生时间:当窗口需要绘制的时候。

  • 附带消息:wParam:0。
    lParam:0。

  • 专职用法:用于绘图。

  • 窗口无效区域:需要重新绘制的区域。
    BOOL InvalidateRect(
    HWND hWnd, //窗口句柄
    CONST RECT* lpRect, //区域的矩形坐标
    BOOL bErase //重绘前是否先擦除
    );

  • 消息处理步骤
    1>开始绘图
    HDC BeginPaint(
    HWND hwnd, //绘制窗口
    LPPAINSTRUCT lpPaint //绘图参数的BUFF
    );//返回汇入设备句柄HDC
    2>正式绘图
    3>结束绘图
    BOOL EndPaint(
    HWND hwnd, //绘制窗口
    CONST LPPAINSTRUCT *lpPaint //绘图参数的指针BeginPaint返回
    );

#include<Windows.h>
#include<stdio.h>

HANDLE g_hOutput = 0;//结束标准输出句柄

void OnPaint(HWND hWnd)
{
	char* pszText = "WM_PAINT\n";
	WriteConsole(g_hOutput, pszText, strlen(pszText), NULL, NULL);

	PAINTSTRUCT ps = { 0 };
	HDC hdc = BeginPaint(hWnd, &ps);
	TextOut(hdc, 100, 100, "hello", 5);
	EndPaint(hWnd, &ps);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
	switch (msgID)
	{
	case WM_PAINT:
		OnPaint(hWnd);
		break;
	case WM_LBUTTONDOWN:
		InvalidateRect(hWnd, NULL, TRUE);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);//可以使用GetMessage函数返回0
		break;
	}
	return DefWindowProc(hWnd, msgID, wParam, lParam);
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
	AllocConsole();//程序中增加DOS窗口
	g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//标准输出句柄
	//注册窗口类
	WNDCLASS wc = { 0 };
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.hCursor = NULL;
	wc.hIcon = NULL;
	wc.hInstance = hIns;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = "Main";
	wc.lpszMenuName = NULL;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&wc);//将以上所有赋值全部写入操作系统。
	//在内存创建窗口
	HWND hWnd = CreateWindowEx(0, "Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
	//显示窗口
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);
	//消息循环
	MSG nMsg = { 0 };
	while (GetMessage(&nMsg, NULL, 0, 0))
	{
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
	}
	return 0;
}

在这里插入图片描述

02 键盘消息

1.键盘消息分类

  • WM_KEYDOWN -按键被按下时产生
  • WM_KEYUP -按键被放开时产生
  • WM_SYSKEYDOWM -系统按键按下时产生
  • WM_SYSKEYUP -系统按键放开时产生
    附带信息:
    WPARAM -按键的Virtual Key
    LPARAM -按键的参数,例如按下次数

2.字符消息(WM_CHAR)

  • TranslateMessage在转换WM_KEYDOWN消息时,对于可见字符可以产生WM_CHAR,不可见字符无此消息。

  • 附带消息
    WPARAM -输入的字符的ASCII字符编码值
    LPARAM -按键的相关参数

    TranslateMessage内部实现过程:
    在这里插入图片描述
    键盘消息流程:
    在这里插入图片描述

#include<Windows.h>
#include<stdio.h>

HANDLE g_hOutput = 0;//结束标准输出句柄

void OnKeyDown(HWND hWnd, WPARAM wParam)
{
	char szText[256] = { 0 };
	sprintf(szText, "WM_KEYDOWN:键码值:%d\n", wParam);
	WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}

void OnKeyUp(HWND hWnd, WPARAM wParam)
{
	char szText[256] = { 0 };
	sprintf(szText, "WM_KEYUP:键码值:%d\n", wParam);
	WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}

void OnChar(HWND hWnd, WPARAM wParam)
{
	char szText[256] = { 0 };
	sprintf(szText, "WM_CHAR:wParam:%d\n", wParam);
	WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
	switch (msgID)
	{
	case WM_CHAR:
		OnChar(hWnd, wParam);
		break;
	case WM_KEYDOWN:
		OnKeyDown(hWnd, wParam);
		break;
	case WM_KEYUP:
		OnKeyUp(hWnd, wParam);
		break;
	case WM_DESTROY:
		PostQuitMessage(0);//可以使用GetMessage函数返回0
		break;
	}
	return DefWindowProc(hWnd, msgID, wParam, lParam);
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
	AllocConsole();//程序中增加DOS窗口
	g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//标准输出句柄
	//注册窗口类
	WNDCLASS wc = { 0 };
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wc.hCursor = NULL;
	wc.hIcon = NULL;
	wc.hInstance = hIns;
	wc.lpfnWndProc = WndProc;
	wc.lpszClassName = "Main";
	wc.lpszMenuName = NULL;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	RegisterClass(&wc);//将以上所有赋值全部写入操作系统。
	//在内存创建窗口
	HWND hWnd = CreateWindowEx(0, "Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
	//显示窗口
	ShowWindow(hWnd, SW_SHOW);
	UpdateWindow(hWnd);
	//消息循环
	MSG nMsg = { 0 };
	while (GetMessage(&nMsg, NULL, 0, 0))
	{
		TranslateMessage(&nMsg);
		DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
	}
	return 0;
}

在这里插入图片描述

03 鼠标消息

1.鼠标消息分类

  • 基本鼠标消息
    WM_LBUTTONDOWN -鼠标左键按下
    WM_LBUTTONUP-鼠标左键抬起
    WM_RBUTTONDOWN -鼠标右键按下
    WM_RBUTTONUP -鼠标右键抬起
    WM_MOUSEMOVE -鼠标移动消息
  • 双击消息
    WM_LBUTTONDBLCLK -鼠标左键双击
    WM_RBUTTONDBLCLK -鼠标右键双击
  • 滚轮消息
    WM_MOUSEWHEEL -鼠标滚轮消息

2.鼠标消息基本消息

  • 附带信息:
    wPARAM:其他按键的状态,例如Ctrl/Shift等
    lPARAM:鼠标的位置,窗口客户区坐标系。
    LOWORD X坐标位置
    HIWORD Y坐标位置
  • 一般情况鼠标按下/抬起成双出现。在鼠标移动过程中,会根据移动速度产生一系列的WM_MOUSEMOVE。

3.鼠标双击消息

  • 附带信息:
    wPARAM:其他按键的状态,例如Ctrl/Shift等
    lPARAM:鼠标的位置,窗口客户区坐标系。
    LOWORD(lParam) // X坐标位置
    HIWORD(lParam) // Y坐标位置
  • 消息产生顺序
    以左键双击为例:
    WM_LBUTTONDOWN
    WM_LBUTTONUP
    WM_LBUTTONDBLCLK
    WM_LBUTTONUP
    使用时需要在注册窗口类的时候添加CS_DBLCLKS风格。

4.鼠标滚轮消息

  • 附带信息:
    wPARAM:
    LOWORD -其他按键的状态
    HIWORD -滚轮的偏移量,通过正负值表示滚轮方向。
    正:向前滚动 负:向后滚动
    lPARAM:鼠标当前的位置,屏幕坐标系
    LOWORD -X坐标
    HIWORD -Y坐标
  • 使用:通过偏移量,获取滚动的方向和距离
#include<Windows.h>
#include<stdio.h>

HANDLE g_hOutput = 0;//结束标准输出句柄

void OnLButtonDown(HWND hWnd, WPARAM wParam,LPARAM lParam)
{
    char szText[256] = { 0 };
    sprintf(szText, "WM_LBUTTONDOWN:其他按键状态:%d,X=%d,Y=%d\n", wParam,LOWORD(lParam),HIWORD(lParam));
    WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}

void OnLButtonUp(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    char szText[256] = { 0 };
    sprintf(szText, "WM_LBUTTONUP:其他按键状态:%d,X=%d,Y=%d\n", wParam, LOWORD(lParam), HIWORD(lParam));
    WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}

void OnMouseMove(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
    char szText[256] = { 0 };
    sprintf(szText, "WM_MOUSEMOVE:其他按键状态:%d,X=%d,Y=%d\n", wParam, LOWORD(lParam), HIWORD(lParam));
    //WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}

void OnLButtonDblClk(HWND hWnd)
{
    char szText[256] = "WM_LBUTTONDBLCLK\n";
    WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}

void OnMouseWheel(HWND hWnd, WPARAM wParam)
{
    short nDelta = HIWORD(wParam);//偏移量
    char szText[256] = { 0 };
    sprintf(szText, "WM_MOUSEHWHEEL:nDelta=%d\n", nDelta);
    WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
    switch (msgID)
    {
    case WM_MOUSEHWHEEL:
        OnMouseWheel(hWnd, wParam);
        break;
    case WM_LBUTTONDBLCLK:
        OnLButtonDblClk(hWnd);
            break;
    case WM_MOUSEMOVE:
        OnMouseMove(hWnd, wParam, lParam);
        break;
    case WM_LBUTTONDOWN:
        OnLButtonDown(hWnd, wParam, lParam);
        break;
    case WM_LBUTTONUP:
        OnLButtonUp(hWnd, wParam, lParam);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);//可以使用GetMessage函数返回0
        break;
    }
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
    AllocConsole();//程序中增加DOS窗口
    g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//标准输出句柄
    //注册窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = NULL;
    wc.hIcon = NULL;
    wc.hInstance = hIns;
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = "Main";
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW|CS_DBLCLKS;
    RegisterClass(&wc);//将以上所有赋值全部写入操作系统。
    //在内存创建窗口
    HWND hWnd = CreateWindowEx(0, "Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
    //显示窗口
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
    //消息循环
    MSG nMsg = { 0 };
    while (GetMessage(&nMsg, NULL, 0, 0))
    {
        TranslateMessage(&nMsg);
        DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
    }
    return 0;
}

在这里插入图片描述

04 定时器消息

1.定时器消息介绍

  • 产生时间:
    在程序中创建定时器,当到达时间间隔时,定时器会向程序发送一个WM_TIMER消息。定时器的精度是毫秒,但是准确度很低。例如设置时间间隔为1000ms,但是会在非1000毫秒到达消息。
  • 附带信息:
    wPARAM:定时器ID
    lPARAM:定时器处理函数的指针

2.创建销毁定时器

  • 创建定时器
    UINT_PTR SetTimer(
    HWND hWnd, //定时器窗口句柄
    UINT_PTR nIDEvent, //定时器ID
    UINT uElapse,//时间间隔
    TIMERPROC lpTimerFunc //定时器处理函数指针(一般不使用,为NULL)
    );创建成功,返回非0。

  • 关闭定时器
    BOOL KillTimer(
    HWND hWnd, //定时器窗口句柄
    UINT_PTR uIDEvent //定时器ID
    );

#include<Windows.h>
#include<stdio.h>

HANDLE g_hOutput = 0;//结束标准输出句柄

void OnTimer(HWND hWnd, WPARAM wParam)
{
    /*switch (wParam) {
    case 1:
        //...
        break;
    case 2:
        //...
        break;
    }*/
    char szText[256] = { 0 };
    sprintf(szText, "WM_TIMER:定时器ID=%d\n", wParam);
    WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
    switch (msgID)
    {
    case WM_TIMER:
        OnTimer(hWnd, wParam);
        break;
    case WM_CREATE:
        SetTimer(hWnd, 1, 1000, NULL);
        SetTimer(hWnd, 2, 2000, NULL);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);//可以使用GetMessage函数返回0
        break;
    }
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
    AllocConsole();//程序中增加DOS窗口
    g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);//标准输出句柄
    //注册窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = NULL;
    wc.hIcon = NULL;
    wc.hInstance = hIns;
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = "Main";
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&wc);//将以上所有赋值全部写入操作系统。
    //在内存创建窗口
    HWND hWnd = CreateWindowEx(0, "Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
    //显示窗口
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
    //消息循环
    MSG nMsg = { 0 };
    while (GetMessage(&nMsg, NULL, 0, 0))
    {
        TranslateMessage(&nMsg);
        DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
    }
    return 0;
}

在这里插入图片描述

05 菜单资源

1.菜单分类

  • 窗口的顶层菜单
  • 弹出式菜单
  • 系统菜单

HMENU类型表示菜单,ID表示菜单项。

2.资源相关

在这里插入图片描述

3.菜单资源使用

  • 添加菜单资源

  • 加载菜单资源
    1>注册窗口类时设置菜单
    2>创建窗口传参设置餐单
    3>在主窗口WM_CREATE消息中利用SetMenu函数设置菜单

    加载菜单资源
    HMENU LoadMenu(
    HINSTANCE hInstance, //handle to module
    LPCTSTR lpMenuName //menu name or resource identifier
    );

步骤:
在这里插入图片描述
右键->添加->资源
在这里插入图片描述
添加名字,更改ID
在这里插入图片描述
添加分隔符:空格,属性分隔符改为true
在这里插入图片描述
最终效果:
在这里插入图片描述
加载菜单资源:
1.注册窗帘类创菜单,将菜单注册到窗口:
wc.lpszMenuName = (char )IDR_MENU1;
在这里插入图片描述
2.创建窗口传参设置菜单
HMENU hMenu = LoadMenu(hIns, (char
)IDR_MENU1);
HWND hWnd = CreateWindowEx(0, “Main”, “window”, WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, hMenu, hIns, NULL);

3.在主窗口WM_CREATE消息中利用SetMenu函数设置菜单

#include<Windows.h>
#include<stdio.h>
#include"resource.h"

HINSTANCE g_hInstance = 0;

void OnCreate(HWND hWnd)
{
    HMENU hMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1);
    SetMenu(hWnd, hMenu);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
    switch (msgID)
    {
    case WM_CREATE:
        OnCreate(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);//可以使用GetMessage函数返回0
        break;
    }
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
    g_hInstance = hIns;
    //注册窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = NULL;
    wc.hIcon = NULL;
    wc.hInstance = hIns;
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = "Main";
    //wc.lpszMenuName = (char *)IDR_MENU1;
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&wc);//将以上所有赋值全部写入操作系统。
    //在内存创建窗口
    HWND hWnd = CreateWindowEx(0, "Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
    //显示窗口
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
    //消息循环
    MSG nMsg = { 0 };
    while (GetMessage(&nMsg, NULL, 0, 0))
    {
        TranslateMessage(&nMsg);
        DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
    }
    return 0;
}

在这里插入图片描述

4.命令消息处理

  • 附带信息
    wPARAM:
    HIWORD -对于菜单为0
    LOWOED -菜单项的ID
    lPARAM -对应菜单为0
#include<Windows.h>
#include<stdio.h>
#include"resource.h"

HINSTANCE g_hInstance = 0;

void OnCreate(HWND hWnd)
{
    HMENU hMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1);
    SetMenu(hWnd, hMenu);
}

void OnCommand(HWND hWnd, WPARAM wParam)
{
    switch (LOWORD(wParam))
    {
    case ID_NEW:
        MessageBox(hWnd, "新建被点击", "Info", MB_OK);
        break;
    case ID_EXIT:
        MessageBox(hWnd, "退出被点击", "Info", MB_OK);
        break;
    case ID_ABOUT:
        MessageBox(hWnd, "关于被点击", "Info", MB_OK);
        break;
    }
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
    switch (msgID)
    {
    case WM_COMMAND:
        OnCommand(hWnd, wParam);
        break;
    case WM_CREATE:
        OnCreate(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);//可以使用GetMessage函数返回0
        break;
    }
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
    g_hInstance = hIns;
    //注册窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = NULL;
    wc.hIcon = NULL;
    wc.hInstance = hIns;
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = "Main";
    //wc.lpszMenuName = (char *)IDR_MENU1;
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&wc);//将以上所有赋值全部写入操作系统。
    //在内存创建窗口
    HWND hWnd = CreateWindowEx(0, "Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
    //显示窗口
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
    //消息循环
    MSG nMsg = { 0 };
    while (GetMessage(&nMsg, NULL, 0, 0))
    {
        TranslateMessage(&nMsg);
        DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
    }
    return 0;
}

在这里插入图片描述

5.上下文菜单

  • 显示上下文菜单
    BOOL TrackPopupMenu(
    HMENU hMenu, //菜单句柄
    UINT uFlags, //显示方式
    int x,//水平位置,屏幕坐标系
    int y,//垂直位置,屏幕坐标系
    int nReserved, //保留,必须0
    HWND hWnd, //处理菜单消息的窗口句柄
    CONST RECT *prcRect //NULL,忽略
    );TrackPopupMenu是阻塞函数
#include<Windows.h>
#include<stdio.h>
#include"resource.h"

HINSTANCE g_hInstance = 0;

void OnCreate(HWND hWnd)
{
    HMENU hMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1);
    SetMenu(hWnd, hMenu);
}

void OnCommand(HWND hWnd, WPARAM wParam)
{
    switch (LOWORD(wParam))
    {
    case ID_NEW:
        MessageBox(hWnd, "新建被点击", "Info", MB_OK);
        break;
    case ID_EXIT:
        MessageBox(hWnd, "退出被点击", "Info", MB_OK);
        break;
    case ID_ABOUT:
        MessageBox(hWnd, "关于被点击", "Info", MB_OK);
        break;
    }
}

void OnRButtonUp(HWND hWnd, LPARAM lParam)
{
    HMENU hMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1);
    HMENU hPopup = GetSubMenu(hMenu,0);
    TrackPopupMenu(hPopup, TPM_CENTERALIGN | TPM_VCENTERALIGN, LOWORD(lParam), HIWORD(lParam), 0, hWnd, NULL);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
    switch (msgID)
    {
    case WM_RBUTTONUP:
        OnRButtonUp(hWnd, lParam);
        break;
    case WM_COMMAND:
        OnCommand(hWnd, wParam);
        break;
    case WM_CREATE:
        OnCreate(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);//可以使用GetMessage函数返回0
        break;
    }
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
    g_hInstance = hIns;
    //注册窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = NULL;
    wc.hIcon = NULL;
    wc.hInstance = hIns;
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = "Main";
    //wc.lpszMenuName = (char *)IDR_MENU1;
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&wc);//将以上所有赋值全部写入操作系统。
    //在内存创建窗口
    HWND hWnd = CreateWindowEx(0, "Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
    //显示窗口
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
    //消息循环
    MSG nMsg = { 0 };
    while (GetMessage(&nMsg, NULL, 0, 0))
    {
        TranslateMessage(&nMsg);
        DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
    }
    return 0;
}
  • WM_RBUTTONUP
    鼠标右键弹起消息为窗口坐标系坐标,要使用需将其转换成屏幕坐标系坐标ClientToScreen

  • WM_CONTEXTMENU
    wParam:右键点击的窗口句柄
    lPARAM:LOWORD X坐标,屏幕坐标系
    HIWORD Y坐标,屏幕坐标系
    WM_CONTEXTMENU消息是在WM_RBUTTONUP消息之后产生。

#include<Windows.h>
#include<stdio.h>
#include"resource.h"

HINSTANCE g_hInstance = 0;

void OnCreate(HWND hWnd)
{
    HMENU hMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1);
    SetMenu(hWnd, hMenu);
}

void OnCommand(HWND hWnd, WPARAM wParam)
{
    switch (LOWORD(wParam))
    {
    case ID_NEW:
        MessageBox(hWnd, "新建被点击", "Info", MB_OK);
        break;
    case ID_EXIT:
        MessageBox(hWnd, "退出被点击", "Info", MB_OK);
        break;
    case ID_ABOUT:
        MessageBox(hWnd, "关于被点击", "Info", MB_OK);
        break;
    }
}

void OnContextMenu(HWND hWnd, LPARAM lParam)
{
    HMENU hMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1);
    HMENU hPopup = GetSubMenu(hMenu,0);
    TrackPopupMenu(hPopup, TPM_CENTERALIGN | TPM_VCENTERALIGN, LOWORD(lParam), HIWORD(lParam), 0, hWnd, NULL);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam)
{
    switch (msgID)
    {
    case WM_CONTEXTMENU:
        OnContextMenu(hWnd, lParam);
        break;
    case WM_COMMAND:
        OnCommand(hWnd, wParam);
        break;
    case WM_CREATE:
        OnCreate(hWnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);//可以使用GetMessage函数返回0
        break;
    }
    return DefWindowProc(hWnd, msgID, wParam, lParam);
}

//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdLine, int nCmdShow)
{
    g_hInstance = hIns;
    //注册窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.hCursor = NULL;
    wc.hIcon = NULL;
    wc.hInstance = hIns;
    wc.lpfnWndProc = WndProc;
    wc.lpszClassName = "Main";
    //wc.lpszMenuName = (char *)IDR_MENU1;
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    RegisterClass(&wc);//将以上所有赋值全部写入操作系统。
    //在内存创建窗口
    HWND hWnd = CreateWindowEx(0, "Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);
    //显示窗口
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
    //消息循环
    MSG nMsg = { 0 };
    while (GetMessage(&nMsg, NULL, 0, 0))
    {
        TranslateMessage(&nMsg);
        DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值