GDI+ 是 GDI (Graphics Device Interface) 的后继者,它为 Windows 应用程序提供了更强大、更丰富的二维图形功能,虽然 GDI+ 的原生 API 是 C++ 风格的,但它完全可以被 C 语言调用。

GDI+ 的核心概念
在使用 GDI+ 之前,你需要理解几个核心对象:
-
GDI+ Startup (GDI+ 初始化):
- GDI+ 不是一个标准的 Windows 库,你需要显式地初始化和关闭它。
- 初始化通过
GdiplusStartup函数完成,它会返回一个GdiplusStartupInput结构体和一个ULONG_PTR类型的GdiplusToken,这个Token必须在程序结束时传给GdiplusShutdown来正确清理资源。
-
Graphics (图形对象):
- 这是 GDI+ 的核心,代表一个绘图表面,你通常用它来绘制线条、形状、文本和图像。
- 它需要一个设备上下文作为构造参数,例如从窗口句柄
HWND获取的HDC。
-
Gdiplus::GdiplusBase (基类):
(图片来源网络,侵删)- GDI+ 中的大多数类(如
Bitmap,Font,Pen,Brush)都继承自这个基类,它重载了new和delete操作符,使用内部引用计数来管理内存。 - 关键点: 你不能直接用 C 的
free()或delete来释放这些对象,必须调用它们的Dispose()方法,或者在它们离开作用域时让析构函数自动释放。
- GDI+ 中的大多数类(如
-
Pen (画笔):
用于绘制线条、曲线和形状的轮廓,你可以定义它的颜色、宽度和样式(实线、虚线等)。
-
Brush (画刷):
- 用于填充形状(如矩形、椭圆)的内部,有多种画刷:
SolidBrush: 纯色填充。LinearGradientBrush: 线性渐变填充。TextureBrush: 使用图像进行纹理填充。
- 用于填充形状(如矩形、椭圆)的内部,有多种画刷:
-
Font (字体):
用于定义文本的样式(字体名称、大小、粗细等)。
-
Bitmap (位图):
- 代表一个图像,你可以从文件加载,也可以在内存中创建,然后绘制到
Graphics对象上。
- 代表一个图像,你可以从文件加载,也可以在内存中创建,然后绘制到
C 语言环境设置
要在 C 语言项目中使用 GDI+,你需要做以下准备:
-
包含头文件:
#include <windows.h> #include <gdiplus.h> // 使用 GDI+ 的命名空间,简化代码 using namespace Gdiplus;
-
链接库文件: 你需要告诉链接器链接
gdiplus.lib。- 在 Visual Studio 中:
- 右键点击你的项目 -> "属性" (Properties)。
- 转到 "配置属性" (Configuration Properties) -> "链接器" (Linker) -> "输入" (Input)。
- 在 "附加依赖项" (Additional Dependencies) 字段中,添加
gdiplus.lib。
- 在命令行编译时:
cl your_code.c /link gdiplus.lib
- 在 Visual Studio 中:
-
运行时库: GDI+ 需要一个 DLL 文件
gdiplus.dll,这个文件在大多数现代 Windows 系统中都自带,如果你的程序需要在旧系统上运行,可能需要将此 DLL 打包到你的程序目录中。
完整 C 语言示例:在窗口上绘制图形
下面是一个完整的 C 语言示例,它创建一个简单的窗口,并在窗口上使用 GDI+ 绘制一个矩形、一个椭圆和一些文本。
main.c
#include <windows.h>
#include <gdiplus.h>
#include <stdio.h>
// 使用 GDI+ 命名空间
using namespace Gdiplus;
// GDI+ 全局变量
ULONG_PTR gdiplusToken;
// 窗口过程函数
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// 1. 初始化 GDI+
GdiplusStartupInput gdiplusStartupInput;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
// 2. 注册窗口类
const wchar_t CLASS_NAME[] = L"GDIPlus Demo Window";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClass(&wc);
// 3. 创建窗口
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"GDI+ in C Language", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if (hwnd == NULL) {
return 0;
}
ShowWindow(hwnd, nCmdShow);
// 4. 消息循环
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// 5. 关闭 GDI+
GdiplusShutdown(gdiplusToken);
return 0;
}
// 窗口过程
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// 创建 GDI+ Graphics 对象
// Graphics(hdc) 构造函数会使用传入的 HDC
Graphics graphics(hdc);
// 6. 绘制图形
// a. 绘制一个红色矩形
SolidBrush redBrush(Color(255, 255, 0, 0)); // ARGB: Alpha, Red, Green, Blue
graphics.FillRectangle(&redBrush, 50, 50, 200, 100);
// b. 绘制一个蓝色轮廓的椭圆
Pen bluePen(Color(255, 0, 0, 255), 5); // 颜色, 宽度
graphics.DrawEllipse(&bluePen, 100, 200, 250, 150);
// c. 绘制一段文本
FontFamily fontFamily(L"Arial");
Font font(&fontFamily, 24, FontStyleRegular);
SolidBrush blackBrush(Color(255, 0, 0, 0));
graphics.DrawString(L"Hello GDI+ from C!", -1, &font, PointF(50.0f, 400.0f), &blackBrush);
// 7. 释放 GDI+ 对象
// GDI+ 对象通过析构函数自动释放,因为它们是栈上的对象。
// 如果是用 new 创建的,则需要调用 delete。
redBrush.Dispose();
bluePen.Dispose();
font.Dispose();
blackBrush.Dispose();
EndPaint(hwnd, &ps);
}
return 0;
case WM_DESTROY: {
PostQuitMessage(0);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
如何编译和运行
-
使用 Visual Studio:
- 创建一个新的 "Windows 桌面应用程序" 项目(选择 C++ 模板,但你可以把
.cpp文件重命名为.c)。 - 将上面的代码复制到你的
.c文件中。 - 按照上面的步骤,在项目属性中链接
gdiplus.lib。 - 编译并运行。
- 创建一个新的 "Windows 桌面应用程序" 项目(选择 C++ 模板,但你可以把
-
使用 MinGW (gcc):
- 将代码保存为
main.c。 - 打开命令行,运行以下命令:
gcc main.c -o gdiplus_demo.exe -mwindows -lgdiplus
mwindows: 链接 Windows 子系统,避免出现控制台窗口。lgdiplus: 链接 GDI+ 库。
- 将代码保存为
运行后,你将看到一个窗口,里面有一个红色矩形、一个蓝色椭圆和一行黑色文本。
关键注意事项
-
内存管理:
- 务必使用
Dispose(): 如果你使用new动态创建 GDI+ 对象(Pen* p = new Pen(...)),必须使用delete p;来释放,但最佳实践是创建在栈上,让它们在作用域结束时自动析构和释放,如示例所示。 - 不要混用 GDI 和 GDI+: 尽量不要在同一个
HDC上混用 GDI 函数(如Rectangle)和 GDI+ 对象,GDI+ 会锁定 GDI 资源,混用可能导致死锁或不可预测的行为,一个常见的模式是:在WM_PAINT中获取HDC,创建Graphics对象,完成所有 GDI+ 绘制,然后让Graphics和所有画笔/画刷对象离开作用域自动释放,最后调用EndPaint。
- 务必使用
-
命名空间:
- 在 C++ 中,
using namespace Gdiplus;是一个方便的写法,在纯 C 语言中,你不能使用using,你必须使用完全限定名,Gdiplus::Graphics graphics(hdc);,GDI+ 的头文件是为 C++ 设计的,在 C 语言中直接使用可能会遇到一些兼容性问题,在实际的 C 语言项目中,最简单的方法还是将文件作为 C++ 编译(.cpp扩展名),这样就可以使用using声明了。
- 在 C++ 中,
-
错误处理:
- GDI+ 函数通常会返回一个
Status枚举值(如Ok,GenericError,InvalidParameter),在实际应用中,你应该检查这些返回值以确保操作成功。
- GDI+ 函数通常会返回一个
GDI+ 为 C 语言开发者提供了强大而灵活的 2D 图形能力,虽然其 API 设计偏向 C++,但通过正确的设置(链接库、包含头文件)和遵循其内存管理规则(使用 Dispose 或让对象自动析构),你就可以在 C 语言程序中充分利用 GDI+ 的功能来创建丰富的用户界面和图形应用。
