强制 Win32 通用控件在“ID2D1HwndRenderTarget”上绘制?

2023-12-02

我的大部分 UI 都是用ID2D1HwndRenderTarget,但我想要一些经典的窗口控件:button, edit. How to

ID2D1HwndRenderTarget * canvas = nullptr; // it's global object
HWND button = nullptr; // it's global object
HWND edit = nullptr; // it's global object
HWND custom = nullptr; // it's global object

// mainWindow WinPproc
case WM_CREATE:
    button = CreateWindowExW(0, L"button", L"Send", WS_CHILD | WS_VISIBLE, 10, 10, 120, 30, hWnd, BUTTON_ID, hInstance, 0); // win32 control
    edit = CreateWindowExW(0, L"edit", L"Edit", WS_CHILD | WS_VISIBLE, 10, 50, 120, 30, hWnd, BUTTON_ID, hInstance, 0); // win32 control
    custom = CreateWindowExW(0, L"custom", L"Custom", WS_CHILD | WS_VISIBLE, 10, 90, 120, 30, hWnd, BUTTON_ID, hInstance, 0); // it's my custom class
    break;

case WM_PAINT:
    BeginPaint(hWnd, nullptr);
    render_target->BeginPaint();
    ... GUI rendering stuff ....
    HRESULT result = render_target->EndDraw();
    if(result != S_OK)
    {
       // Error handling
       ...
    }
    EndPaint(hWnd, nullptr);
    break;
// custom WinProc
case WM_PAINT:
    BeginPaint(hWnd, nullptr);
    render_target->BeginPaint();
    ... rendering stuff ....
    HRESULT result = render_target->EndDraw();
    if(result != S_OK)
    {
       // Error handling
       ...
    }
    EndPaint(hWnd, nullptr);
    break;

只画有的东西render_target是可见的。我明白为什么:因为button and edit是默认的win32控件,内部使用PAINTSTRUCT->HDC语境。我读Direct2D 和 GDI 互操作性概述并了解概念,但仍然不知道 HDC intrecpet 应该在哪里进行?我不想触摸默认控件 WM_PAINT。我必须supclass所有默认的win32控件?

如何强制这些 Win32 控件绘制到我的render_target?


如果你想shareGDI 和 Direct2D 之间的设备上下文 (HDC),您可以使用ID2D1DC渲染目标 and Bind当您想要在 HDC 上进行渲染时。

官方示例对此进行了演示:GDI/Direct2D 互操作性示例

请注意,它不能在当今的 Visual Studio 中编译/工作。因此,这里有一个类似的代码,带有一个简单的按钮和文本框:

#include <windows.h>
#include <stdlib.h>
#include <math.h>
#include <d2d1.h>

template<class Interface>
inline void
SafeRelease(Interface** ppInterfaceToRelease)
{
    if (*ppInterfaceToRelease != NULL)
    {
        (*ppInterfaceToRelease)->Release();
        (*ppInterfaceToRelease) = NULL;
    }
}

EXTERN_C IMAGE_DOS_HEADER __ImageBase;

class DemoApp
{
public:
    DemoApp();
    ~DemoApp();

    HRESULT Initialize();

private:
    HRESULT CreateDeviceIndependentResources();
    HRESULT CreateDeviceResources();
    void DiscardDeviceResources();
    HRESULT OnRender(const PAINTSTRUCT& ps);
    static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

private:
    HWND m_hwnd;
    ID2D1Factory* m_pD2DFactory;
    ID2D1DCRenderTarget* m_pDCRT;
    ID2D1SolidColorBrush* m_pBlackBrush;
};

int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/)
{
    if (SUCCEEDED(CoInitialize(NULL)))
    {
        DemoApp app;
        if (SUCCEEDED(app.Initialize()))
        {
            MSG msg;
            while (GetMessage(&msg, NULL, 0, 0))
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        CoUninitialize();
    }
    return 0;
}

DemoApp::DemoApp() :
    m_hwnd(NULL),
    m_pD2DFactory(NULL),
    m_pDCRT(NULL),
    m_pBlackBrush(NULL)
{
}

DemoApp::~DemoApp()
{
    SafeRelease(&m_pD2DFactory);
    SafeRelease(&m_pDCRT);
    SafeRelease(&m_pBlackBrush);
}

HRESULT DemoApp::Initialize()
{
    HRESULT hr;

    hr = CreateDeviceIndependentResources();
    if (SUCCEEDED(hr))
    {
        // Register the window class.
        WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
        wcex.style = CS_HREDRAW | CS_VREDRAW;
        wcex.lpfnWndProc = DemoApp::WndProc;
        wcex.cbClsExtra = 0;
        wcex.cbWndExtra = sizeof(LONG_PTR);
        wcex.hInstance = (HINSTANCE)&__ImageBase;
        wcex.hbrBackground = NULL;
        wcex.lpszMenuName = NULL;
        wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
        wcex.lpszClassName = L"D2DDemoApp";
        RegisterClassEx(&wcex);

        // Create the application window.
        // Because the CreateWindow function takes its size in pixels, we obtain the system DPI and use it to scale the window size.
        FLOAT dpiX, dpiY;
        m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY);

        m_hwnd = CreateWindow(
            L"D2DDemoApp",
            L"Direct2D Demo App",
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            static_cast<UINT>(ceil(640.f * dpiX / 96.f)),
            static_cast<UINT>(ceil(480.f * dpiY / 96.f)),
            NULL,
            NULL,
            (HINSTANCE)&__ImageBase,
            this
        );

        hr = m_hwnd ? S_OK : E_FAIL;
        if (SUCCEEDED(hr))
        {
            ShowWindow(m_hwnd, SW_SHOWNORMAL);

            UpdateWindow(m_hwnd);
        }
    }

    return hr;
}

HRESULT DemoApp::CreateDeviceIndependentResources()
{
    // Create D2D factory
    return D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);
}

HRESULT DemoApp::CreateDeviceResources()
{
    HRESULT hr = S_OK;
    if (!m_pDCRT)
    {
        // Create a DC render target.
        D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
            D2D1_RENDER_TARGET_TYPE_DEFAULT,
            D2D1::PixelFormat(
                DXGI_FORMAT_B8G8R8A8_UNORM,
                D2D1_ALPHA_MODE_IGNORE),
            0,
            0,
            D2D1_RENDER_TARGET_USAGE_NONE,
            D2D1_FEATURE_LEVEL_DEFAULT
        );

        hr = m_pD2DFactory->CreateDCRenderTarget(&props, &m_pDCRT);
        if (SUCCEEDED(hr))
        {
            // Create a black brush.
            hr = m_pDCRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &m_pBlackBrush);
        }
    }

    return hr;
}

void DemoApp::DiscardDeviceResources()
{
    SafeRelease(&m_pDCRT);
    SafeRelease(&m_pBlackBrush);
}

HRESULT DemoApp::OnRender(const PAINTSTRUCT& ps)
{
    HRESULT hr;
    RECT rc;

    // Get the dimensions of the client drawing area.
    GetClientRect(m_hwnd, &rc);

    // Draw the pie chart with Direct2D.

    // Create the DC render target.
    hr = CreateDeviceResources();

    if (SUCCEEDED(hr))
    {
        // Bind the DC to the DC render target.
        hr = m_pDCRT->BindDC(ps.hdc, &rc);

        m_pDCRT->BeginDraw();
        m_pDCRT->SetTransform(D2D1::Matrix3x2F::Identity());
        m_pDCRT->Clear(D2D1::ColorF(D2D1::ColorF::White));

        m_pDCRT->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(150.0f, 150.0f), 100.0f, 100.0f), m_pBlackBrush, 3.0);

        m_pDCRT->DrawLine(
            D2D1::Point2F(150.0f, 150.0f),
            D2D1::Point2F((150.0f + 100.0f * 0.15425f), (150.0f - 100.0f * 0.988f)), m_pBlackBrush, 3.0
        );

        m_pDCRT->DrawLine(
            D2D1::Point2F(150.0f, 150.0f),
            D2D1::Point2F((150.0f + 100.0f * 0.525f), (150.0f + 100.0f * 0.8509f)), m_pBlackBrush, 3.0
        );

        m_pDCRT->DrawLine(
            D2D1::Point2F(150.0f, 150.0f),
            D2D1::Point2F((150.0f - 100.0f * 0.988f), (150.0f - 100.0f * 0.15425f)), m_pBlackBrush, 3.0
        );

        hr = m_pDCRT->EndDraw();

        if (SUCCEEDED(hr))
        {
            // Draw the pie chart with GDI.

            // Save the original object.
            HGDIOBJ original = NULL;
            original = SelectObject(ps.hdc, GetStockObject(DC_PEN));

            HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
            SelectObject(ps.hdc, blackPen);

            Ellipse(ps.hdc, 300, 50, 500, 250);

            POINT pntArray1[2];
            pntArray1[0].x = 400;
            pntArray1[0].y = 150;
            pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
            pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);

            POINT pntArray2[2];
            pntArray2[0].x = 400;
            pntArray2[0].y = 150;
            pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
            pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);


            POINT pntArray3[2];
            pntArray3[0].x = 400;
            pntArray3[0].y = 150;
            pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
            pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);

            Polyline(ps.hdc, pntArray1, 2);
            Polyline(ps.hdc, pntArray2, 2);
            Polyline(ps.hdc, pntArray3, 2);

            DeleteObject(blackPen);

            // Restore the original object.
            SelectObject(ps.hdc, original);
        }
    }

    if (hr == D2DERR_RECREATE_TARGET)
    {
        hr = S_OK;
        DiscardDeviceResources();
    }

    return hr;
}

LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    if (message == WM_CREATE)
    {
        LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
        SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pcs->lpCreateParams);

        auto button = CreateWindowExW(0, L"button", L"Send", WS_CHILD | WS_VISIBLE, 10, 10, 120, 30, hwnd, (HMENU)1, (HINSTANCE)&__ImageBase, 0); // win32 control
        auto edit = CreateWindowExW(0, L"edit", L"Edit", WS_CHILD | WS_VISIBLE, 10, 50, 120, 30, hwnd, (HMENU)2, (HINSTANCE)&__ImageBase, 0); // win32 control
        return 1;
    }

    LRESULT result = 0;
    DemoApp* pDemoApp = (DemoApp*)(GetWindowLongPtr(hwnd, GWLP_USERDATA));
    bool wasHandled = false;
    if (pDemoApp)
    {
        switch (message)
        {
        case WM_PAINT:
        case WM_DISPLAYCHANGE:
        {
            PAINTSTRUCT ps;
            BeginPaint(hwnd, &ps);
            pDemoApp->OnRender(ps);
            EndPaint(hwnd, &ps);
        }
        result = 0;
        wasHandled = true;
        break;

        case WM_DESTROY:
        {
            PostQuitMessage(0);
        }
        result = 1;
        wasHandled = true;
        break;
        }
    }

    if (!wasHandled)
    {
        result = DefWindowProc(hwnd, message, wParam, lParam);
    }

    return result;
}

下面是它的渲染方式(左圆是 Direct2D,右圆是别名 GDI):

enter image description here

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

强制 Win32 通用控件在“ID2D1HwndRenderTarget”上绘制? 的相关文章

  • 如何一步步遍历目录树?

    我发现了很多关于遍历目录树的示例 但我需要一些不同的东西 我需要一个带有某种方法的类 每次调用都会从目录返回一个文件 并逐渐遍历目录树 请问我该怎么做 我正在使用函数 FindFirstFile FindNextFile 和 FindClo
  • GDI 函数 BitBlt 和 StretchBlt 在 Win32 中是否硬件加速?

    我似乎无法通过搜索得到明确的答案 Win32 blitting 操作是否硬件加速 GDI 而不是 GDI 我不知道这些函数如何与图形驱动程序交互 是否有任何函数调用来验证此功能 例如 GetCaps 对于特定的图形设备 win32图形设备
  • GetKeyState 函数?

    为什么我按下方向箭头ON后 函数GetKeyState仍然给我一个大于0的值 include
  • 构建适用于 Windows 8 的控制面板小程序

    我开发了一个控制面板小程序 cpl 文件 我们与我们的应用程序一起分发 它在所有以前版本的 Windows 上运行良好 但在 Windows 8 上不行 该小程序根本不显示在控制面板中 以前我们所要做的就是将 cpl 文件放入 system
  • 原始 PDO 将 IOCTL 发送到上层过滤器驱动程序 (kbfiltr/moufiltr) 以启用/禁用设备

    我对驱动程序开发非常陌生 并尝试编写一个简单的过滤驱动程序来启用或禁用键盘或鼠标设备 如果我能让它工作 我想在插入鼠标时使用它来禁用笔记本电脑上的触摸板 我意识到可能已经有软件可以做到这一点 但我对设备驱动程序非常感兴趣并且想要自己学习如何
  • 将 Ctrl+Up 发送到窗口

    I m trying to send messages to a window that says Ctrl and Up arrow has been pressed I ve got the basics down I can send
  • 从单击的按钮上移除焦点

    当我单击按钮时 焦点将设置到该按钮 如何使用 C 中的 WIN API 移除此按钮的焦点 我想也许以某种方式SendMessageWIN API 方法可以从按钮上移除焦点 我只有按钮的句柄 也无法再访问父窗口 我知道几乎每次都可以使用 Se
  • 检查 Win32 线程是否正在运行或处于挂起状态

    如何检查 Win32 线程是否正在运行或处于挂起状态 我找不到任何提供线程状态的 Win32 API 那么如何获取线程状态呢 我认为 最初 没有提供此信息 因为任何提供此信息的 API 都会产生误导且无用 考虑两种可能的情况 当前线程已挂起
  • 没有窗口的 GetFontUnicodeRanges

    有机会打电话吗GetFontUnicodeRanges没有窗户 例如 它可能是不允许与桌面交互的 Windows 服务 目前我正在使用控制台应用程序对此进行测试 program UnicodeConsoleOutput APPTYPE CO
  • 如何从更高级别启动用户级别的 Exe

    我希望一个进程始终在用户级别运行 当它由以管理员级别运行的安装程序 自定义 而不是 msi 启动时 或者当用户登录时 环顾四周 我不确定这是否可能 最简单的方法是有 2 个进程 一种是普通用户 它启动提升 管理进程 然后管理进程可以使用 I
  • C# 的 user32 和内核方法列表 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 有没有一个很好的清单来说明我们可以从中进口什么user32 dll and kernel dll并在 C 中使用 我是 Windows A
  • 发送/捕获 SIGTERM 的 Win32 API 模拟

    在 POSIX OS 下 有信号 API 允许向进程发送信号以将其关闭 使用kill 您可以使用sigaction捕获它并执行您需要的操作 然而 Win32不是POSIX系统 所以 如何处理可能出现的关闭事件 例如来自 任务管理器 中的 结
  • 注册表碎片整理程序[重复]

    这个问题在这里已经有答案了 这是出于好奇 但我见过几个 其中一些非常流行 称为注册表碎片整理程序的软件 虽然我可以看到它们提供的好处 但我很好奇您到底是如何进行注册表碎片整理的 请注意 我并不是要求提供软件名称 只是询问其如何以编程方式完成
  • 即使我使用 SetWindowTextW(),Unicode 文本在编辑框中显示为问号

    我遇到了 unicode 文件名在编辑框中显示为问号的问题 当我将 unicode 字符 例如阿拉伯语或泰语 粘贴到编辑框中时 它们会正确显示 但在运行此代码后 它们会变成问号 怎么会 WCHAR buf 100 GetWindowText
  • 使用 WinAPI 连接禁用的显示设备

    我的问题是启用禁用的监视器ChangeDisplaySettingsEx 我想这不是火箭科学 但经过一番挖掘后 它看起来仍然是不可能的 我找到了一种根据找到的 Microsoft 代码示例禁用所有辅助显示器的方法here https msd
  • 多个原始输入窗口接收器

    我有一个仅消息窗口 ATL CWindowImpl 它使用 RIDEV INPUTSINK 标志将自身注册为原始输入 这意味着无论该窗口是否是前台窗口 它都会获取所有输入 当该窗口只有一个实例时 这非常有效 但是 当我创建超过 1 个窗口实
  • 当用户更改 Windows 中的语言键盘布局时如何通知?

    I want to show a message to user when the user changes the language keyboard layout of Windows for example from EN to FR
  • 有没有办法找到dll公开的所有函数

    我一直在寻找一种方法来获取映射到 dll 中函数名称的所有字符串 我的意思是您可以调用 GetProcAddress 的所有字符串 如果你对 dll 进行十六进制转储 符号 字符串 就在那里 但我认为必须有一个系统调用来获取这些名称 如果您
  • .NET 或 Windows 同步原语性能规范

    我目前正在写一篇科学文章 我需要非常准确地引用 有人可以向我指出 MSDN MSDN 文章 一些已发表的文章来源或一本书 我可以在其中找到 Windows 或 NET 同步原语的性能比较 我知道这些是按性能降序排列的 互锁 API 关键部分
  • 获取多个显示器的整个屏幕的设备上下文

    我需要用 C 在所有内容上画一条线 用鼠标 我可以使用 P Invoke 获取桌面窗口的 Graphics 对象 DesktopGraphics Graphics FromHdc GetDC IntPtr Zero 但是 我使用此图形对象绘

随机推荐

  • Android Eclipse 错误:“Gson 无法解析为类型”

    我对 Java Eclipse 和 Android 比较陌生 所以这可能是一个完全愚蠢的问题 但我还是要问它 我正在学习一个项目来测试连接到 flickr api 并简单地显示最近的图像 我现在想要解析从 flickr 收到的 JSON 我
  • 如何防止 scanf 失败导致无限循环

    该代码应该可以换一美元并且运行良好 但教授说他将随字母一起输入随机数字 它可以很好地处理数字 但是当输入字母时会出现无限循环 有什么建议吗 include
  • 如何在android中对本地数据库进行语音识别检查?

    您是否还记得在旧手机中您可以创建语音快捷方式来呼叫某人 我正在尝试在 android 中制作一个具有该功能的应用程序 用户录制想要控制应用程序的单词或声音 语音识别器只会检查它听到的声音是否等于之前录制的声音 有谁知道如何制作这个或知道指南
  • 在Python中调整OSX系统音频音量

    我想通过 python 脚本调整 OSX 中的系统音量 这个问题关于实现键盘快捷键告诉我如何在 applescript 中执行此操作 但我真的很想从 python 脚本中执行此操作 而不使用 os system popen 等 理想情况下
  • Android 中基于一个手柄旋转和缩放视图

    我正在尝试根据 Android 中的一个 拖动 手柄来旋转和缩放视图 随着布局旋转和视图大小调整 最终结果应该是拖动手柄跟随用户手指移动 这是基于结合这两个问题 如何使用手柄在android中缩放视图 在android中绕两点旋转不起作用
  • 使用 htaccess 将页面从一个页面重定向到另一个页面而不更改 url

    有没有办法在 joomla 中使用 htaccess 将页面从一个页面重定向到另一个页面而不更改 url 我想更改网址 http idaycom com index php component quates to http idaycom
  • 如何在 julia 中将 Array{Array{Float64, 1}, 1} 转换为矩阵?

    假设我有这样的输入 gt 0 8681299566762923 0 3472589826095631 3 2300860990307445 3 3731249077464946 如何将其转换为更令人愉快的类型 例如矩阵 已知尺寸 您可以使用
  • 如何在旧版本的 Android 上使用 ActionBar? [复制]

    这个问题在这里已经有答案了 可能的重复 早期 sdk 版本中的 ActionBar 相当于什么 如果我没记错的话 Android 指南说您应该使用 ActionBar 在应用程序内进行全局导航 但与此同时 您通常希望以尽可能最旧的 API
  • 防止静态块的继承

    如果我们运行派生类 它将打印派生类和父类 有什么方法可以防止静态块的继承吗 1类 public class parent static System out println Parent Class2 public class derive
  • Akka 流:读取多个文件

    我有一个文件列表 我想 将所有这些作为单一来源进行阅读 文件应该按顺序 按顺序读取 无循环赛 任何时候都不应要求任何文件完全位于内存中 从文件读取错误应该会崩溃流 感觉这应该可行 Scala akka streams v2 4 7 val
  • .gitattributes 合并驱动程序未使用

    首先我知道这个问题如何告诉 git 始终选择本地版本来进行特定文件上的冲突合并 但这篇文章对我没有帮助 而且由于我的声誉 我无法添加任何评论 http git scm com book en Customizing Git Git Attr
  • 允许特殊字符,但只有特殊字符时不允许

    假设我有这个 a zA Z0 9 允许使用字母数字字母和一些特殊字符 逗号 点 破折号等 一次或多次 但现在 我只想在还有字母数字字母时允许使用这些特殊字符 a b c ok a ok 不好 不好 我尝试了一些方法 但仍然无法找到一种方法
  • iOS13 UIAlertController 具有自定义视图和首选样式作为操作表灰度所有颜色

    我不确定这是一个错误还是一个功能 但是这段代码 let sheet UIAlertController customView awesomeView preferredStyle actionSheet present sheet anim
  • 如何编辑 .fig 文件中保存的图形的属性而不显示它

    我想编辑保存为 fig MATLAB 的默认格式 文件的 MATLAB 图形的某个属性 我在脚本中创建了很多图形密集型图形 因此我选择通过使默认图形不可见来不显示它们set 0 DefaultFigureVisible off 这设置了 V
  • C# 中的 UDPclient 速率控制

    我连续发送多个 udp 数据包到远程电脑 问题是 如果数据量太大 通道之间的某些设备会遇到缓冲区溢出 我打算限制 节流 控制 udp 数据包的发送速率 有人可以给我一些关于如何找到最佳速率发送间隔的指导吗 顺便说一句 请停止建议使用 tcp
  • Java 可选 orElseThrow 与空集合

    我正在实现一个使用集合的流Foo列表获取该列表中所有项目的 id 并使用它们获取 Bar 实例的值 我想确保这个方法会抛出资源未找到异常如果栏列表上没有项目 尽管在当前状态下它会检查列表栏是否为空 但它不是 因为它包含一个空列表 您能帮我并
  • 了解 mysql 元组搜索的性能影响

    我正在研究这样的表结构 emp data id dept id emp id emp name role 1 101 1001 Tom Good Worker 2 101 1002 Dick Smart Worker 3 102 1001
  • rJava 在 macOS 10.14 上安装失败

    升级到 MacOS Mojave 10 14 1 后 我无法再安装rJava适用于安装了 Java 版本 1 8 0 102 的 R 版本 3 5 1 我尝试过MacOS 10 11 的先前解决方案但它仍然不起作用 给出了这个错误 Erro
  • SED:在匹配之前寻址两行

    打印行 位于匹配 模式 之前 2 行 我接下来尝试 sed n loop h x n n cen p s c p t x s n c p t loop g p datafile 剧本 sed n 1N 2N XXX n P N D 工作原理
  • 强制 Win32 通用控件在“ID2D1HwndRenderTarget”上绘制?

    我的大部分 UI 都是用ID2D1HwndRenderTarget 但我想要一些经典的窗口控件 button edit How to ID2D1HwndRenderTarget canvas nullptr it s global obje