文章目录
环境
使用的是VSCode + MinGW;
配置环境
VSCode写C语言的环境就不讲了,就说一下本篇文章编译的条件吧。
本篇文章需要编译器链接Windows GDI32库,所以如果你用的是VSCode+MinGW,就需要修改task.json文件,使其在链接的时候,链接Window GDI32库。
修改也比较简单,只需要为args数组加上一个字符串"-lgdi32",示例代码如下:
c
"args": [
"-o",
"${fileDirname}\\${fileBasenameNoExtension}",
"${file}",
"-lgdi32"
]
使用库
我们使用windows.h库来实现图形化界面。
头文件如下:
c
#include <windows.h>
windows.h是 Windows 操作系统的核心头文件,它提供了许多与 Windows API 相关的功能和宏定义。
基础概念
句柄
首先我们来了解一个概念,叫句柄。句柄是一种表示、访问或操作资源的引用或标识符。它可以被视为对象或数据结构的抽象表示。简单来说,句柄是指向资源的指针。
在不同的上下文中,句柄可以表示多种类型的资源,比如说:内存句柄、文件句柄、窗口句柄等等许多许多。
句柄通常由操作系统提供和管理,开发者使用句柄来引用和操作资源,而无需了解底层实现的具体细节。句柄的具体实现方式因操作系统而异,可能是一个整数、一个指针或其他形式的标识符。
使用句柄的好处之一是它提供了一种封装和抽象资源的方式,隐藏了底层实现细节,使得资源的使用更安全和高效。另外,句柄也使得多个程序或线程可以共享资源,提高了系统资源的利用率。
程序的入口
我们之前写的C程序控制台入口都是int main(void){},但是当我们使用windows.h库,想要创建图形界面的时候就不可以了,应该使用如下程序入口:
c
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
}
int WINAPI WinMain是Windows程序的入口函数。下面对WinMain函数的参数进行解释:
- HINSTANCE hInstance:当前应用程序的实例句柄。实例是指正在运行的应用程序的唯一标识。这个参数在Windows程序中常常用来标识应用程序以及与其他应用程序进行交互。
- HINSTANCE hPrevInstance:前一个应用程序的实例句柄。在Windows中已经被弃用,现在始终为NULL。
- LPSTR lpCmdLime:命令行参数。在Windows程序中,可以通过命令行传递额外的参数。这个参数是一个指向以空字符终止的字符串的指针,其中包含了命令行参数的文本。
- int nCmdShow:窗口的显示状态。它指示窗口在初始化后应如何显示,比如是否最大化、最小化或正常显示。nCmdShow参数可以采用以下常用值:
- SW_SHOW:显示窗口。
- SW_HIDE:隐藏窗口。
- SW_MAXIMIZE:最大化窗口。
- SW_MINIMIZE:最小化窗口。
创建窗口
定义窗口类
通过窗口类,我们可以实现自定义的窗口行为和外观。
我们首先需要定义一个WNDCLASS结构体变量。
c
WNDCLASS wc = {0};
如上,我们定义了一个名为wc的WNDCLASS结构体变量,并初始化所有成员为0。使用{0}可以将结构体中的所有成员都设置为默认值。
然后我们需要将窗口过程函数的地址赋给WNDCLASS结构体变量的lpfnWndProc成员。窗口过程函数是窗口消息的处理函数,代码如下。
c
wc.lpfnWndProc = WndProc;
然后将当前应用程序的实例句柄赋给WNDCLASS结构体变量的hInstance成员。实例句柄用于标识当前运行的应用程序的实例。
c
wc.hInstance = hInstance;
接着将背景刷子的句柄赋给WNDCLASS结构体变量的hbrBackground成员。
c
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
在这里,使用了预定义的COLOR_BACKGROUND作为背景颜色,通过强制类型转换将其转换为HBRUSH类型。
最后,我们将窗口名赋给WNDCLASS结构体变量的lpszClassName成员。
c
wc.lpszClassName = "MyWinClass";
这里我们将窗口类的名称被设置为"MyWinClass"。
完整代码如下:
c
// 定义窗口类
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = "MyWinClass";
注册窗口类
我们需要使用RegisterClass函数来注册窗口类,该函数需要一个参数,该参数指向包含窗口类信息的WNDCLASS结构体的指针。将窗口类信息传递给函数,以便系统知道如何处理后续创建的窗口。
c
RegisterClass(&wc)
同时,我们应该检查RegisterClass函数的返回值是否为0,也就是是否注册窗口类失败。如果注册失败,返回值为0。
c
// 注册窗口类
if (!RegisterClass(&wc)) {
MessageBox(NULL, "窗口注册失败!", "错误", MB_ICONERROR);
return 1;
}
代码中,如果窗口类注册失败,则弹出一个消息框,显示错误信息。
MessageBox方法的第一个参数NULL表示没有父窗口,第二个参数是消息框的内容,第三个参数是消息框的标题,MB_ICONERROR表示使用错误图标。
最后return 1,作为程序的退出码。这个值将被返回给操作系统,表示程序的执行状态。
通过注册窗口类,我们告知操作系统如何处理后续创建的窗口。如果注册窗口类失败,这通常是因为系统资源不足或窗口类信息错误,导致无法创建窗口。
创建窗口
我们可以通过CreateWindow()方法创建一个窗口实例,并将其句柄保存在变量中。
c
// 创建窗口
HWND hWnd = CreateWindow("MyWinClass", "我的窗口", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, NULL, hInstance, NULL);
HWND是窗口句柄,用于标识窗口。
完整代码
c
#include <windows.h>
// 声明窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 定义窗口类
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = "MyWinClass";
// 注册窗口类
if (!RegisterClass(&wc)) {
MessageBox(NULL, "窗口注册失败!", "错误", MB_ICONERROR);
return 1;
}
// 创建窗口
HWND hWnd = CreateWindow("MyWinClass", "我的窗口", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, NULL, hInstance, NULL);
if (!hWnd) {
MessageBox(NULL, "窗口创建失败!", "错误", MB_ICONERROR);
return 1;
}
// 显示窗口
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// 消息循环
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// 设置字体和背景颜色
HFONT hFont = CreateFont(30, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial");
HFONT hOldFont = (HFONT)SelectObject(hdc, hFont);
SetTextColor(hdc, RGB(0, 0, 0));
SetBkColor(hdc, RGB(255, 255, 255));
// 绘制文本
RECT rect;
GetClientRect(hWnd, &rect);
DrawText(hdc, "Hello World", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
SelectObject(hdc, hOldFont);
DeleteObject(hFont);
EndPaint(hWnd, &ps);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
运行效果
最终我们代码的完整运行效果如图所示。