alpha49 2023-03-25 18:02 采纳率: 61.1%
浏览 97
已结题

请问纯C语言如何不使用easyx实现窗口绘图?

最近我在学习Windows窗口编程。之前使用easyx图形库编写了一个贪吃蛇小程序,但是easyx图形库只能用在vs上,且不支持在纯C语言中使用,而且之前尝试在32位系统上编译失败报错了,所以想要尝试弄懂easyx图形库的原理,自己使用纯C语言编写一个简易的图形库。

img

如上图所示,如果在一个黑色背景的窗口中绘制一个白色的矩形,使用easyx可以很简单的做到:
#include "graphics.h"

int main() {
    initgraph(800, 600);
    setlinecolor(WHITE);
    setfillcolor(WHITE);
    fillrectangle(50, 50, 100, 100);
    system("pause");
}
但是如果不使用easyx图形库来还原这个效果,我想到的方法是创建一个子窗口,代码如下:
#include <windows.h>

LRESULT CALLBACK ParentWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
    WNDCLASS wc;
    HWND hwndParent, hwndChild;
    MSG msg;

    // 注册父窗口类
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = ParentWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = TEXT("ParentWndClass");
    RegisterClass(&wc);

    // 注册子窗口类
    wc.lpfnWndProc = ChildWndProc;
    wc.lpszClassName = TEXT("ChildWndClass");
    wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    RegisterClass(&wc);

    // 创建父窗口
    hwndParent = CreateWindow(TEXT("ParentWndClass"),
                              TEXT("Parent Window"),
                              WS_OVERLAPPEDWINDOW,
                              CW_USEDEFAULT,
                              CW_USEDEFAULT,
                              300,
                              300,
                              NULL,
                              NULL,
                              hInstance,
                              NULL);

    ShowWindow(hwndParent, iCmdShow);
    UpdateWindow(hwndParent);

    // 消息循环
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK ParentWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_CREATE:
            // 创建子窗口
            CreateWindow(TEXT("ChildWndClass"),
                         TEXT("Child Window"),
                         WS_CHILD | WS_VISIBLE,
                         10,
                         10,
                         50,
                         50,
                         hwnd,
                         NULL,
                         ((LPCREATESTRUCT)lParam)->hInstance,
                         NULL);
            break;

        case WM_SIZE:
            // 调整子窗口的大小和位置
            MoveWindow(GetDlgItem(hwnd, 1),
                       10,
                       10,
                       LOWORD(lParam) - 100,
                       HIWORD(lParam) - 100,
                       TRUE);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    return 0;
}

LRESULT CALLBACK ChildWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    switch (msg) {
        case WM_CREATE:
            break;

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
        }
            break;
        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }

    return 0;
}

运行结果:

img

但是这样虽然可以还原出来一模一样的效果,但如果每创建一个矩形就要创建一个子窗口,在编游戏程序时无疑会占用很大的内存,而且通过Spy++查找窗口,easyx绘制的窗口也不是创建子窗口来绘图的。请问如何才能做到不依赖easyx实现简易的窗口绘图?求解答,感谢。
  • 写回答

7条回答 默认 最新

  • OKX安生 2023-03-25 18:28
    关注
    该回答引用于gpt与OKX安生共同编写:
    
    • 该回答引用于gpt与OKX安生共同编写:

    实现窗口绘图可以使用 Windows API 中的 GDI (Graphics Device Interface)。GDI 是一组用于处理图形相关操作的函数和数据结构,包括画图、刷漆、字体等等。

    下面是一个使用 GDI 绘制矩形的例子:

    #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) {
        WNDCLASSEX wc;
        HWND hwnd;
        MSG msg;
    
        ZeroMemory(&wc, sizeof(WNDCLASSEX));
    
        wc.cbSize = sizeof(WNDCLASSEX);
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = WndProc;
        wc.hInstance = hInstance;
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wc.lpszClassName = "WindowClass";
    
        RegisterClassEx(&wc);
    
        hwnd = CreateWindowEx(0,
                              "WindowClass",
                              "Hello, World!",
                              WS_OVERLAPPEDWINDOW,
                              300,
                              300,
                              500,
                              400,
                              NULL,
                              NULL,
                              hInstance,
                              NULL);
    
        ShowWindow(hwnd, nCmdShow);
        UpdateWindow(hwnd);
    
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return msg.wParam;
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
        HDC hdc;
        PAINTSTRUCT ps;
        RECT rect;
    
        switch (msg) {
            case WM_PAINT:
                hdc = BeginPaint(hwnd, &ps);
    
                GetClientRect(hwnd, &rect);
                SetTextColor(hdc, RGB(0, 0, 255));
                DrawText(hdc, "Hello, World!", -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    
                SelectObject(hdc, GetStockObject(NULL_BRUSH));
                SetROP2(hdc, R2_COPYPEN);
                SetBkMode(hdc, TRANSPARENT);
                Rectangle(hdc, 100, 100, 300, 200);
    
                EndPaint(hwnd, &ps);
                break;
            case WM_DESTROY:
                PostQuitMessage(0);
                break;
            default:
                return DefWindowProc(hwnd, msg, wParam, lParam);
        }
    
        return 0;
    }
    

    在这个示例中,我首先创建了一个窗口类 WNDCLASSEX,并使用 RegisterClassEx 函数进行注册。接着,在窗口函数 WndProc 中处理 WM_PAINT 消息,使用 BeginPaint 来获取设备上下文句柄 HDC,然后通过 GDI 绘制矩形、文字等图形,最后使用 EndPaint 结束绘图。

    需要注意的是,GDI 编程需要了解一些 Windows API 的基本知识,例如如何创建和处理消息循环,如何获取设备上下文句柄,如何绘制各种图形等等。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(6条)

报告相同问题?

问题事件

  • 系统已结题 4月2日
  • 已采纳回答 3月25日
  • 创建了问题 3月25日

悬赏问题

  • ¥15 metadata提取的PDF元数据,如何转换为一个Excel
  • ¥15 关于arduino编程toCharArray()函数的使用
  • ¥100 vc++混合CEF采用CLR方式编译报错
  • ¥15 coze 的插件输入飞书多维表格 app_token 后一直显示错误,如何解决?
  • ¥15 vite+vue3+plyr播放本地public文件夹下视频无法加载
  • ¥15 c#逐行读取txt文本,但是每一行里面数据之间空格数量不同
  • ¥50 如何openEuler 22.03上安装配置drbd
  • ¥20 ING91680C BLE5.3 芯片怎么实现串口收发数据
  • ¥15 无线连接树莓派,无法执行update,如何解决?(相关搜索:软件下载)
  • ¥15 Windows11, backspace, enter, space键失灵