技术演进中的开发沉思-29 MFC系列:关于win32

不得不提的,是win32程序已经走在淘汰的边缘了,但今天还是想说说它。若把计算机系统比作一座不断翻新的城市,Win32 就像那些承载着城市记忆的老街区。64 位系统的普及确实像拓宽了主干道,Win64 作为新拓宽的车道,能跑更大更重的 "卡车"(处理更大内存、更复杂运算),但老街区的石板路(Win32)依然有它的用处 ------ 那些只需要自行车就能到达的目的地(轻量工具、嵌入式设备),没必要非得开卡车。

目前国产化已经盛行,win32程序如果想在麒麟系统里运行,就得是64位的windows程序。因为麒麟系统用模拟器支持 64 位,更像是城市管理者在老街区旁修了座现代化换乘站。国产化系统需要兼容大量历史软件,就像新城区得对接老城区的供水系统,模拟器不是 "淘汰" 的信号,反而是技术传承的桥梁。当年从 16 位到 32 位过渡时,Windows 也曾保留 DOS 虚拟机,这种兼容背后想必是对用户习惯和历史资产的尊重。

已经过了20多年了,如今再看 Win32 程序的构成要素,它们如精密钟表齿轮,各居其位,驱动着 Windows 世界运转。把 Win32 程序比作一栋房子,开发流程就是从打地基到封顶的过程。就像盖房先规划,写程序也得按步骤来,看这段代码。

cpp 复制代码
#include <windows.h>

// 窗口过程(处理消息)

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

switch(msg) {

case WM_DESTROY:

PostQuitMessage(0);

break;

default:

return DefWindowProc(hWnd, msg, wParam, lParam);

}

return 0;

}

// 主函数(程序入口)

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

WNDCLASSEX wc;

HWND hWnd;

MSG Msg;

// 初始化窗口类(相当于设计房子图纸)

wc.cbSize = sizeof(WNDCLASSEX);

wc.lpfnWndProc = WndProc;

wc.hInstance = hInstance;

wc.lpszClassName = "MyClass";

// 注册窗口类(给房子登记身份)

RegisterClassEx(&wc);

// 创建窗口(搭建房子框架)

hWnd = CreateWindowEx(0, "MyClass", "Win32程序", WS_OVERLAPPEDWINDOW,

CW_USEDEFAULT, CW_USEDEFAULT, 500, 300, NULL, NULL, hInstance, NULL);

ShowWindow(hWnd, nCmdShow);

UpdateWindow(hWnd);

// 消息循环(处理各种请求)

while(GetMessage(&Msg, NULL, 0, 0) > 0) {

TranslateMessage(&Msg);

DispatchMessage(&Msg);

}

return Msg.wParam;

}

所需函数库与头文件就像工匠的工具箱和说明书。#include <windows.h> 如同万能工具箱,里面装着盖房子要用的各种工具(函数)和零件(数据结构)。就像做饭不能没有锅碗瓢盆,写 Win32 程序也离不开这些基础库,它们是和 Windows 系统沟通的桥梁。

消息机制像老办公室的传呼系统。鼠标点击是一张纸条,窗口移动是一封电报,消息循环就是收发员。当你点击按钮,就像寄了封信,WM_COMMAND 消息就是信封上的地址,确保送到正确的地方。

cpp 复制代码
// 消息处理示例,如同前台接待不同客人

case WM_LBUTTONDOWN:

// 处理鼠标左键点击,像接待一位要咨询的客人

MessageBox(hWnd, "鼠标左键被点击了", "提示", MB_OK);

break;

case WM_KEYDOWN:

// 处理键盘按键,如同接到一个电话

if(wParam == VK_ESCAPE) {

DestroyWindow(hWnd);

}

break;

程序结构是房子的骨架。WinMain 是总工程师,窗口类注册是给房子登记身份,消息循环是不停处理各种事务,窗口函数则是具体应对不同情况的前台经理。它们分工协作,让程序有条不紊地运行。

对话框像临时咨询台。模态对话框是 "请稍等" 的牌子,非模态是开放式柜台。创建对话框就像搭个临时摊位,方便和用户互动。

cpp 复制代码
// 创建模态对话框,如同设置一个必须先处理完的咨询点

DialogBox(hInstance, MAKEINTRESOURCE(IDD_MYDIALOG), hWnd, DlgProc);

模块定义文件与资源文件是装修手册。.def 文件规定哪些房间可外人进入,.rc 文件记录窗帘花色、壁画位置等。我曾为调整按钮图标在.rc 里改了一下午,那像素级的较真,是程序员的浪漫。

Windows 程序的生与死像一场宴席。WinMain 启动是宾客入座,WM_CREATE 是摆餐具,消息循环是热闹的聚餐,WM_DESTROY 是道别,PostQuitMessage 是关灯锁门。有次调试时,程序退出了还有线程发消息,就像散席后仍在自饮的醉汉。

空闲时间处理是程序的 "摸鱼时刻"。消息循环没事做时,就像值班人员整理文件,悄悄处理后台任务。

cpp 复制代码
// 空闲时间处理,如同值班人员利用空闲整理资料

while(GetMessage(&Msg, NULL, 0, 0) > 0) {

TranslateMessage(&Msg);

DispatchMessage(&Msg);

// 检查是否有空闲时间

while(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) == 0) {

// 处理空闲任务,像趁机整理文件

DoIdleTasks();

if(需要退出空闲处理) break;

}

}

Console 程序和 DOS 程序,如同传呼机与智能手机。DOS 程序是单车道自行车,Win32 控制台程序是带导航的汽车,能在自己车道行驶,还能听广播(系统消息)。

进程与线程是公司和员工。进程是独立公司,有自己的地址和执照;线程是员工,有的接待客户(UI 线程),有的埋头算账(后台线程)。优先级像任务紧急程度,给实时数据采集线程高优先级,如同给急诊医生开绿灯。

cpp 复制代码
// 创建线程,如同招聘一名员工去完成特定工作

HANDLE hThread = CreateThread(

NULL, // 默认安全属性

0, // 默认栈大小

MyThreadFunc, // 线程要执行的函数(员工的工作内容)

NULL, // 传递给线程的参数

0, // 立即执行

&dwThreadId); // 线程ID

这些代码和概念,是我们与机器对话的语法,是用逻辑编织的咒语。当 Windows 系统念起这些咒语,冰冷的硅晶片有了温度,像素开始跳舞,这就是 Win32 开发的动人之处。

现在回头看,Win32 更像是位退居二线的老师傅。它不再冲锋在前,但年轻时攒下的 "手艺"(消息机制、窗口管理思想)早已融入现代编程的基因里。就像活字印刷术虽被数字印刷取代,但其 "模块化" 理念却启发了计算机编程。或许某天 Win32 会彻底淡出视野,但那些在调试窗口函数时悟到的逻辑思维,永远不会过时。这也是我想在这里梳理之前经历技术的意义,或许年轻的程序员根本就没层了解过,但想说的如今很多的技术要点,其实源于早期win32开发的一些脉络,从诞生到运行再到结束,每个环节都有其意义。未完待续.......