【问题标题】:Window message loop ( WndProc | While (GetMEssage)) thread in .NET Core.NET Core 中的窗口消息循环(WndProc | While (GetMEssage))线程
【发布时间】:2018-10-14 01:28:20
【问题描述】:

我正在尝试使用 .Net Core 订阅窗口消息

我能够接收初始消息以创建窗口(通过 pinvoke)并销毁消息。但在那之后我创建的窗口被阻止并且没有收到任何其他消息。

 public class CustomWindow : IDisposable
{
    delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    [StructLayout(LayoutKind.Sequential,CharSet = CharSet.Unicode)]
    struct WNDCLASS
    {
        public uint style;
        public IntPtr lpfnWndProc;
        public int cbClsExtra;
        public int cbWndExtra;
        public IntPtr hInstance;
        public IntPtr hIcon;
        public IntPtr hCursor;
        public IntPtr hbrBackground;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpszMenuName;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string lpszClassName;
    }

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

    [DllImport("user32.dll", SetLastError = true)]
    static extern UInt16 RegisterClassW([In] ref WNDCLASS lpWndClass);

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

    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr DefWindowProcW(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true)]
    static extern bool DestroyWindow(IntPtr hWnd);

    private const int ERROR_CLASS_ALREADY_EXISTS = 1410;

    private bool _mDisposed;
    public IntPtr Hwnd;

    public List<uint> Messages { get; set; }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!_mDisposed)
        {
            if (disposing)
            {
                // Dispose managed resources
            }

            // Dispose unmanaged resources
            if (Hwnd != IntPtr.Zero)
            {
                DestroyWindow(Hwnd);
                Hwnd = IntPtr.Zero;
            }
        }
    }

    public CustomWindow()
    {
        Messages = new List<uint>();
        var className = "InvisibleWindow";

        _mWndProcDelegate = CustomWndProc;

        // Create WNDCLASS
        WNDCLASS windClass = new WNDCLASS
        {
            lpszClassName = className,
            lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_mWndProcDelegate)
        };

        UInt16 classAtom = RegisterClassW(ref windClass);

        int lastError = Marshal.GetLastWin32Error();

        if (classAtom == 0 && lastError != ERROR_CLASS_ALREADY_EXISTS)
        {
            throw new Exception("Could not register window class");
        }

        const UInt32 WS_OVERLAPPEDWINDOW = 0xcf0000;
        const UInt32 WS_VISIBLE = 0x10000000;


        // Create window
        Hwnd = CreateWindowExW(
            0,
            className,
            "My WIndow",
            WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 300, 400,
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero,
            IntPtr.Zero
        );

        Importer.ShowWindow(Hwnd, 1);
        Importer.UpdateWindow(Hwnd);
    }

    private  IntPtr CustomWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
    {
        Messages.Add(msg);
        return DefWindowProcW(hWnd, msg, wParam, lParam);
    }

    private WndProc _mWndProcDelegate;
}

类创建自定义窗口CustomWndProc 接收消息。我还没有在那里执行任何逻辑,只是想让事情正常进行。

我正在试图弄清楚如何从一个单独的线程中执行此操作,以便在不阻塞主 api/gui/console 接口的同时继续收听消息

我可以在单独的线程中创建这个类,并且仍然使用依赖注入并且可以随心所欲地访问它的数据/消息/事件。我知道在 .net 中有很多方法可以做到这一点,但这是 .net Core。

downvotes 是怎么回事,实际上没有资源如何在 Net Core 中实现这一点,我无法访问任何简化这一点的 .Net 表单/控件,我还提供了完整的工作类,如果有我的帖子中缺少细节,请发表评论。不要在没有充分理由的情况下随机投票。如果有另一个帖子解释了如何做,请链接它,然后投反对票。

 private void GetMessage()
    {
       IntPtr hwnd = _customWindow.Hwnd;
        int bRet;
        while ((bRet = Importer.GetMessage(out var msg, _customWindow.GetHandle, 0, 0)) !=0 )
        {
            if (bRet == -1)
            {
                Console.WriteLine("Error");
            }
            else
            {
                Importer.TranslateMessage(ref msg);
                Importer.DispatchMessage(ref msg);
                Console.WriteLine(_customWindow.Messages.LastOrDefault());
            }
        }
    }

实现了 GetMessage 循环,我的窗口现在接收所有消息并且没有被阻塞,但它现在阻塞了主线程。我会看看我是否可以在单独的线程上创建窗口并在该线程上运行消息循环。

【问题讨论】:

  • 你需要自己编写消息循环(GetMessage/DispatchMessage):en.wikipedia.org/wiki/Message_loop_in_Microsoft_Windows你做到了吗?
  • @SimonMourier 这正是我要问的,我该怎么做
  • 你必须 p/invoke 这些 Windows API 方法,你必须模仿一个 Windows 应用程序,就像你对 CreateWindow 所做的那样,等等。
  • @SimonMourier 我在主线程中使用了模仿 getMessage 吗?这适用于 c++ gui 应用程序,因为这就是执行 gui 操作的方式。但是在 .Net Core 中,它会锁定自己接口的执行。如何确保 getMessage 循环始终在监听,但我的 .Net Core api/UI 也是如此
  • 您有完整的复制项目以便我们进行测试吗?

标签: .net-core pinvoke wndproc window-messages message-loop


【解决方案1】:

最终设法找到了可行的解决方案。

虽然在 C++ 中创建的窗口会自动在自己的线程中工作,无需任何输入,并且能够处理自己的消息队列。也因为它主要是 GUI,这就是接收输入的方式。

在 .Net 中,您不能将对象分配给特定线程。因此,即使您在新线程或任务中启动 GetMessage Loop,窗口本身也会被阻塞,因为它位于主线程中。 要克服这个问题,请在主线程中运行 C++ Window 和 GetMessage 循环,并在第二个线程中运行您的控制台/Gui/APi。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-21
    • 1970-01-01
    • 2012-05-26
    • 1970-01-01
    • 1970-01-01
    • 2011-07-14
    • 1970-01-01
    相关资源
    最近更新 更多