背景
首先,忽略我们为什么需要这样做的问题,假设我正在使用经典的Win32模式通过C#通过P / Invoke创建一个自定义窗口。
一般的Win32模式是:
WndProc
的自定义窗口类
功能GetMessage(...)
,翻译和调度来开始消息处理循环。在非托管世界中,WndProc
函数在调用消息循环线程CreateWindow(...)
,a.k.a的同一线程上调用。
问题
WndProc
回调总是会在同一个托管线程上发生吗?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);
}
}