【问题标题】:C# and PInvoke Winapi program hangsC# 和 PInvoke Winapi 程序挂起
【发布时间】:2021-06-18 01:01:51
【问题描述】:

所以我正在尝试使用 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);
        }
    }
}

【问题讨论】:

  • "尝试使用 PInvoke 制作一个 winapi 应用程序" 很好奇,为什么? Win32 API 是裸机,但曲折且充满怪癖。 C# 更简单、更安全,但是是沙盒的。将两者结合起来就像挑选两全其​​美的樱桃。
  • @dxiv 我只想制作一个包装器,比如 WinForms,因为我觉得它一团糟。我也完全清楚我自愿忍受的:)。如果我在 C++ 中做,然后在 C# 中使用 C++ 库,可能会是一个好主意,但我对 C 或 C++ 感到不舒服
  • C++/CLI 让您可以从本机实现无缝导出托管接口。

标签: c# winapi pinvoke .net-5


【解决方案1】:

你的结构大多是错误的。

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

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

【讨论】:

  • 我已经完成了你告诉我的一切,但应用程序仍然处于这种“僵尸化”状态。光标很忙,我不能移动也不能调整窗口大小。我也没有收到 WM_CLOSE 消息
  • 没关系,我忘了修复WndProcDefWindowProcW 签名。感谢您的回答!
  • 或者使用C#/Win32,再也不会弄错你的P/Invoke签名和类型了。
  • @TopchetoEU 加上更多的东西。 WndProcDefWindowProcW 应该返回 IntPtr 而不是 long,C# 中的 long 类型即使对于 32 位进程也是 64 位的。与类不同,C# 结构默认已经有LayoutKind.Sequential。当您将结构传递给像 ref WNDCLASSEX _class 这样的 C 函数时,将 [In] 属性应用于该参数会稍微高效一些,然后运行时不会费心将 C 代码所做的可能更改传播回 C# 结构。 WinApi 将参数指定为const WNDCLASSEXW *,注意const
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-06
  • 1970-01-01
  • 1970-01-01
  • 2011-07-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多