一,作用
提供线程队列服务,通过windows message 这个基本原理逻辑代码。
二,代码
异步调用
dispacher 把我们行为封装成为DispatcherOperation,然后投递队列,同时投递事件给绑定的线程对应的 message only 窗口,窗口收到消息就运行我的行为。
写c++开发时候,为了拥有这样功能,我们都要自己写窗口,封装异步行为,到了WPF 这个已经是自带功能。
private void InvokeAsyncImpl(DispatcherOperation operation, CancellationToken cancellationToken) { DispatcherHooks hooks = null; bool succeeded = false; // Could be a non-dispatcher thread, lock to read lock(_instanceLock) { if (!cancellationToken.IsCancellationRequested && !_hasShutdownFinished && !Environment.HasShutdownStarted) { // Add the operation to the work queue //当前行为添加到队列里面去 operation._item = _queue.Enqueue(operation.Priority, operation); // Make sure we will wake up to process this operation. //投递消息给创建window,这样子处理消息 succeeded = RequestProcessing(); if (succeeded) { // Grab the hooks to use inside the lock; but we will // call them below, outside of the lock. hooks = _hooks; } else { // Dequeue the item since we failed to request // processing for it. Note we will mark it aborted // below. _queue.RemoveItem(operation._item); } } } if (succeeded == true) { // We have enqueued the operation. Register a callback // with the cancellation token to abort the operation // when cancellation is requested. if(cancellationToken.CanBeCanceled) { CancellationTokenRegistration cancellationRegistration = cancellationToken.Register(s => ((DispatcherOperation)s).Abort(), operation); // Revoke the cancellation when the operation is done. operation.Aborted += (s,e) => cancellationRegistration.Dispose(); operation.Completed += (s,e) => cancellationRegistration.Dispose(); } if(hooks != null) { hooks.RaiseOperationPosted(this, operation); } if (EventTrace.IsEnabled(EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info)) { EventTrace.EventProvider.TraceEvent(EventTrace.Event.WClientUIContextPost, EventTrace.Keyword.KeywordDispatcher | EventTrace.Keyword.KeywordPerf, EventTrace.Level.Info, operation.Priority, operation.Name, operation.Id); } } else { // We failed to enqueue the operation, and the caller that // created the operation does not expose it before we return, // so it is safe to modify the operation outside of the lock. // Just mark the operation as aborted, which we can safely // return to the user. operation._status = DispatcherOperationStatus.Aborted; operation._taskSource.SetCanceled(); } }
创建dispacher
- 添加全局list dispacher【全局管理】
- 创建MessageOnlyHwndWrappe 窗口,这个是核心的功能。
- 添加 窗口消息处理函数 WndProcHook
[SecurityCritical, SecurityTreatAsSafe] private Dispatcher() { _queue = new PriorityQueue<DispatcherOperation>(); _tlsDispatcher = this; // use TLS for ownership only _dispatcherThread = Thread.CurrentThread; // Add ourselves to the map of dispatchers to threads. lock(_globalLock) { //全局list 增加 当前的list _dispatchers.Add(new WeakReference(this)); } _unhandledExceptionEventArgs = new DispatcherUnhandledExceptionEventArgs(this); _exceptionFilterEventArgs = new DispatcherUnhandledExceptionFilterEventArgs(this); _defaultDispatcherSynchronizationContext = new DispatcherSynchronizationContext(this); // Create the message-only window we use to receive messages // that tell us to process the queue. MessageOnlyHwndWrapper window = new MessageOnlyHwndWrapper(); _window = new SecurityCriticalData<MessageOnlyHwndWrapper>( window ); _hook = new HwndWrapperHook(WndProcHook); _window.Value.AddHook(_hook); // DDVSO:447590 // Verify that the accessibility switches are set prior to any major UI code running. AccessibilitySwitches.VerifySwitches(this); }
WndProcHook
处理窗口消息,用来处理 我们投递过来的异步操作
private IntPtr WndProcHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { WindowMessage message = (WindowMessage)msg; if(_disableProcessingCount > 0) { throw new InvalidOperationException(SR.Get(SRID.DispatcherProcessingDisabledButStillPumping)); } if(message == WindowMessage.WM_DESTROY) { if(!_hasShutdownStarted && !_hasShutdownFinished) // Dispatcher thread - no lock needed for read { // Aack! We are being torn down rudely! Try to // shut the dispatcher down as nicely as we can. ShutdownImpl(); } } else if(message == _msgProcessQueue) { //如果消息,异步投递消息 //这个就是我们用dispacher beginInvoke的 // 内部调用 RequestProcessing(); 投递回调消息过来 ProcessQueue(); } else if(message == WindowMessage.WM_TIMER && (int) wParam == TIMERID_BACKGROUND) { // This timer is just used to process background operations. // Stop the timer so that it doesn't fire again. SafeNativeMethods.KillTimer(new HandleRef(this, hwnd), TIMERID_BACKGROUND); ProcessQueue(); } else if(message == WindowMessage.WM_TIMER && (int) wParam == TIMERID_TIMERS) { // We want 1-shot only timers. So stop the timer // that just fired. KillWin32Timer(); PromoteTimers(Environment.TickCount); } // We are about to return to the OS. If there is nothing left // to do in the queue, then we will effectively go to sleep. // This is the condition that means Idle. DispatcherHooks hooks = null; bool idle = false; lock(_instanceLock) { idle = (_postedProcessingType < PROCESS_BACKGROUND); if (idle) { hooks = _hooks; } } if (idle) { if(hooks != null) { hooks.RaiseDispatcherInactive(this); } ComponentDispatcher.RaiseIdle(); } return IntPtr.Zero ; }
获取当前dispatcher
- 通过FromThread 函数获取 当前线程的dispacher
- 如果当前线程不存在dispacher,那么直接创建。
public static Dispatcher CurrentDispatcher { get { // Find the dispatcher for this thread. Dispatcher currentDispatcher = FromThread(Thread.CurrentThread);; // Auto-create the dispatcher if there is no dispatcher for // this thread (if we are allowed to). if(currentDispatcher == null) { currentDispatcher = new Dispatcher(); } return currentDispatcher; } }
我们可以new Thread ,然后执行获当前dispacher。
消息循环
//<SecurityNote> // Critical - as this calls critical methods (GetMessage, TranslateMessage, DispatchMessage). // TreatAsSafe - as the critical method is not leaked out, and not controlled by external inputs. //</SecurityNote> [SecurityCritical, SecurityTreatAsSafe ] private void PushFrameImpl(DispatcherFrame frame) { SynchronizationContext oldSyncContext = null; SynchronizationContext newSyncContext = null; MSG msg = new MSG(); _frameDepth++; try { // Change the CLR SynchronizationContext to be compatable with our Dispatcher. oldSyncContext = SynchronizationContext.Current; newSyncContext = new DispatcherSynchronizationContext(this); SynchronizationContext.SetSynchronizationContext(newSyncContext); try { while(frame.Continue) { if (!GetMessage(ref msg, IntPtr.Zero, 0, 0)) break; TranslateAndDispatchMessage(ref msg); } // If this was the last frame to exit after a quit, we // can now dispose the dispatcher. if(_frameDepth == 1) { if(_hasShutdownStarted) { ShutdownImpl(); } } } finally { // Restore the old SynchronizationContext. SynchronizationContext.SetSynchronizationContext(oldSyncContext); } } finally { _frameDepth--; if(_frameDepth == 0) { // We have exited all frames. _exitAllFrames = false; } } }
如果普通的windwos 窗口程序,一般只有一个消息循环,消息循环每个线程必须要有一个。如果你创建windows不在UI线程,那么必须自己循环,不然程序跑不起来。
Appliaction 继承了 DispatcherObject,那么默认创建一个dispacker的对象。
/// <summary> /// Instantiate this object associated with the current Dispatcher. /// </summary> protected DispatcherObject() { _dispatcher = Dispatcher.CurrentDispatcher; }
可以退出,如果wpf起码有2个窗口,dispacher创建窗口。
wpf 程序会调用dispacher Run
我通过自己写c++ 工具搜索消息窗口(消息窗口不能被枚举函数枚举,所以你用spy++是无法查找到这个窗口,必须通过下面FindWindowEx函数才可以)
void find_hwnd_message(HWND hw){ HWND child_hwnd = FindWindowEx(HWND_MESSAGE, hw, NULL, NULL); if(child_hwnd == NULL){ return; } wchar_t class_name[256] = {0}; GetClassName(hw, class_name, 255); wprintf_s(L"class_name: %s\n", class_name ); find_hwnd_message(child_hwnd); }
然后再main函数调用 find_hwnd_message (null); 这样子可以查询所有的消息窗口,你也可以填写class name 和 windows name 做过滤。
三、总结
application 启动时候就默认创建 dispacher,通过Dispacher 获取当前的,当前不存在就默认创建逻辑,然后启动主窗口,然后调用当前dispacher中Run ,用来消息循环,从而整个window 应用程序核心逻辑走完了。dispacher会绑定线程,会创建一个消息窗口。通过原理,我们可以推出来,dispacher是不能在很短时间做大量异步行为(如果消息还消耗时间的话),因为dispacher 消息处理不过来,阻塞界面。