You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在C#中创建Win32窗口的技术求助(自定义渲染场景)

在C#中创建Win32窗口的实用方案

要在C#里直接创建原生Win32窗口,核心是通过P/Invoke调用Win32 API,以下是可直接运行的极简实现,适配DirectX渲染需求:

核心步骤与代码实现

1. 定义必要的Win32 API绑定

首先需要导入Win32窗口相关的核心函数和结构体,用DllImport实现P/Invoke:

using System;
using System.Runtime.InteropServices;

public static class Win32
{
    public delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    [StructLayout(LayoutKind.Sequential)]
    public struct WNDCLASSEX
    {
        public uint cbSize;
        public uint style;
        public WndProc lpfnWndProc;
        public int cbClsExtra;
        public int cbWndExtra;
        public IntPtr hInstance;
        public IntPtr hIcon;
        public IntPtr hCursor;
        public IntPtr hbrBackground;
        public string lpszMenuName;
        public string lpszClassName;
        public IntPtr hIconSm;
    }

    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool RegisterClassEx(ref WNDCLASSEX lpwcx);

    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr CreateWindowEx(
        uint dwExStyle,
        string lpClassName,
        string lpWindowName,
        uint dwStyle,
        int x, int y, int nWidth, int nHeight,
        IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);

    [DllImport("user32.dll")]
    public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    [DllImport("user32.dll")]
    public static extern bool UpdateWindow(IntPtr hWnd);

    [DllImport("user32.dll")]
    public static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll")]
    public static extern bool GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);

    [DllImport("user32.dll")]
    public static extern bool TranslateMessage(ref MSG lpMsg);

    [DllImport("user32.dll")]
    public static extern IntPtr DispatchMessage(ref MSG lpMsg);

    [StructLayout(LayoutKind.Sequential)]
    public struct MSG
    {
        public IntPtr hWnd;
        public uint message;
        public IntPtr wParam;
        public IntPtr lParam;
        public uint time;
        public POINT pt;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int x;
        public int y;
    }

    // 常用窗口样式和消息常量
    public const uint WS_OVERLAPPEDWINDOW = 0x00CF0000;
    public const uint WM_DESTROY = 0x0002;
    public const int SW_SHOW = 5;
}

2. 实现窗口类与消息循环

创建一个封装窗口的类,处理窗口注册、创建和消息循环,同时预留DirectX初始化的入口:

public class CustomWin32Window
{
    private IntPtr _hWnd;
    private Win32.WndProc _wndProcDelegate; // 必须保留引用,防止被GC回收

    public IntPtr Handle => _hWnd;

    public void Create(int width, int height, string title)
    {
        // 注册窗口类
        _wndProcDelegate = WndProc;
        var wc = new Win32.WNDCLASSEX
        {
            cbSize = (uint)Marshal.SizeOf(typeof(Win32.WNDCLASSEX)),
            style = 0,
            lpfnWndProc = _wndProcDelegate,
            cbClsExtra = 0,
            cbWndExtra = 0,
            hInstance = Marshal.GetHINSTANCE(typeof(CustomWin32Window).Module),
            hIcon = IntPtr.Zero,
            hCursor = IntPtr.Zero,
            hbrBackground = IntPtr.Zero, // 透明背景,适合自定义渲染
            lpszMenuName = null,
            lpszClassName = "CustomRenderWindowClass",
            hIconSm = IntPtr.Zero
        };

        if (!Win32.RegisterClassEx(ref wc))
            throw new System.ComponentModel.Win32Exception();

        // 创建窗口
        _hWnd = Win32.CreateWindowEx(
            0,
            wc.lpszClassName,
            title,
            Win32.WS_OVERLAPPEDWINDOW,
            100, 100, width, height,
            IntPtr.Zero, IntPtr.Zero, wc.hInstance, IntPtr.Zero);

        if (_hWnd == IntPtr.Zero)
            throw new System.ComponentModel.Win32Exception();

        // 显示窗口
        Win32.ShowWindow(_hWnd, Win32.SW_SHOW);
        Win32.UpdateWindow(_hWnd);
    }

    public void RunMessageLoop()
    {
        Win32.MSG msg;
        while (Win32.GetMessage(out msg, IntPtr.Zero, 0, 0))
        {
            Win32.TranslateMessage(ref msg);
            Win32.DispatchMessage(ref msg);
        }
    }

    private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    {
        switch (msg)
        {
            case Win32.WM_DESTROY:
                // 这里可以添加DirectX资源释放逻辑
                Win32.DefWindowProc(hWnd, msg, wParam, lParam);
                Environment.Exit(0);
                return IntPtr.Zero;
            default:
                return Win32.DefWindowProc(hWnd, msg, wParam, lParam);
        }
    }
}

3. 主程序入口调用

在Main函数中启动窗口,后续可在窗口创建后初始化DirectX设备:

class Program
{
    static void Main(string[] args)
    {
        var window = new CustomWin32Window();
        window.Create(800, 600, "Custom Render Window");
        
        // 在这里初始化DirectX设备,使用window.Handle作为渲染目标窗口句柄
        
        window.RunMessageLoop();
    }
}

关键注意事项

  • 委托引用保留:_wndProcDelegate必须作为类成员保留,否则会被GC回收,导致窗口回调失效崩溃。
  • 背景设置:将hbrBackground设为IntPtr.Zero,避免系统绘制背景干扰自定义渲染。
  • 消息循环:RunMessageLoop是Win32窗口的核心,必须持续运行处理系统消息。
  • DirectX适配:初始化DirectX时,直接使用window.Handle作为输出窗口的句柄即可实现硬件加速渲染。

内容的提问来源于stack exchange,提问作者DALVES2012

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 03:50:48