• 创建你的第一个Windows程序


    第一个 Windows 程序

    * 纯净版

    #include
    
    int WINAPI WinMain(
    	HINSTANCE hInstance,
    	HINSTANCE hPrevInstance,
    	LPSTR lpCmdLine,
    	int iShowCmd
    )
    {
    	MessageBox(NULL, TEXT("Hello, Windows!"), TEXT("HelloMsg"), 1);
    	return 0;
    }
    

    * 注释版

    #include
    
    int WINAPI WinMain(
    	HINSTANCE hInstance,		// 当前实例的句柄,唯一标识了我们这个程序(在Windows程序中,句柄就是一个数值,用来标识某个东西)
    	HINSTANCE hPrevInstance,	// 前一个实例的句柄(在Win32中,此概念已弃用,因此该参数总是NULL)
    	LPSTR lpCmdLine,			// 命令行参数
    	int iShowCmd				// 指明程序最初如何显示(正常显示、最大化到全屏、最小化到任务栏)
    )
    {
    	MessageBox(					// 函数作用:弹出一个小窗口,主要用来显示一些短消息
    		NULL,					// 窗口句柄,唯一标识这个窗口
    		TEXT("Hello, Windows!"),// 要在信息框里面出现的文本字符串
    		TEXT("HelloMsg"),		// 要在标题栏上显示的文本字符串
    		1						// 选择按钮的类型
    	);
    	return 0;
    }
    
    /*
    Windows API 中"类型"的命名规则:
    
    	HINSTANCE: H + INSTANCE == handle + instance == 实例句柄
    	LPSTR: L + P + STR == long + pointer + str == 长字符指针
    	i == int
    	sz == 以零结尾的字符串
    	c == const == 常量
    	s == static == 静态
    
    */
    

    编码问题

    • 早期 Windows 使用 ASCII 进行编码,对于美国自己来用,基本上足够了

    • 随着计算机的发展,世界各国都想要自己的文字能够在计算机上表示

    • 为了统一世界各国的字符编码问题,Unicode 诞生了

    • 它是通过增加位的方式来扩充,由原来的7位,变成现在的16位(方案不同,会有所出入,但只会更多,不会更少)

    • 可容纳的符号,由原来的27=128,变成现在的216=65536(方案不同,会有所出入,但只会更多,不会更少)

    • 使用 Unicode 也有缺点:就是用 Unicode 写的字符占用的空间,会比用 ASCII 写的字符所占用的空间大2倍

    • 为此,人们希望最好能够创建两个版本的程序,让不同需求的人,拿最适合自己的那一个版本即可

    • 于是,有了以下的设计:

    使用 TCHAR.H 头文件,这个头文件提供了一系列的类型或函数的替代名称

    一个命名为 _UNICODE 的标识符被定义了,在生成代码时

    TCHAR 就自动变成 wchar_t_tprintf 就自动变成 wprintf(w:width)

    一个名为 _UNICODE 的标识符没有被定义,在生成代码时

    TCHAR 就自动变成 char_tprintf 就自动变成 printf

    同样的道理,对于字符串而言,使用 _T("hello world")_TEXT("hello world") 也能自动变化

    #include
    #include
    
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
    {
    	MessageBox(NULL, _T("你好,窗口!"), _T("信息窗口"), 0);
    	return 0;
    }
    

    创建窗口

    运行代码的时候:

    • 打开项目属性。
    • 导航到“链接器”>“输入”>“附加依赖项”。
    • 在“附加依赖项”字段中添加 winmm.lib(如果列表中还没有的话)

    * 纯净版

    #include
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
    
    int WINAPI WinMain(
    	HINSTANCE hInstance,
    	HINSTANCE hPrevInstance,
    	PSTR szCmdLine,
    	int iCmdShow
    )
    {
    	static TCHAR szAppName[] = TEXT("HelloWin");
    	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_APPSTARTING);
    	wndclass.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
    	wndclass.lpszMenuName	= NULL;
    	wndclass.lpszClassName	= szAppName;
    
    	if (!RegisterClass(&wndclass))
    	{
    		MessageBox(NULL, TEXT("This program requires Windwos!"), szAppName, MB_ICONERROR);
    		return 0;
    	}
    
    	hwnd = CreateWindow(
    		szAppName,
    		TEXT("The Hello Program"),
    		WS_OVERLAPPEDWINDOW,
    		CW_USEDEFAULT,
    		CW_USEDEFAULT,
    		CW_USEDEFAULT,
    		CW_USEDEFAULT,
    		NULL,
    		NULL,
    		hInstance,
    		NULL
    	);
    
    	ShowWindow(hwnd, iCmdShow);
    	UpdateWindow(hwnd);
    
    	while (GetMessage(&msg, NULL, 0, 0))
    	{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    	}
    	return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
    	HDC hdc;
    	PAINTSTRUCT ps;
    	RECT rect;
    
    	switch (message)
    	{
    	case WM_CREATE:
    		PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC);
    		return 0;
    
    	case WM_PAINT:
    		hdc = BeginPaint(hwnd, &ps);
    
    		GetClientRect(hwnd, &rect);
    
    		DrawText(hdc, TEXT("Hello, Windows!"), -1, &rect,
    			DT_SINGLELINE | DT_CENTER | DT_VCENTER);
    
    		EndPaint(hwnd, &ps);
    		return 0;
    
    	case WM_DESTROY:
    		PostQuitMessage(0);
    		return 0;
    	}
    	return DefWindowProc(hwnd, message, wParam, lParam);
    }
    

    * 注释版

    #include
    
    /* ------------------------------------------------------------------
    	要创建窗口,首先需要注册一个窗口类,而窗口类又需要"窗口过程"来处理窗口消息
    	名词解释:窗口过程,是一个函数,用于处理发送到类的所有窗口或发布到所有窗口的所有消息
    	窗口过程是给 Windows 回调用的,它必须遵循规定的格式
    	对窗口过程的子程序名并没有规定,对 Windows 来说,窗口过程的地址才是惟一需要的
    ------------------------------------------------------------------ */
    
    
    // 声明"窗口过程"(窗口处理函数),用于处理窗口消息
    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);  // 窗口过程的名字,是可以自定义命名的
    
    
    // Windows 程序的入口点
    int WINAPI WinMain(
    	HINSTANCE hInstance,		// 当前实例的句柄
    	HINSTANCE hPrevInstance,	// 前一个实例的句柄(在Win32中,此参数总是NULL)
    	PSTR szCmdLine,				// 命令行参数
    	int iCmdShow				// 控制窗口如何显示
    )
    {
    	static TCHAR szAppName[] = TEXT("HelloWin");	// 窗口类名
    	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_APPSTARTING);	// 加载默认光标
    	wndclass.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);	// 背景画刷
    	wndclass.lpszMenuName	= NULL;									// 菜单名(这里没有使用)
    	wndclass.lpszClassName	= szAppName;							// 窗口类名
    
    	// 注册窗口类
    	if (!RegisterClass(&wndclass))
    	{
    		MessageBox(
    			NULL,
    			TEXT("This program requires Windwos!"),
    			szAppName,
    			MB_ICONERROR
    		);
    		return 0;
    	}
    
    	// 创建窗口
    	hwnd = CreateWindow(
    		szAppName,					// 窗口类名
    		TEXT("The Hello Program"),	// 窗口标题
    		WS_OVERLAPPEDWINDOW,		// 窗口风格
    		CW_USEDEFAULT,				// x坐标(使用默认值)
    		CW_USEDEFAULT,				// y坐标(使用默认值)
    		CW_USEDEFAULT,				// 宽度(使用默认值)
    		CW_USEDEFAULT,				// 高度(使用默认值)
    		NULL,						// 父窗口句柄(无父窗口)
    		NULL,						// 菜单句柄(无菜单)
    		hInstance,					// 应用实例句柄
    		NULL						// 创建参数(这里不使用)
    	);
    
    	// 显示并更新窗口
    	ShowWindow(hwnd, iCmdShow);
    	UpdateWindow(hwnd);
    
    	// 消息循环
    	while (GetMessage(&msg, NULL, 0, 0))
    	{
    		TranslateMessage(&msg);		// 翻译消息(如键盘消息到字符消息)
    		DispatchMessage(&msg);		// 分发消息到窗口过程(窗口处理函数)
    	}
    	return msg.wParam;
    }
    
    
    // 实现窗口过程(窗口处理函数)
    LRESULT CALLBACK WndProc(
    	HWND hwnd,			// 窗口句柄
    	UINT message,		// 消息ID
    	WPARAM wParam,		// 消息参数(对于某些消息,它表示特定的信息)
    	LPARAM lParam		// 消息参数(对于某些消息,它通常是一个指向结构体的指针)
    )
    {
    	HDC hdc;			// 设备上下文句柄,用于绘图操作
    	PAINTSTRUCT ps;		// 绘图结构体,用于 BeginPaint 和 EndPaint 函数
    	RECT rect;			// 矩形区域,用于描述窗口的绘图区域
    
    	switch (message)
    	{
    	case WM_CREATE:						// 窗口创建消息
    		PlaySound(						// 播放声音文件
    			TEXT("hellowin.wav"),		// 声音文件的路径
    			NULL,
    			SND_FILENAME | SND_ASYNC	// SND_FILENAME表示通过文件名播放声音,SND_ASYNC表示异步播放(不阻塞程序)
    		);
    		return 0;
    
    	case WM_PAINT:						// 窗口绘图消息
    		hdc = BeginPaint(hwnd, &ps);	// 开始绘图
    
    		GetClientRect(hwnd, &rect);		// 获取窗口客户区大小
    
    		DrawText(
    			hdc,
    			TEXT("Hello, Windows!"),	// TEXT("Hello, Windows!"): 要绘制的文本字符串
    			-1,							// -1: 字符串中的字符数。如果为-1,则函数会自动计算字符串长度 
    			&rect,						// &rect: RECT结构体的指针,指定文本绘制的区域。文本将在这个矩形区域内被格式化
    			DT_SINGLELINE | DT_CENTER | DT_VCENTER
    			// DT_SINGLELINE | DT_CENTER | DT_VCENTER: 文本绘制选项
    			// DT_SINGLELINE 表示文本将被格式化为单行
    			// DT_CENTER 表示文本将在指定的矩形区域内水平居中
    			// DT_VCENTER 表示文本将在指定的矩形区域内垂直居中
    		);
    
    		EndPaint(hwnd, &ps);			// 结束绘图
    		return 0;
    
    	case WM_DESTROY:					// 窗口销毁消息
    		PostQuitMessage(0);				// 发出退出消息到消息队列
    		return 0;
    	}
    
    	// 如果消息没有被窗口过程(WndProc)显式处理,则调用默认的窗口处理函数(DefWindowProc)来处理它
    	return DefWindowProc(
    		hwnd,		// hwnd: 窗口句柄,表示哪个窗口接收到了消息
    		message,	// message: 消息ID,标识了要处理的消息类型
    		wParam,		// wParam: 消息的参数之一,它的具体含义取决于消息类型
    		lParam		// lParam: 消息的参数之二,它的具体含义也取决于消息类型
    	);
    }
    

    常见的标识符前缀

    前缀常量
    CS类风格选项
    CW创建窗口选项
    DT文本绘制选项
    IDI图标的 ID 号
    IDC光标的 ID 号
    MB消息框选项
    SND声音选项
    WM窗口消息
    WS窗口风格

    常用的变量名前缀

    前缀数据类型
    cchar 或 WCHAR 或 TCHAR
    byBYTE(无符号字符)
    nshort
    iint
    x, yint, 表示 x 坐标和 y 坐标
    cx, cyint, 表示 x 或 y 的长度,c 表示 count (计数)
    B 或 fBOOL(int); f 表示 flag
    wWORD(无符号短整型)
    lLONG(长整型)
    dwDWORD(无符号长整型)
    fn函数
    s字符串
    sz以零结束的字符串
    h句柄
    p指针
  • 相关阅读:
    探索CPU的黑盒子:解密指令执行的秘密
    nginx 常用命令 |升级到1.20.1版本 | 如何更换 Nginx SSL 证书
    vue+express、gitee pm2部署轻量服务器(20230923)
    不同设备的请求头信息UserAgent,Headers
    maxwell学习笔记
    [python 刷题] 242 Valid Anagram
    Python 中的类与继承
    C练题笔记之:Leetcode-827. 最大人工岛
    webpack打包 - webpack篇
    探索 Google Guava
  • 原文地址:https://blog.csdn.net/code_stream/article/details/139705654