创建自定义窗口类时的托管和非托管线程行为

时间:2015-12-16 15:28:27

标签: c# winapi pinvoke

背景

首先,忽略我们为什么需要这样做的问题,假设我正在使用经典的Win32模式通过C#通过P / Invoke创建一个自定义窗口。

一般的Win32模式是:

  1. 注册具有指定名称和WndProc的自定义窗口类 功能
  2. 在专用线程上,创建窗口的实例和 通过调用GetMessage(...),翻译和调度来开始消息处理循环。
  3. 在非托管世界中,WndProc函数在调用消息循环线程CreateWindow(...),a.k.a的同一线程上调用。

    问题

    • 在托管上下文中创建线程时,此行为是否保证相同?
    • 如果消息循环托管在托管线程上,那么WndProc回调总是会在同一个托管线程上发生吗?
    • 如果我通过P / Invoke检索非托管线程ID,我是否可以始终使用该ID将方法发送到该线程,例如PostThreadMessage(...)

    我只是因为臭名昭着的warning from MSDN

    而问
      

    操作系统ThreadId与托管没有固定的关系   线程,因为非托管主机可以控制之间的关系   托管和非托管线程。

    示例代码

    我尽力让它变得尽可能小。我测试了它并发现ID确实匹配,但是想知道在给定不同的环境或框架版本时这是否会破坏。

    public partial class CustomNativeWindow
    {
        static WNDCLASSEX WindowClass;
    
        IntPtr _hwnd;
    
        Thread _mainThread;
        int _managedThreadId;
        uint _unmanagedThreadId;
    
        static CustomNativeWindow()
        {
            // Ignoring implementation here, this constructor creates
            // a window class struct with the given name and WndProc
            WindowClass = new WNDCLASSEX("ClassName", WndProc);
    
            NativeMethods.RegisterClassEx(WindowClass);
        }
    
        public CustomNativeWindow()
        {
            _mainThread = new Thread(MainThread);
            _mainThread.Start();
        }
    
        void MainThread()
        {
            _hwnd = NativeMethods.CreateWindowEx( /* ... */ );
    
            CustomNativeWindow.RegisterHwnd(_hwnd);
    
            // We grab these values before we start our message loop
            _managedThreadId = Thread.CurrentThread.ManagedThreadId;
            _unmanagedThreadId = NativeMethods.GetCurrentThread();
    
            MSG msg;
            int result;
            while((result = NativeMethods.GetMessage(out msg, _hwnd, 0, 0)) != 0)
            {
                NativeMethods.TranslateMessage(ref msg);
                NativeMethods.DispatchMessage(ref msg);
            }
        }
    
        // Although not so obvious, this method is invoked on the MainThread thread
        // while GetMessage call is blocking
        static IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
        {
            // Since WndProc is defined for the class, not the instance, assume we have
            // some way to retrieve the specific instance this method is invoked on
            var windowInstance = CustomNativeWindow.LookupHwnd(hWnd);
    
            if (windowInstance._managedThreadId != Thread.CurrentThread.ManagedThreadId)
                throw new Exception("Is this even possible?");
    
            if (windowInstance._unmanagedThreadId != NativeMethods.GetCurrentThread())
                throw new Exception("How about this?");
    
            return NativeMethods.DefWindowProc(hWnd, msg, wParam, lParam);
        }
    }
    

0 个答案:

没有答案