Windows API窗口绘图程序设计

实验效果截图说明

屏幕截图 2026-09-24 160126

截图描述:
窗口背景为白色;在窗口内单击鼠标左键,会在点击位置生成一个红色填充正方形,正方形边长随机在 10~100 像素之间;多次点击可以叠加绘制多个红色方块,窗口缩放、最小化还原后,所有绘制的正方形不会消失。

一、实验思路

实验目的

使用 Win32 API 编写 Windows 桌面图形程序,掌握 Windows 窗口消息机制、GDI 绘图、鼠标消息捕获、随机数生成。实现需求:在窗口任意位置按下鼠标左键,绘制边长范围在 10~100 的红色填充正方形。

设计思路

  1. 窗口创建:注册窗口类,创建 800×600 的窗口,启动消息循环,等待系统消息。
  2. 数据存储:自定义结构体保存每个正方形的左上角坐标、边长;用数组存储多个正方形信息,支持多次点击绘制多个图形。
  3. 鼠标消息捕获:监听WM_LBUTTONDOWN(鼠标左键按下消息),获取鼠标点击坐标,生成 10~100 的随机边长,存入数组。
  4. GDI 绘图重绘:调用InvalidateRect()触发窗口重绘消息WM_PAINT;在重绘消息中创建红色画刷,循环遍历数组,使用Rectangle函数绘制并填充所有正方形。
  5. 资源管理:绘图结束释放 GDI 画刷资源,避免内存泄漏。
  6. 窗口销毁:监听WM_DESTROY消息,退出消息循环,关闭程序。

实现原理

Windows 程序是消息驱动模型。程序不主动轮询鼠标,而是等待操作系统发送消息。当鼠标左键按下,系统发送WM_LBUTTONDOWN;窗口需要重绘(窗口打开、窗口移动、调用 InvalidateRect)时触发WM_PAINT,在这个消息里完成绘图。GDI(图形设备接口)负责窗口绘图,画刷决定图形填充颜色。

二、完整源代码

#include <Windows.h>
#include <cstdlib>
#include <ctime>

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
struct RectInfo
{
    int x;
    int y;
    int width;
};
RectInfo rectList[1000];
int rectCount = 0;

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    srand((unsigned int)time(NULL));

    const wchar_t CLASS_NAME[] = L"MyWinRectClass";
    WNDCLASSEX wc = {0};
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.lpfnWndProc   = WndProc;
    wc.hInstance     = hInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);

    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, L"窗口注册失败!", L"Error", MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    HWND hWnd = CreateWindowEx(
        0,
        CLASS_NAME,
        L"随机红色正方形",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL, NULL, hInstance, NULL
    );

    if (hWnd == NULL)
    {
        MessageBox(NULL, L"窗口创建失败!", L"Error", MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    MSG msg = {0};
    while (GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_LBUTTONDOWN:
    {
        if (rectCount >= 1000) break;

        int mouseX = LOWORD(lParam);
        int mouseY = HIWORD(lParam);
        int side = rand() % 91 + 10; 

        rectList[rectCount].x = mouseX;
        rectList[rectCount].y = mouseY;
        rectList[rectCount].width = side;
        rectCount++;

        InvalidateRect(hWnd, NULL, TRUE);
        break;
    }

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);

        HBRUSH hRedBrush = CreateSolidBrush(RGB(255, 0, 0));
        HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, hRedBrush);

        for (int i = 0; i < rectCount; i++)
        {
            int x1 = rectList[i].x;
            int y1 = rectList[i].y;
            int x2 = x1 + rectList[i].width;
            int y2 = y1 + rectList[i].width;
            Rectangle(hdc, x1, y1, x2, y2);
        }

        SelectObject(hdc, hOldBrush);
        DeleteObject(hRedBrush);
        EndPaint(hWnd, &ps);
        break;
    }

    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);
    }
    return 0;
}

三、主要代码详细解析

1. 头文件

#include <Windows.h>
#include <cstdlib>
#include <ctime>
  • Windows.h:Win32 API 核心头文件,提供窗口、消息、GDI 绘图全部函数与数据类型。
  • cstdlib:rand () 随机数函数。
  • ctime:time () 获取系统时间,作为随机数种子。

2. 结构体 RectInfo

struct RectInfo
{
    int x;
    int y;
    int width;
};
RectInfo rectList[1000];
int rectCount = 0;

作用:保存每一个正方形的数据。
因为窗口每次重绘时,画面会清空,必须把所有正方形信息保存起来,每次 WM_PAINT 全部重新绘制。
rectList数组存放所有方块,rectCount记录当前方块总数。

3. WinMain 程序入口

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)

Win32 窗口程序入口。

  • hInstance:当前程序实例句柄。
  • srand((unsigned int)time(NULL)):随机种子,保证每次运行随机边长不一样。

窗口注册:WNDCLASSEX 结构体描述窗口类的属性,包含消息处理函数、光标、背景色,使用RegisterClassEx向操作系统注册窗口类。
CreateWindowEx:创建窗口实例,设置窗口标题、大小、样式。
消息循环 while(GetMessage):Windows 程序核心。不断读取系统消息,交给 WndProc 处理,程序持续运行直到收到退出消息。

4. WndProc 窗口回调函数

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

窗口消息回调函数,操作系统有事件(鼠标点击、窗口重绘、关闭)就调用这个函数,msg 代表消息类型。

① WM_LBUTTONDOWN 鼠标左键按下

int mouseX = LOWORD(lParam);
int mouseY = HIWORD(lParam);
int side = rand() % 91 + 10;
  • lParam 参数打包了鼠标坐标,LOWORD取低 16 位 = X 坐标;HIWORD取高 16 位 = Y 坐标。
  • rand()%91:得到 0~90,+10 → 范围 10~100,满足题目边长要求。
  • InvalidateRect(hWnd, NULL, TRUE):请求窗口重绘,触发 WM_PAINT 消息。

② WM_PAINT 绘图消息

HDC hdc = BeginPaint(hWnd, &ps);
HBRUSH hRedBrush = CreateSolidBrush(RGB(255,0,0));
SelectObject(hdc, hRedBrush);
Rectangle(hdc, x1,y1,x2,y2);
  • HDC:设备上下文句柄,窗口绘图的画布。
  • CreateSolidBrush(RGB(255,0,0)):创建红色实心画刷,绘制图形会自动填充内部。
  • Rectangle(hdc, x1,y1,x2,y2):绘制矩形,x1,y1 左上角,x2,y2 右下角;因为宽高相等,绘制结果就是正方形。
  • DeleteObject(hRedBrush):GDI 资源必须手动释放,否则会内存泄漏。

③ WM_DESTROY

窗口点击关闭按钮,触发此消息,调用PostQuitMessage(0),退出消息循环,程序结束。

四、实验结果

  1. 编译运行,弹出 800×600 窗口,标题为随机红色正方形。
  2. 在窗口任意位置单击鼠标左键,点击位置生成红色填充正方形,边长随机 10~100。
  3. 多次点击,生成多个大小各不相同红色正方形。
  4. 窗口最小化后恢复、拖动窗口改变大小,所有正方形仍然保留。
  5. 点击窗口关闭按钮,程序正常退出。

五、小组分工与心得体会

组员1(负责:代码编写、功能实现、代码合作调试)

分工:完成 Win32 窗口框架代码编写,实现鼠标消息捕获、随机正方形生成、GDI 绘图;调试程序,保证程序正常运行。
心得体会:本次实验学习了 Windows 消息驱动编程模型,一开始对 WinMain、WndProc 消息回调很难理解,最开始遇到 LNK2019 找不到 main 的链接错误,通过修改VS项目为Windows窗口程序解决。明白了窗口绘图不是永久画在屏幕上,每次窗口重绘都要重新绘制图形,所以必须保存所有图形的数据。掌握 GDI 画刷、Rectangle 绘图函数,理解鼠标消息的获取方式,体会到 Windows 程序事件驱动的特点。

组员2(负责:实验思路梳理、代码注释、文档撰写、代码合作调试)

分工:梳理实验设计思路,阅读源码,为代码添加详细注释;撰写实验文档,整理代码解析部分,整理博客文章结构,梳理每个函数的作用。
心得体会:在阅读 Win32 代码过程中,理解 Windows 图形程序的运行逻辑。原来程序不是顺序执行,而是被动等待操作系统发送消息。通过整理文档,把消息循环、WM_LBUTTONDOWN、WM_PAINT 这些概念梳理清楚。认识到写代码时规范注释的重要性,方便团队成员看懂代码,同时加深自己对 GDI 绘图原理的理解。

组员3(负责:程序测试、截图、代码合作调试)

分工:运行程序进行多组测试,测试边界情况(连续多次点击、窗口缩放、最小化还原),截取程序运行效果图;检查程序是否满足题目要求。
心得体会:本次作业测试中,测试了多次连续点击生成大量正方形,验证数组上限;测试窗口缩放后图形不会丢失。熟悉了 VS 项目创建流程,区分控制台项目与 Windows 桌面项目的区别。以前只写控制台 C++ 程序,第一次接触图形窗口编程,感受到图形编程的魅力,也发现图形程序需要考虑资源释放、消息重绘等控制台程序不会遇到的问题。

六、实验小结

本次实验基于 Win32 API 实现 Windows 消息驱动图形程序,完成鼠标左键点击窗口生成随机大小红色填充正方形的功能。实验搭建标准 Win32 窗口框架,利用结构体数组存储每一个正方形的坐标与边长信息,通过捕获WM_LBUTTONDOWN鼠标左键消息获取点击位置,生成 10~100 像素的随机边长并保存图形数据;调用InvalidateRect触发WM_PAINT重绘消息,在重绘函数中使用 GDI 画刷完成所有正方形的绘制,同时做好 GDI 资源释放,防止内存泄漏。

实验验证了 Windows 消息驱动机制:程序不会主动轮询输入,而是等待操作系统分发消息;窗口画面无法永久保存,当窗口缩放、最小化还原时会触发重绘,因此必须持久化图形数据,每次重绘时重新绘制全部图形。开发过程中遇到 Windows 子系统配置错误引发的 LNK2019 链接问题,通过修改项目属性解决。小组分工完成代码编写调试、文档整理注释、功能测试与截图工作。

通过本次实验,掌握 Win32 窗口创建、消息循环、回调函数、鼠标消息捕获与 GDI 基础绘图;理解 GDI 资源管理要点,区分控制台程序与 Windows 窗口程序开发差异。同时认识到图形程序开发需要兼顾数据存储、消息响应、资源回收。

posted @ 2026-09-24 18:59  IXIXIX0404  阅读(8)  评论(0)    收藏  举报