尝试在 C# 中创建窗口时出现错误 1407

问题描述 投票:0回答:1

我已经开始学习 Windows API,并尝试用 C# 创建窗口,但总是收到错误 1407,“找不到窗口类”。我已经为

[MarshalAs(UnmanagedType.LPWStr)]
lpClassName
写了
lpWindowName
,但没有帮助。

但是窗口类注册返回49856。

这是我的代码:

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

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

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

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

[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetModuleHandle(string lpModuleName);

[StructLayout(LayoutKind.Sequential)]
public struct WNDCLASSEX
{
    public uint cbSize;
    public uint style;
    public IntPtr 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;
}

private static IntPtr WindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
    if (msg == 0x0010)
    {
        Environment.Exit(0);
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

static void Main()
{
    const string CLASS_NAME = "SampleWindowClass";

    IntPtr hInstance = GetModuleHandle(null);

    WNDCLASSEX wc = new WNDCLASSEX
    {
        cbSize = (uint)Marshal.SizeOf(typeof(WNDCLASSEX)),
        style = 0,
        lpfnWndProc = Marshal.GetFunctionPointerForDelegate(new WndProcDelegate(WindowProc)),
        hInstance = hInstance,
        hbrBackground = (IntPtr)1,
        lpszClassName = CLASS_NAME, // Имя класса
        hIconSm = IntPtr.Zero,
        hIcon = IntPtr.Zero,
        hCursor = IntPtr.Zero,
        cbClsExtra = 0,
        cbWndExtra = 0,
        lpszMenuName = null
    };

    ushort classAtom = RegisterClassEx(ref wc);
    if (classAtom == 0)
    {
        int errorCode = Marshal.GetLastWin32Error();
        Console.WriteLine("Class registration error: " + errorCode);
        return;
    }

    IntPtr hwnd = CreateWindowExW(
        0,
        CLASS_NAME,
        "Learn to Program Windows",
        0x00040000,
        100,
        100,
        500,
        500,
        IntPtr.Zero,
        IntPtr.Zero,
        hInstance,
        IntPtr.Zero
    );

    if (hwnd == IntPtr.Zero)
    {
        int errorCode = Marshal.GetLastWin32Error();
        Console.WriteLine("Window creation error: " + errorCode);
        return;
    }

    ShowWindow(hwnd, 1);
}

private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

可能是什么问题?

c# winapi
1个回答
0
投票

由于

WNDCLASSEX.lpszMenuName
WNDCLASSEX.lpszClassName
是结构体的成员,因此结构体中字符串的编组规则适用于此处。由于您没有为结构指定任何此类规则,因此应用默认规则。来自文档https://learn.microsoft.com/en-us/dotnet/standard/native-interop/customize-struct-marshalling#customizing-string-field-marshalling

默认情况下,.NET 将字符串编组为指向以 null 结尾的字符串的指针。编码取决于 System.Runtime.InteropServices.StructLayoutAttribute 中的 StructLayoutAttribute.CharSet 字段的值。 如果未指定属性,则编码默认为 ANSI 编码。

因此,基本上 WNDCLASSEX 结构中的字符串被编组为指向 ANSI 字符串的指针。但是您调用 Unicode 版本的

RegisterClassEx
WINAPI,它使用 WNDCLASSEXW 结构,具有
LPCWSTR
指针(指向宽/Unicode 字符串的指针)。轰隆隆!

将 WNDCLASSEX [StructLayout] 属性的

CharSet
参数设置为 Unicode(或者可能是 Auto)以解决代码中的这个特定错误。我不知道您的代码中是否还潜藏着其他问题,但是这个问题对我来说相对容易发现......

© www.soinside.com 2019 - 2024. All rights reserved.