首页 > 解决方案 > C# 和 PInvoke Winapi 程序挂起

问题描述

所以我正在尝试使用 PInvoke 制作一个 winapi 应用程序。到目前为止,我已经能够打开一个窗口,但从那里,我似乎无法完成这项WndProc工作。现在,应用程序启动了,但它既不会响应调整大小,也不会响应移动或任何东西,除了悬停在控制按钮上方。

WindowLL.cs:

public delegate long WndProc(IntPtr hWnd, uint message, byte wParam, long lParam);
public static unsafe class WindowLL
{
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    public static extern ushort RegisterClassExA(ref WNDCLASSEX _class);
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    public static extern IntPtr CreateWindowExA(
        uint extraStyle,
        ushort className,
        //[MarshalAs(UnmanagedType.LPWStr)]
        //string className,
        [MarshalAs(UnmanagedType.LPStr)]
        string title,
        uint style,
        int x,
        int y,
        int width,
        int height,
        IntPtr parent,
        IntPtr menu,
        IntPtr instance,
        IntPtr parameter
    );
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern bool DestroyWindow(IntPtr hWnd);
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    public static extern bool ShowWindow(IntPtr hWnd, int cmdShow);
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool GetMessage(
        out MSG lpMsg,
        IntPtr hWnd,
        uint wMsgFilterMin,
        uint wMsgFilterMax
    );
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern uint DispatchMessage(ref MSG lpMsg);
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern uint TranslateMessage(ref MSG lpMsg);
    [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern long DefWindowProcA(
        IntPtr hWnd,
        uint Msg,
        byte wParam,
        long lParam
    );
}

结构(分成不同的文件):

[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
    public IntPtr hwnd;
    public uint message;
    public byte wParam;
    public long lParam;
    public short time;
    public POINT pt;
    public short lPrivate;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
    public long X;
    public long Y;
}
[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;
    [MarshalAs(UnmanagedType.LPStr)]
    public string    lpszMenuName;
    [MarshalAs(UnmanagedType.LPStr)]
    public string    lpszClassName;
    public IntPtr    hIconSm;
}

程序.cs:

class Program
{
    static long WndProc(IntPtr hWnd, uint message, byte wParam, long lParam)
    {
        return WindowLL.DefWindowProcA(hWnd, message, wParam, lParam);
    }
    static void Main(string[] args)
    {
        var winCalss = new WNDCLASSEX() {
            lpszClassName = "test",
            lpfnWndProc = WndProc,
            cbSize = (uint)Marshal.SizeOf(typeof(WNDCLASSEX)),
        };
        var classId = WindowLL.RegisterClassExA(ref winCalss);

        var overlapped =
            WindowStyle.Overlapped |
            WindowStyle.Caption |
            WindowStyle.SysMenu |
            WindowStyle.ThickFrame |
            WindowStyle.MinimizeBox |
            WindowStyle.MaximizeBox;

        IntPtr hWnd = WindowLL.CreateWindowExA(0,
            classId, "test",
            (uint)overlapped,
            0, 0,
            1280, 720,
            IntPtr.Zero, IntPtr.Zero,
            IntPtr.Zero, IntPtr.Zero
        );

        WindowLL.ShowWindow(hWnd, (int)NCmdShow.Show);

        while (WindowLL.GetMessage(out var msg, hWnd, 0, 0)) {
            WindowLL.TranslateMessage(ref msg);
            WindowLL.DispatchMessage(ref msg);
        }
    }
}

标签: c#winapipinvoke.net-5

解决方案


你的结构大多是错误的。

wParam / lParam 都应该是 IntPtr,时间应该是 uint,点 x/y 应该是 int。

您甚至不需要自己编写它们,从框架中复制粘贴,MIT 许可证允许:

https://source.dot.net/#WindowsBase/System/Windows/Interop/MSG.cs,8a0b462145bdb8d8

https://source.dot.net/#PresentationFramework/System/Windows/Standard/NativeMethods.cs,527982c752231945

还有一件事。您不应该使用 A 函数和结构,例如CreateWindowExA. 这些是 Windows 95、98 和 Me 的兼容性垫片,这 3 个都在几十年前停产了。现代软件应该只使用 W 版本。作为一个很好的副作用,字符串编组更便宜,C# 字符串存储为 UTF-16,它与 W WinApi 函数预期的编码相匹配。


推荐阅读