资讯动态

Windows-classic-samples 中 Direct2D Hello World 示例解析:用 Direct2D 绘制第一行文本的完整实战指南

发布时间:2026/10/16 6:02:36 来源:尧图企业网站定制
示例工程【免费下载链接】Windows-classic-samplesThis repo contains samples that demonstrate the API used in Windows classic desktop applications.项目地址https://gitcode.com/gh_mirrors/wi/Windows-classic-samples点击查看免费下载本文以 Windows-classic-samples 仓库中的 Direct2DHelloWorld 示例 README 为骨架结合其完整源码逐行剖析说明如何用 C 通过 Direct2D 与 DirectWrite 在 Win32 窗口上渲染出 Hello, World! 文本。读完本文你将掌握 Direct2D 应用的标准骨架设备无关资源与设备相关资源的创建与销毁、渲染目标的建立、文本格式的定义、窗口消息驱动的绘制流程以及 DPI 感知清单的声明方式可直接迁移到自己的桌面图形应用中。示例概览一个最小可用的 Direct2D 应用该示例的目标非常朴素创建一个 Win32 窗口在白色背景上居中以黑色 Verdana 字体绘制一行文字 Hello, World!。它是 multimedia/Direct2D 目录下众多 Direct2D 示例如 SimpleDirect2DApplication、TextAnimationSample 等中最基础的一个恰好完整覆盖了 Direct2D 应用的最小闭环创建Direct2D 工厂ID2D1Factory——设备无关资源创建DirectWrite 工厂IDWriteFactory与文本格式IDWriteTextFormat——设备无关资源基于 HWND 创建渲染目标ID2D1HwndRenderTarget与纯色画刷ID2D1SolidColorBrush——设备相关资源通过DrawText完成一次绘制并在WM_PAINT、WM_SIZE、WM_DISPLAYCHANGE等消息驱动下持续更新画面通过内嵌清单声明 DPI 感知保证窗口尺寸与文本在系统 DPI 下不失真。示例为纯 C 实现不依赖任何第三方库链接库仅为系统自带的d2d1.lib、dwrite.lib等是学习 Direct2D 编程模型的最佳起点。文件清单与各自职责示例目录Samples/Win7Samples/multimedia/Direct2D/Direct2DHelloWorld/包含 6 个文件README 已逐一说明其用途文件职责DeclareDPIAware.manifest声明示例应用程序为 DPI-aware感知系统 DPI的清单文件Direct2DHelloWorld.cpp包含应用程序入口点WinMain与DemoApp类的全部实现Direct2DHelloWorld.hDemoApp类的头文件含类声明、SafeRelease等辅助宏Direct2DHelloWorld.sln解决方案文件Visual Studio 2008 格式Format Version 10.00Direct2DHelloWorld.vcproj示例的项目文件定义配置、链接库与清单嵌入方式工程文件本身是 Visual C 项目ProjectTypeVisual CVersion9.00同时支持Win32与x64两个平台并预置了Debug / Release四种组合配置Direct2DHelloWorld.vcproj解决方案文件也对应映射了这四种配置Direct2DHelloWorld.sln。前置条件根据 README 的 Prerequisites 一节构建并运行该示例需要Microsoft Windows 7Direct2D 原生支持的系统起点此后各版本 Windows 亦兼容Windows SDK for Windows 7 与 .NET Framework 3.5 Service Pack 1用于提供d2d1.h、dwrite.h等头文件与d2d1.lib、dwrite.lib等导入库。仓库根 README.md 也补充说明这些示例大多面向 Windows 7、Windows 8.1 与 Windows Server 2012 R2 编写多数情况下在更高版本系统上同样可以运行如果示例无法按预期工作很可能是平台、版本或特定 SKU 的限制。对于 Direct2D 而言从 Windows 7 到 Windows 11 的桌面系统均可使用。两种构建方式README 给出了命令行与 Visual Studio 两种官方构建路径以下如实保留并补充细节。方式一命令行构建打开“命令提示符”窗口切换到示例目录即本仓库的Samples/Win7Samples/multimedia/Direct2D/Direct2DHelloWorld/执行msbuild Direct2DHelloWorld.sln。需要注意的是msbuild需来自已安装的 .NET Framework 3.5 或 Visual Studio 工具链在后续版本的 Visual Studio 中也可使用“开发者命令提示符”Developer Command Prompt来调用。方式二Visual Studio 2008推荐方式打开 Windows 资源管理器定位到示例目录双击.sln解决方案文件的图标在 Visual Studio 中打开在“生成”Build菜单中点击“生成解决方案”Build Solution。生成的应用程序默认位于工程的\Debug或\Release目录下Direct2DHelloWorld.vcproj 中OutputDirectory指定为Debug/Releasex64 配置则输出到$(PlatformName)\$(ConfigurationName)。双击运行生成的Direct2DHelloWorld.exe即可看到白底居中的 Hello, World! 文本。程序架构DemoApp 类与资源生命周期示例的 UI 逻辑全部封装在DemoApp类中。从 Direct2DHelloWorld.h 的类声明可以看到类成员按 Direct2D 经典的设备无关 / 设备相关两类资源组织class DemoApp { public: DemoApp(); ~DemoApp(); HRESULT Initialize(); void RunMessageLoop(); private: HRESULT CreateDeviceIndependentResources(); HRESULT CreateDeviceResources(); void DiscardDeviceResources(); HRESULT OnRender(); void OnResize(UINT width, UINT height); static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); private: HWND m_hwnd; ID2D1Factory *m_pD2DFactory; // Direct2D 工厂设备无关 IWICImagingFactory *m_pWICFactory; // WIC 图像工厂声明于头文件本示例未实际创建 IDWriteFactory *m_pDWriteFactory; // DirectWrite 工厂设备无关 ID2D1HwndRenderTarget *m_pRenderTarget; // 渲染目标设备相关 IDWriteTextFormat *m_pTextFormat; // 文本格式设备无关 ID2D1SolidColorBrush *m_pBlackBrush; // 黑色纯色画刷设备相关 };DemoApp构造函数将全部指针初始化为NULLDirect2DHelloWorld.cpp析构函数则通过SafeRelease依次释放所有接口Direct2DHelloWorld.cpp。SafeRelease是定义在头文件中的模板辅助函数Direct2DHelloWorld.h先判空再调用Release()并将指针置NULL避免重复释放。这是所有 COM 风格接口的规范释放模式。入口点WinMain程序从WinMain开始Direct2DHelloWorld.cppint WINAPI WinMain(...) { // 即使 HeapSetInformation 失败也继续运行 HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (SUCCEEDED(CoInitialize(NULL))) { { DemoApp app; if (SUCCEEDED(app.Initialize())) { app.RunMessageLoop(); } } CoUninitialize(); } return 0; }入口流程有三件事值得注意HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0)启用堆损坏自动终止增强健壮性代码注释明确说明即使失败也忽略返回值继续运行CoInitialize(NULL)Direct2D/DirectWrite 接口基于 COM必须在单线程上初始化 COM 后才可使用结束前用CoUninitialize配对释放DemoApp被定义在局部作用域中确保其析构即资源释放发生在CoUninitialize之前顺序正确。设备无关资源的创建CreateDeviceIndependentResourcesDirect2D 的编程模型把资源严格区分为两类设备无关资源device-independent生命周期覆盖应用全程不依赖具体显卡/渲染设备典型代表是 Direct2D 工厂、DirectWrite 工厂与文本格式设备相关资源device-dependent绑定到具体 Direct3D 设备典型代表是渲染目标与画刷设备丢失如显示器切换、远程桌面、显卡拔出时需要重建。DemoApp::Initialize首先调用CreateDeviceIndependentResourcesDirect2DHelloWorld.cpp其实现位于 Direct2DHelloWorld.cpp按顺序完成三步第一步创建 Direct2D 工厂hr D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, m_pD2DFactory);D2D1CreateFactory是 Direct2D 的全局入口函数参数D2D1_FACTORY_TYPE_SINGLE_THREADED表示工厂及其创建的对象仅在创建它的线程上使用示例的应用逻辑全在 UI 线程因此选单线程类型最合理。工厂对象随后承担创建渲染目标、获取系统 DPI 等职责。第二步创建 DirectWrite 工厂hr DWriteCreateFactory( DWRITE_FACTORY_TYPE_SHARED, __uuidof(m_pDWriteFactory), reinterpret_castIUnknown **(m_pDWriteFactory));DWriteCreateFactory是 DirectWrite 的全局入口DWRITE_FACTORY_TYPE_SHARED表示共享工厂内部可复用字体缓存等资源。注意这里以__uuidof取得IDWriteFactory的 IID并通过IUnknown**转型完成 QueryInterface 式的创建。第三步创建文本格式并设置对齐static const WCHAR msc_fontName[] LVerdana; static const FLOAT msc_fontSize 50; hr m_pDWriteFactory-CreateTextFormat( msc_fontName, // 字体族Verdana NULL, // 字体集合NULL 表示使用系统默认字体集合 DWRITE_FONT_WEIGHT_NORMAL, // 字重常规 DWRITE_FONT_STYLE_NORMAL, // 样式常规 DWRITE_FONT_STRETCH_NORMAL, // 拉伸常规 msc_fontSize, // 字号50DIP L, // 区域设置空字符串表示使用系统默认 locale m_pTextFormat); // 水平居中 m_pTextFormat-SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); // 垂直居中 m_pTextFormat-SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);IDWriteTextFormat封装了字体族、字重、样式、拉伸、字号、locale、对齐方式等排版特征是 DirectWrite 文本绘制的核心参数对象。示例用SetTextAlignment与SetParagraphAlignment实现文字在窗口中水平和垂直双居中。这里的字号 50 以 DIPDevice Independent Pixel设备无关像素为单位与系统 DPI 配合后可保证不同缩放比例下的清晰度。窗口创建与 DPI 缩放设备无关资源就绪后Initialize注册窗口类并创建窗口Direct2DHelloWorld.cppWNDCLASSEX wcex { sizeof(WNDCLASSEX) }; wcex.style CS_HREDRAW | CS_VREDRAW; // 窗口尺寸变化时重绘 wcex.lpfnWndProc DemoApp::WndProc; wcex.cbWndExtra sizeof(LONG_PTR); // 预留空间存放 this 指针 wcex.hInstance HINST_THISCOMPONENT; wcex.hCursor LoadCursor(NULL, IDC_ARROW); wcex.lpszClassName LD2DDemoApp; RegisterClassEx(wcex);其中HINST_THISCOMPONENT是头文件里通过__ImageBase取得的当前模块实例句柄Direct2DHelloWorld.h。创建窗口时有一个典型的 DPI 处理细节——CreateWindow的尺寸参数以像素为单位因此要先从工厂读取系统 DPI 并按比例换算FLOAT dpiX, dpiY; m_pD2DFactory-GetDesktopDpi(dpiX, dpiY); m_hwnd CreateWindow( LD2DDemoApp, LDirect2D Demo Application, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, static_castUINT(ceil(640.f * dpiX / 96.f)), // 设计尺寸 640×480 static_castUINT(ceil(480.f * dpiY / 96.f)), // 按 DPI 放大 NULL, NULL, HINST_THISCOMPONENT, this); // lpParam 传入 thisGetDesktopDpi返回系统每英寸像素数96 为 100% 缩放的基准示例据此把逻辑上的 640×480 窗口放大到实际像素尺寸使窗口在不同 DPI 设置下物理大小一致。窗口创建成功后会立即ShowWindow与UpdateWindow显示窗口。CreateWindow的最后一个参数this会被传入WM_CREATE消息的CREATESTRUCT.lpCreateParamsWndProc在WM_CREATE中通过SetWindowLongPtrW(GWLP_USERDATA, ...)把DemoApp*存进窗口的 user dataDirect2DHelloWorld.cpp此后所有消息都能用GetWindowLongPtrW取回实例指针。这是 Win32 中面向对象化窗口过程的标准技巧。设备相关资源的创建与销毁创建渲染目标与画刷CreateDeviceResourcesDirect2DHelloWorld.cpp在首次渲染时才按需创建设备相关资源RECT rc; GetClientRect(m_hwnd, rc); D2D1_SIZE_U size D2D1::SizeU(rc.right - rc.left, rc.bottom - rc.top); hr m_pD2DFactory-CreateHwndRenderTarget( D2D1::RenderTargetProperties(), // 默认渲染目标属性 D2D1::HwndRenderTargetProperties(m_hwnd, size), // 绑定窗口与客户区尺寸 m_pRenderTarget); if (SUCCEEDED(hr)) { hr m_pRenderTarget-CreateSolidColorBrush( D2D1::ColorF(D2D1::ColorF::Black), // 黑色画刷 m_pBlackBrush); }要点如下CreateHwndRenderTarget把渲染目标直接绑定到 HWNDDirect2D 会在内部创建 Direct3D 交换链把绘制内容呈现到窗口客户区尺寸使用客户区client area大小D2D1::SizeU与D2D1::HwndRenderTargetProperties都是d2d1helper.h提供的便捷构造辅助D2D1::ColorF::Black是预定义颜色枚举也可用D2D1::ColorF(0, 0, 0)指定 RGB。代码注释还点明了设计意图设备相关资源全部集中在这里创建以便 Direct3D 设备丢失显示器切换、远程会话、显卡移除等时统一重建。设备丢失处理DiscardDeviceResourcesvoid DemoApp::DiscardDeviceResources() { SafeRelease(m_pRenderTarget); SafeRelease(m_pBlackBrush); }当检测到设备丢失时直接释放渲染目标与画刷下一次OnRender调用CreateDeviceResources时会重新创建。渲染目标内部的CreateDeviceResources判断!m_pRenderTarget才执行创建逻辑Direct2DHelloWorld.cpp因此释放后自动走重建路径。渲染流程OnRender 详解OnRender是整段示例的核心Direct2DHelloWorld.cppHRESULT DemoApp::OnRender() { HRESULT hr; hr CreateDeviceResources(); if (SUCCEEDED(hr) !(m_pRenderTarget-CheckWindowState() D2D1_WINDOW_STATE_OCCLUDED)) { static const WCHAR sc_helloWorld[] LHello, World!; D2D1_SIZE_F renderTargetSize m_pRenderTarget-GetSize(); m_pRenderTarget-BeginDraw(); m_pRenderTarget-SetTransform(D2D1::Matrix3x2F::Identity()); m_pRenderTarget-Clear(D2D1::ColorF(D2D1::ColorF::White)); m_pRenderTarget-DrawText( sc_helloWorld, ARRAYSIZE(sc_helloWorld) - 1, // 字符数不含结尾 \0 m_pTextFormat, D2D1::RectF(0, 0, renderTargetSize.width, renderTargetSize.height), m_pBlackBrush); hr m_pRenderTarget-EndDraw(); if (hr D2DERR_RECREATE_TARGET) // 设备丢失信号 { hr S_OK; DiscardDeviceResources(); } } return hr; }逐段理解其语义遮挡检测CheckWindowState()返回窗口状态标志若含D2D1_WINDOW_STATE_OCCLUDED例如锁屏遮挡则跳过绘制代码注释明确说明此时不渲染任何内容BeginDraw / EndDraw 配对所有绘制命令必须放在BeginDraw()与EndDraw()之间EndDraw把命令批量提交给 GPU 并呈现变换重置SetTransform(D2D1::Matrix3x2F::Identity())把当前变换矩阵复位为单位矩阵确保绘制坐标基于窗口原点清屏Clear(White)用白色填充整个渲染目标形成白底绘制文本DrawText接收宽字符串LHello, World!、字符长度ARRAYSIZE - 1去掉结尾\0、文本格式、目标矩形此处为整个渲染目标尺寸与画刷配合前面设置的双居中对齐文字正好显示在窗口中央设备丢失恢复EndDraw返回D2DERR_RECREATE_TARGET即表示设备丢失此时把 HRESULT 恢复为S_OK并调用DiscardDeviceResources()下次绘制自动重建资源——这正是 Direct2D 推荐的标准恢复模式。消息循环与窗口消息处理RunMessageLoopDirect2DHelloWorld.cpp是标准的 Win32 消息泵GetMessage取消息 →TranslateMessage转换键盘消息 →DispatchMessage分发给窗口过程直到收到WM_QUIT退出。WndProcDirect2DHelloWorld.cpp是驱动渲染的中枢主要处理三类消息消息处理逻辑WM_SIZE取出LOWORD(lParam)/HIWORD(lParam)得到新客户区宽高调用OnResize(width, height)调整渲染目标尺寸WM_PAINT/WM_DISPLAYCHANGE标准BeginPaint/EndPaint包裹下调用OnRender()重绘WM_DISPLAYCHANGE覆盖了显示器分辨率/色彩深度变化需要重绘的场景WM_DESTROY调用PostQuitMessage(0)结束消息循环退出程序OnResize的实现Direct2DHelloWorld.cpp直接调用m_pRenderTarget-Resize(size)同步渲染目标与窗口客户区大小。注释特别说明Resize 可能失败但此处可以忽略错误——失败会在下一次EndDraw时重现这正是 Direct2D 延迟错误报告机制的体现。DPI 感知清单DeclareDPIAware.manifest示例通过内嵌清单声明 DPI 感知DeclareDPIAware.manifestassembly xmlnsurn:schemas-microsoft-com:asm.v1 manifestVersion1.0 xmlns:asmv3urn:schemas-microsoft-com:asm.v3 asmv3:application asmv3:windowsSettings xmlnshttp://schemas.microsoft.com/SMI/2005/WindowsSettings dpiAwaretrue/dpiAware /asmv3:windowsSettings /asmv3:application /assemblydpiAwaretrue/dpiAware使系统认为该进程是 DPI-aware从而不会对窗口进行位图拉伸式的虚拟化缩放。这与此前GetDesktopDpi换算窗口尺寸的逻辑互为呼应窗口按实际 DPI 构造文本以 DIP 为单位渲染Direct2D 负责最终的清晰呈现。该清单通过工程文件的清单工具嵌入程序集四种配置Debug/Release × Win32/x64的VCManifestTool都配置了AdditionalManifestFilesDeclareDPIAware.manifest例如 Direct2DHelloWorld.vcproj。README 的文件列表中正是把它列为 Specifies that the sample application is DPI-aware。工程配置中的链接库Direct2DHelloWorld.vcproj 的链接器配置VCLinkerTool的AdditionalDependencies在四种配置中完全一致揭示了本示例用到的系统库d2d1.lib dwrite.lib windowscodecs.lib dxgi.lib d3d10_1.lib各库职责如下d2d1.libDirect2D 核心 API工厂、渲染目标、画刷等dwrite.libDirectWrite 文本 API工厂与文本格式windowscodecs.libWIC 图像编解码 API对应头文件中声明的m_pWICFactory成员本示例未实际使用属于预留dxgi.libDXGI 基础接口Direct2D 底层交换链依赖d3d10_1.libDirect3D 10.1 APIDirect2D 在其之上构建的渲染管线。编译器配置方面WarningLevel4且WarnAsErrortrue如 Direct2DHelloWorld.vcproj即四级警告且将警告视为错误保证代码质量UsePrecompiledHeader0说明该示例未使用预编译头。另外头文件顶部为WINVER与_WIN32_WINNT定义了0x0700Direct2DHelloWorld.h即显式声明目标平台为 Windows 7 及以上同时定义了UNICODE与WIN32_LEAN_AND_MEAN精简 Windows 头文件并包含d2d1.h、d2d1helper.h、dwrite.h、wincodec.h四个关键头文件。常见问题与延伸学习围绕该示例有几点值得在实际开发中注意线程模型Direct2D 工厂选择SINGLE_THREADED渲染必须始终发生在创建工厂的同一线程通常是 UI 线程不要在后台线程直接操作渲染目标设备丢失是常态而非异常远程桌面断开、显卡驱动重置、显示器切换都会触发D2DERR_RECREATE_TARGET务必按示例的DiscardDeviceResources 延迟重建模式处理DPI 两套机制配合内嵌清单声明感知 代码里用GetDesktopDpi换算像素尺寸二者缺一不可否则会出现窗口模糊或文字渲染不清的问题资源释放顺序COM 接口按依赖逆序释放画刷/渲染目标 → 文本格式 → 两个工厂示例在DemoApp析构函数中集中处理避免泄漏。如果想继续深入仓库的 multimedia/Direct2D 目录还提供了同主题的进阶示例GdiInteropSampleDirect2D 与 GDI 互操作、SaveAsImageFileSample渲染结果存为图片文件、SimplePathAnimationSample 与 TextAnimationSample路径与文本动画等可在掌握本示例的基础上按需阅读。小结Direct2D Hello World 虽然只绘制一行文字却完整示范了 Direct2D 桌面应用的全部关键模式双类资源设备无关/设备相关的生命周期管理、HWND 渲染目标的创建与窗口尺寸联动、BeginDraw/EndDraw 渲染协议、WM_PAINT 驱动的重绘、DPI 感知声明以及设备丢失的优雅恢复。以 Direct2DHelloWorld.cpp 为模板替换DrawText为DrawRectangle、FillGeometry、DrawBitmap等调用即可快速扩展出任意 2D 图形应用——这正是它作为 Direct2D 入门第一课的价值所在。赞分享示例工程【免费下载链接】Windows-classic-samplesThis repo contains samples that demonstrate the API used in Windows classic desktop applications.项目地址https://gitcode.com/gh_mirrors/wi/Windows-classic-samples点击查看免费下载相关推荐3 条路径搞定 Gopeed macOS 部署从双击 .app 到源码编译3 条路径搞定 Gopeed macOS 部署从双击 .app 到源码编译 当你双击 Gopeed 的 .app 图标弹窗跳出「无法验证开发者可能已损坏」示例工程Windows-classic-samples 中的 Simple Direct2D Application用 C 与 Direct2D 绘制图形、文本和位图Windows classic samples 中的 Simple Direct2D Application用 C 与 Direct2D 绘制图形、文本和示例工程Windows-classic-samples 之 Direct2D 简单路径动画示例SimplePathAnimationSample深度剖析Windows classic samples 之 Direct2D 简单路径动画示例SimplePathAnimationSample深度剖析 导读 本指示例工程上一篇终极指南3种简单方法彻底卸载Windows Edge浏览器的PowerShell脚本工具下一篇5个StreamFX滤镜技巧让直播画面瞬间专业起来创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑