在这里插入图片描述

在这里插入图片描述在这里插入图片描述

🚀欢迎互三👉:程序猿方梓燚 💎💎
🚀关注博主,后期持续更新系列文章
🚀如果有错误感谢请大家批评指出,及时修改
🚀感谢大家点赞👍收藏⭐评论✍

引言

在编程的世界里,我们常常追求创造出令人惊叹的视觉效果和交互体验。今天,我们将深入探索一段用 C语言编写的代码,它实现了一个令人仿佛置身于《黑客帝国》电影中的矩阵效果。
在这里插入图片描述
想象一下,你的屏幕上如瀑布般落下一串串神秘的绿色字符,仿佛在诉说着未知的密码。这段代码不仅是技术的展示,更是创造力与编程艺术的结合。对于
C/C++零基础的小白来说,这将是一次充满挑战和惊喜的学习之旅。
通过剖析这段代码,我们将逐步揭开图形界面编程的神秘面纱,了解如何利用 Windows API
实现动态的视觉效果,掌握结构体的运用、消息处理机制以及随机数生成等关键技术。无论你是对编程充满好奇的初学者,还是渴望拓展技能的开发者,这段代码都将为你打开一扇通往新领域的大门。
让我们一同踏上这段代码探索之旅,感受编程的魅力与无限可能。
效果如下:

一、代码概述

这段代码是用 C 语言编写的一个模拟矩阵效果的程序。在屏幕上会显示类似电影《黑客帝国》中的绿色字符流不断下落的效果。用户可以通过右键点击暂停或继续效果,也可以通过按下按键或左键点击退出程序。

二、代码结构分析

一、包含头文件和定义常量部分

#include <windows.h>

#define ID_TIMER    1

#define STRMAXLEN  25
#define STRMINLEN  8

1.这段代码的开头部分引入了<windows.h>头文件,这是进行 Windows
编程所必需的。它包含了大量用于创建窗口、处理消息、进行图形绘制等功能的函数和结构体定义。
2.接着,定义了两个常量。ID_TIMER被设置为1,这个常量将在后续的代码中作为定时器的唯一标识。定时器在程序中用于定期触发特定的操作,比如更新屏幕上的字符列。
3. STRMAXLENSTRMINLEN分别定义为 25 和 8,它们将在后续用于控制字符列中字符串的长度范围。这意味着生成的字符列的长度将在 8 到 25 个字符之间随机确定。

二、定义结构体部分

typedef struct tagCharChain {
    struct tagCharChain *prev;
    char ch;
    struct tagCharChain *next;
} CharChain, *pCharChain;

typedef struct tagCharColumn {
    CharChain *head, *current, *point;
    int x, y, iStrLen;
    int iStopTimes, iMustStopTimes;
} CharColumn, *pCharColumn;

1.这里定义了两个重要的结构体。CharChain结构体代表字符链表中的一个节点。它包含三个成员:prev是一个指针,指向前一个CharChain节点;ch是一个字符,用于存储实际的字符值;next也是一个指针,指向后一个CharChain节点。通过这种方式,可以将多个CharChain节点连接成一个链表,从而实现对一系列字符的存储和管理。
2.CharColumn结构体代表一个字符列。它包含了多个成员:headcurrentpoint都是CharChain类型的指针,分别用于指向字符链表的头节点、当前节点和遍历指针;x和y是整数,分别表示字符列在屏幕上的横坐标和纵坐标;iStrLen是整数,代表字符列中字符串的长度;iStopTimesiMustStopTimes也是整数,用于控制字符列的暂停和继续状态。

三、主函数部分

int main(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
    static TCHAR szAppName[] = TEXT("matrix");
    HWND hwnd;
    MSG msg;
    WNDCLASS wndclass;

    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = WndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szAppName;

    if (!RegisterClass(&wndclass)) {
        MessageBox(NULL, TEXT("此程序必须运行在 NT 下!"), szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd = CreateWindow(szAppName, NULL, WS_DLGFRAME | WS_THICKFRAME | WS_POPUP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hInstance, NULL);

    ShowWindow(hwnd, SW_SHOWMAXIMIZED);
    UpdateWindow(hwnd);
    ShowCursor(FALSE);

    srand((int)GetCurrentTime());
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    ShowCursor(TRUE);
    return msg.wParam;
}

1.主函数是程序的入口点。首先,定义了一些变量,包括应用程序实例句柄hInstance、前一个应用程序实例句柄hPrevInstance、命令行参数szCmdLine和窗口显示参数iCmdShow。此外,还定义了窗口句柄hwnd、消息结构体msg和窗口类结构体wndclass
2.接着,设置窗口类的各种属性。wndclass.style被设置为CS_HREDRAW |CS_VREDRAW,这表示当窗口的水平或垂直大小发生改变时,窗口将自动重绘。wndclass.lpfnWndProc被设置为WndProc,这是一个函数指针,指向窗口过程函数,用于处理窗口的各种消息。wndclass.hInstance被设hInstance,表示当前应用程序的实例句柄。wndclass.hIconwndclass.hCursor分别用于设置窗口的图标和光标为默认值。wndclass.hbrBackground被设置为黑色画刷,这将使窗口的背景为黑wndclass.lpszMenuName被设置为NULL,表示窗口没有菜单。wndclass.lpszClassName被设置为szAppName,即窗口类的名称。
3.然后,使用RegisterClass(&wndclass)函数注册窗口类。如果注册失败,将弹出一个错误消息框,并返回 0 表示程序退出。
4.接下来,使用CreateWindow函数创建窗口。这个函数的参数包括窗口类名、窗口标题、窗口样式、窗口的初始位置和大小、菜单句柄、父窗口句柄、应用程序实例句柄等。这里创建了一个具有对话框框架、粗边框和弹出窗口样式的窗口,窗口的大小设置为屏幕大小。
5.ShowWindow(hwnd, SW_SHOWMAXIMIZED)用于显示窗口并最大化。UpdateWindow(hwnd)用于触发一个WM_PAINT消息,使窗口绘制自身。ShowCursor(FALSE)用于隐藏光标。
6.然后,使用srand((int)GetCurrentTime())设置随机数种子。这将确保每次运行程序时生成的随机数序列不同。
7.最后,进入一个消息循环。GetMessage(&msg, NULL, 0, 0)用于从消息队列中获取消息。如果有消息,将消息进行翻译和分发处理。TranslateMessage(&msg)用于将虚拟键消息转换为字符消息。DispatchMessage(&msg)将消息发送给窗口过程函数进行处理。当接收到退出消息时,循环结束。最后,显示光标并返回消息的参数wParam

四、随机字符生成函数部分

char randomChar() {
    return (rand() % 2)? '1' : '0';
}

1.这个函数用于生成随机的字符'0''1'。它通过调用rand函数生成一个随机数,然后对 2 取余。如果结果为 0,则返回'0';否则返回'1'

五、初始化字符列函数部分

int init(CharColumn *cc, int cyScreen, int x) {
    int j;
    cc->iStrLen = rand() % (STRMAXLEN - STRMINLEN) + STRMINLEN;
    cc->x = x + 3;
    cc->y = rand() % 3? rand() % cyScreen : 0;
    cc->iMustStopTimes = rand() % 6;
    cc->iStopTimes = 0;
    cc->head = cc->current = (pCharChain)calloc(cc->iStrLen, sizeof(CharChain));
    for (j = 0; j < cc->iStrLen - 1; j++) {
        cc->current->prev = cc->point;
        cc->current->ch = '\0';
        cc->current->next = cc->current + 1;
        cc->point = cc->current++;
    }
    cc->current->prev = cc->point;
    cc->current->ch = '\0';
    cc->current->next = cc->head;
    cc->head->prev = cc->current;

    cc->current = cc->point = cc->head;
    cc->head->ch = randomChar();
    return 0;
}

1.这个函数用于初始化一个字符列。它接受一个字符列结构体指针cc、屏幕高度cyScreen和一个整数x作为参数。
2.首先,生成一个随机数作为字符列的长度,范围在STRMINLENSTRMAXLEN之间。然后,设置字符列的横坐标x为传入的参数x加上 3,纵坐标y为随机生成的值或 0。接着,生成一个随机数作为字符列必须停止的次数。初始化停止次数为 0。
3.然后,使用calloc函数分配内存,创建一个长度为cc->iStrLen的字符链表。通过一个循环,设置链表中每个节点的前后指针,形成一个循环链表。
4.最后,将当前指针、遍历指针和头指针指向链表的头节点,并设置头节点的字符为一个随机生成的字符'0''1'。函数返回 0 表示初始化成功。

六、窗口过程函数部分

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    int i, j, temp, ctn;
    static HDC hdcMem;
    HFONT hFont;
    static HBITMAP hBitmap;
    static int cxScreen, cyScreen;
    static int iFontWidth = 10, iFontHeight = 15, iColumnCount;
    static CharColumn *ccChain;

    switch (message) {
        case WM_CREATE:
            cxScreen = GetSystemMetrics(SM_CXSCREEN);
            cyScreen = GetSystemMetrics(SM_CYSCREEN);
            SetTimer(hwnd, ID_TIMER, 10, NULL);

            hdc = GetDC(hwnd);
            hdcMem = CreateCompatibleDC(hdc);
            hBitmap = CreateCompatibleBitmap(hdc, cxScreen, cyScreen);
            SelectObject(hdcMem, hBitmap);
            ReleaseDC(hwnd, hdc);

            hFont = CreateFont(iFontHeight, iFontWidth - 5, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DRAFT_QUALITY, FIXED_PITCH | FF_SWISS, TEXT("Fixedsys"));
            SelectObject(hdcMem, hFont);
            DeleteObject(hFont);
            SetBkMode(hdcMem, TRANSPARENT);
            iColumnCount = cxScreen / (iFontWidth * 3 / 2);

            ccChain = (pCharColumn)calloc(iColumnCount, sizeof(CharColumn));
            for (i = 0; i < iColumnCount; i++) {
                init(ccChain + i, cyScreen, (iFontWidth * 3 / 2) * i);
            }
            return 0;

        case WM_TIMER:
            hdc = GetDC(hwnd);
            PatBlt(hdcMem, 0, 0, cxScreen, cyScreen, BLACKNESS);
            for (i = 0; i < iColumnCount; i++) {
                ctn = (ccChain + i)->iStopTimes++ > (ccChain + i)->iMustStopTimes;
                (ccChain + i)->point = (ccChain + i)->head;

                SetTextColor(hdcMem, RGB(255, 255, 255));
                TextOut(hdcMem, (ccChain + i)->x, (ccChain + i)->y, &((ccChain + i)->point->ch), 1);
                j = (ccChain + i)->y;
                (ccChain + i)->point = (ccChain + i)->point->next;
                temp = 0;
                while ((ccChain + i)->point!= (ccChain + i)->head && (ccChain + i)->point->ch) {
                    SetTextColor(hdcMem, RGB(0, 255 - (255 * (temp++) / (ccChain + i)->iStrLen), 0));
                    TextOut(hdcMem, (ccChain + i)->x, j -= iFontHeight, &((ccChain + i)->point->ch), 1);
                    (ccChain + i)->point = (ccChain + i)->point->next;
                }
                if (ctn)
                    (ccChain + i)->iStopTimes = 0;
                else continue;
                (ccChain + i)->y += iFontHeight;
                if ((ccChain + i)->y - (ccChain + i)->iStrLen * iFontHeight > cyScreen) {
                    free((ccChain + i)->current);
                    init(ccChain + i, cyScreen, (iFontWidth * 3 / 2) * i);
                }
                (ccChain + i)->head = (ccChain + i)->head->prev;
                (ccChain + i)->head->ch = randomChar();
            }

            BitBlt(hdc, 0, 0, cxScreen, cyScreen, hdcMem, 0, 0, SRCCOPY);
            ReleaseDC(hwnd, hdc);
            return 0;

        case WM_RBUTTONDOWN:
            KillTimer(hwnd, ID_TIMER);
            return 0;

        case WM_RBUTTONUP:
            SetTimer(hwnd, ID_TIMER, 10, NULL);
            return 0;

        case WM_KEYDOWN:
        case WM_LBUTTONDOWN:
        case WM_DESTROY:
            KillTimer(hwnd, ID_TIMER);
            DeleteObject(hBitmap);
            DeleteDC(hdcMem);
            for (i = 0; i < iColumnCount; i++) {
                free((ccChain + i)->current);
            }
            free(ccChain);
            PostQuitMessage(0);
            return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

1.这个函数是窗口的消息处理函数。根据不同的消息类型进行不同的处理。
当接收到WM_CREATE消息时,获取屏幕的宽度和高度,设置一个定时器,创建内存设备上下文、位图和字体,设置背景模式为透明,计算字符列的数量,分配内存创建字符列数组,并初始化每个字符列。
2.当接收到WM_TIMER消息时,获取窗口的设备上下文,用黑色填充内存设备上下文,遍历每个字符列,根据停止状态和位置更新字符的显示,将内存设备上下文的内容复制到窗口设备上下文。
3.当接收到右键按下消息WM_RBUTTONDOWN时,停止定时器。当接收到右键抬起消息WM_RBUTTONUP时,重新启动定时器。
4.当接收到按键按下消息WM_KEYDOWN、左键按下消息WM_LBUTTONDOWN或窗口销毁消息WM_DESTROY时,停止定时器,删除位图、内存设备上下文,释放每个字符列的链表内存和字符列数组内存,发送退出消息。
5.如果接收到的消息不是上述特定的消息类型,则调用DefWindowProc函数进行默认处理。

三、完整代码

#include <windows.h>

// 定义定时器的 ID
#define ID_TIMER    1

// 定义字符串的最大长度和最小长度
#define STRMAXLEN  25
#define STRMINLEN  8

// 声明窗口过程函数
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

// 定义字符链表结构体
typedef struct tagCharChain {
	struct tagCharChain *prev;  // 指向前一个字符节点的指针
	char ch;  // 字符
	struct tagCharChain *next;  // 指向后一个字符节点的指针
} CharChain, *pCharChain;

// 定义字符列结构体
typedef struct tagCharColumn {
	CharChain *head, *current, *point;  // 分别指向链表头、当前节点、遍历指针
	int x, y, iStrLen;  // x、y 坐标和字符串长度
	int iStopTimes, iMustStopTimes;  // 停止次数和必须停止的次数
} CharColumn, *pCharColumn;

// 主函数入口
int main(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
	// 窗口类名
	static TCHAR szAppName[] = TEXT("matrix");
	HWND hwnd;  // 窗口句柄
	MSG msg;  // 消息结构体
	WNDCLASS wndclass;  // 窗口类结构体

	// 设置窗口类的风格为水平和垂直重绘
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	// 设置窗口过程函数为 WndProc
	wndclass.lpfnWndProc = WndProc;
	// 类的额外字节数为 0
	wndclass.cbClsExtra = 0;
	// 窗口的额外字节数为 0
	wndclass.cbWndExtra = 0;
	// 应用程序实例句柄
	wndclass.hInstance = hInstance;
	// 加载默认图标
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	// 加载默认光标
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	// 设置窗口背景为黑色画刷
	wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	// 菜单名为 NULL,表示没有菜单
	wndclass.lpszMenuName = NULL;
	// 窗口类名
	wndclass.lpszClassName = szAppName;

	// 注册窗口类
	if (!RegisterClass(&wndclass)) {
		// 如果注册失败,弹出错误消息框并返回 0
		MessageBox(NULL, TEXT("此程序必须运行在 NT 下!"), szAppName, MB_ICONERROR);
		return 0;
	}

	// 创建窗口
	hwnd = CreateWindow(szAppName, NULL, WS_DLGFRAME | WS_THICKFRAME | WS_POPUP, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN), NULL, NULL, hInstance, NULL);

	// 显示窗口并更新窗口
	ShowWindow(hwnd, SW_SHOWMAXIMIZED);
	UpdateWindow(hwnd);
	// 隐藏光标
	ShowCursor(FALSE);

	// 使用当前时间作为随机数种子
	srand((int)GetCurrentTime());
	// 消息循环
	while (GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	// 显示光标
	ShowCursor(TRUE);
	return msg.wParam;
}

// 生成随机字符,0 或 1
char randomChar() {
	return (rand() % 2) ? '1' : '0';
}

// 初始化字符列
int init(CharColumn *cc, int cyScreen, int x) {
	int j;
	// 随机生成字符串长度
	cc->iStrLen = rand() % (STRMAXLEN - STRMINLEN) + STRMINLEN;
	// 设置 x 坐标
	cc->x = x + 3;
	// 随机设置 y 坐标或为 0
	cc->y = rand() % 3 ? rand() % cyScreen : 0;
	// 随机生成必须停止的次数
	cc->iMustStopTimes = rand() % 6;
	// 初始化停止次数为 0
	cc->iStopTimes = 0;
	// 分配内存创建字符链表
	cc->head = cc->current = (pCharChain)calloc(cc->iStrLen, sizeof(CharChain));
	for (j = 0; j < cc->iStrLen - 1; j++) {
		// 设置链表节点的前后指针
		cc->current->prev = cc->point;
		cc->current->ch = '\0';
		cc->current->next = cc->current + 1;
		cc->point = cc->current++;
	}
	// 设置最后一个节点的指针
	cc->current->prev = cc->point;
	cc->current->ch = '\0';
	cc->current->next = cc->head;
	cc->head->prev = cc->current;

	cc->current = cc->point = cc->head;
	// 设置链表头节点的字符为随机字符
	cc->head->ch = randomChar();
	return 0;
}

// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
	HDC hdc;  // 设备上下文句柄
	int i, j, temp, ctn;
	static HDC hdcMem;  // 内存设备上下文句柄
	HFONT hFont;  // 字体句柄
	static HBITMAP hBitmap;  // 位图句柄
	static int cxScreen, cyScreen;  // 屏幕宽度和高度
	static int iFontWidth = 10, iFontHeight = 15, iColumnCount;  // 字体宽度、高度和列数
	static CharColumn *ccChain;  // 字符列指针

	switch (message) {
		case WM_CREATE:
			// 获取屏幕宽度和高度
			cxScreen = GetSystemMetrics(SM_CXSCREEN);
			cyScreen = GetSystemMetrics(SM_CYSCREEN);
			// 设置定时器,ID 为 ID_TIMER,时间间隔为 10 毫秒
			SetTimer(hwnd, ID_TIMER, 10, NULL);

			// 获取窗口设备上下文
			hdc = GetDC(hwnd);
			// 创建与窗口设备上下文兼容的内存设备上下文
			hdcMem = CreateCompatibleDC(hdc);
			// 创建与窗口兼容的位图
			hBitmap = CreateCompatibleBitmap(hdc, cxScreen, cyScreen);
			// 选择位图到内存设备上下文
			SelectObject(hdcMem, hBitmap);
			// 释放窗口设备上下文
			ReleaseDC(hwnd, hdc);

			// 创建字体
			hFont = CreateFont(iFontHeight, iFontWidth - 5, 0, 0, FW_BOLD, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DRAFT_QUALITY, FIXED_PITCH | FF_SWISS, TEXT("Fixedsys"));
			// 选择字体到内存设备上下文
			SelectObject(hdcMem, hFont);
			// 删除字体句柄
			DeleteObject(hFont);
			// 设置背景模式为透明
			SetBkMode(hdcMem, TRANSPARENT);
			// 计算列数
			iColumnCount = cxScreen / (iFontWidth * 3 / 2);

			// 分配内存创建字符列数组
			ccChain = (pCharColumn)calloc(iColumnCount, sizeof(CharColumn));
			for (i = 0; i < iColumnCount; i++) {
				// 初始化每个字符列
				init(ccChain + i, cyScreen, (iFontWidth * 3 / 2) * i);
			}
			return 0;

		case WM_TIMER:
			// 获取窗口设备上下文
			hdc = GetDC(hwnd);
			// 用黑色填充内存设备上下文
			PatBlt(hdcMem, 0, 0, cxScreen, cyScreen, BLACKNESS);
			for (i = 0; i < iColumnCount; i++) {
				// 判断是否应该停止
				ctn = (ccChain + i)->iStopTimes++ > (ccChain + i)->iMustStopTimes;
				// 设置遍历指针为链表头
				(ccChain + i)->point = (ccChain + i)->head;

				// 设置文本颜色为白色
				SetTextColor(hdcMem, RGB(255, 255, 255));
				// 在内存设备上下文输出一个字符
				TextOut(hdcMem, (ccChain + i)->x, (ccChain + i)->y, &((ccChain + i)->point->ch), 1);
				j = (ccChain + i)->y;
				// 移动遍历指针到下一个节点
				(ccChain + i)->point = (ccChain + i)->point->next;
				temp = 0;
				while ((ccChain + i)->point != (ccChain + i)->head && (ccChain + i)->point->ch) {
					// 设置文本颜色为渐变绿色
					SetTextColor(hdcMem, RGB(0, 255 - (255 * (temp++) / (ccChain + i)->iStrLen), 0));
					// 在内存设备上下文输出一个字符
					TextOut(hdcMem, (ccChain + i)->x, j -= iFontHeight, &((ccChain + i)->point->ch), 1);
					// 移动遍历指针到下一个节点
					(ccChain + i)->point = (ccChain + i)->point->next;
				}
				if (ctn)
					// 如果应该停止,重置停止次数
					(ccChain + i)->iStopTimes = 0;
				else continue;
				// 更新 y 坐标
				(ccChain + i)->y += iFontHeight;
				if ((ccChain + i)->y - (ccChain + i)->iStrLen * iFontHeight > cyScreen) {
					// 如果超出屏幕,释放链表内存并重新初始化
					free((ccChain + i)->current);
					init(ccChain + i, cyScreen, (iFontWidth * 3 / 2) * i);
				}
				// 链表头指针移动到前一个节点
				(ccChain + i)->head = (ccChain + i)->head->prev;
				// 设置新的随机字符到链表头
				(ccChain + i)->head->ch = randomChar();
			}

			// 将内存设备上下文的内容复制到窗口设备上下文
			BitBlt(hdc, 0, 0, cxScreen, cyScreen, hdcMem, 0, 0, SRCCOPY);
			// 释放窗口设备上下文
			ReleaseDC(hwnd, hdc);
			return 0;

		case WM_RBUTTONDOWN:
			// 右键按下,停止定时器
			KillTimer(hwnd, ID_TIMER);
			return 0;

		case WM_RBUTTONUP:
			// 右键抬起,重新启动定时器
			SetTimer(hwnd, ID_TIMER, 10, NULL);
			return 0;

		case WM_KEYDOWN:
		case WM_LBUTTONDOWN:
		case WM_DESTROY:
			// 按键按下、左键按下或窗口销毁时,停止定时器
			KillTimer(hwnd, ID_TIMER);
			// 删除位图
			DeleteObject(hBitmap);
			// 删除内存设备上下文
			DeleteDC(hdcMem);
			for (i = 0; i < iColumnCount; i++) {
				// 释放每个字符列的链表内存
				free((ccChain + i)->current);
			}
			// 释放字符列数组内存
			free(ccChain);
			// 发送退出消息
			PostQuitMessage(0);
			return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}

四、总结

总体而言,这段代码通过巧妙地运用数据结构、消息处理、图形绘制和随机数生成等技术,创造了一个具有吸引力的动态可视化效果,为 C 语言在图形界面编程方面提供了一个很好的示例。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐