In WPF program, there may be Application.Current.Dispatcher.Xxx
such a code to let some logic back to the main UI thread. Because there have been discovered at the time of calling of this code NullReferenceException
, so there are three small partner told me Current
and Dispatcher
attributes as possible null
.
However, in practice this is only possible Current
as null
while this context Dispatcher
is definitely not to null
the. (Of course, we discussed here are conventional programming means, if unconventional means, you can even let instance this
is null
it ......)
When your application exits, all the UI thread code is no longer executed, so it is safe; but all the non-UI thread code still continue, this time may be encountered at any time Application.Current
property is null.
Because the two parts are slightly longer described herein, it is split into two blog, it is easier to understand.
- WPF's Application.Current.Dispatcher in, Dispatcher property must not be null
- WPF's Application.Current.Dispatcher. Why Current may be null
In this article
Application.Current
Static properties
Source
Application
Type of source code will be very long, so there is not posted, you can go here:
Among them, Current
the return of _appInstance
the static field. So _appInstance
the field is null
the timing that Application.Current
is null
the timing.
/// <summary>
/// The Current property enables the developer to always get to the application in
/// AppDomain in which they are running.
/// </summary>
static public Application Current
{
get
{
// There is no need to take the _globalLock because reading a
// reference is an atomic operation. Moreover taking a lock
// also causes risk of re-entrancy because it pumps messages.
return _appInstance;
}
}
Since the _appInstance
field is private, so only survey the class itself can find all assignment opportunities. (Reflection needs unconventional means excluded, because it means that the developer is more than funny - you can not blame yourself break the curse WPF framework)
The timing of the assignment
_appInstance
The timing of the assignment, there are two:
Application
Instance constructor (note oh instance constructor is not static constructor);Application.DoShutdown
method.
In Application
Examples constructor:
_appInstance
The assignment is thread-safe, which means that multipleApplication
configuration example of the thread-safety issues will not lead to_appInstance
a state field is wrong.- If
_appCreatedInThisAppDomain
istrue
, then, will throw an exception, this application domain organization create a secondApplication
instance of a type.
/// <summary>
/// Application constructor
/// </summary>
/// <SecurityNote>
/// Critical: This code posts a work item to start dispatcher if in the browser
/// PublicOk: It is ok because the call itself is not exposed and the application object does this internally.
/// </SecurityNote>
[SecurityCritical]
public Application()
{
// 省略了一部分代码。
lock(_globalLock)
{
// set the default statics
// DO NOT move this from the begining of this constructor
if (_appCreatedInThisAppDomain == false)
{
Debug.Assert(_appInstance == null, "_appInstance must be null here.");
_appInstance = this;
IsShuttingDown = false;
_appCreatedInThisAppDomain = true;
}
else
{
//lock will be released, so no worries about throwing an exception inside the lock
throw new InvalidOperationException(SR.Get(SRID.MultiSingleton));
}
}
// 省略了一部分代码。
}
In other words, this type is actually designed as a single example. After the first constructed example, the example of a single embodiment can be started.
Follow-up assignment
The only time to end this instance is the singleton Application.DoShutdown
method. This is the only _appInstance
assigned to null
code.
/// <summary>
/// DO NOT USE - internal method
/// </summary>
///<SecurityNote>
/// Critical: Calls critical code: Window.InternalClose
/// Critical: Calls critical code: HwndSource.Dispose
/// Critical: Calls critical code: PreloadedPackages.Clear()
///</SecurityNote>
[SecurityCritical]
internal virtual void DoShutdown()
{
// 省略了一部分代码。
// Event handler exception continuality: if exception occurs in ShuttingDown event handler,
// our cleanup action is to finish Shuttingdown. Since Shuttingdown cannot be cancelled.
// We don't want user to use throw exception and catch it to cancel Shuttingdown.
try
{
// fire Applicaiton Exit event
OnExit(e);
}
finally
{
SetExitCode(e._exitCode);
// By default statics are shared across appdomains, so need to clear
lock (_globalLock)
{
_appInstance = null;
}
_mainWindow = null;
_htProps = null;
NonAppWindowsInternal = null;
// 省略了一部分代码。
}
}
This code can call the public API are:
Application.Shutdown
Examples of methods- Cause
Window
several methods closedInternalDispose
( ) IBrowserHostServices.PostShutdown
Interface Method
Therefore, all direct or indirect calls to the above methods will result in local Application.Current
property is assigned null
.
Impact on the written code
From the above analysis that can, as long as you can in Application.DoShutdown
continuing to execute code after execution, then this part of the code will be faced with Application.Current
as a null
risk.
So, in the end what opportunity might encounter Application.Current
is null
it? This part of it is strongly associated with the business code readers used in the project.
But this part of the business code will have some common features to help you determine whether you might write encounter Application.Current
for the null
code.
This feature is: This code is Application.Current
not in the same thread .
And Application.Current
not in the same thread
For WPF program, most of your code may be generated by user interaction, even with the implementation of the follow-up code, it still is generated from the UI interaction. This code will not encounter Application.Current
for the null
situation.
However, if your code is triggered by non-UI thread, for example, in the Usb
device changes, the other end of the communication, some asynchronous code callback, etc., the code is not Dispatcher
whether to schedule impact, almost certainly executed. Therefore, Application.Current
even if the assignment is null
, and they do not know, we will continue to perform, so they encounter Application.Current
is null
.
It is a security thread on this nature.
Use Invoke/BeginInvoke/InvokeAsync
the code will not go wrong
Application.DoShutdown
The method was ShutdownImpl
packed, and all calls are entered from the packaging, so all could lead Application.Current
to null
code that will call this method, that is, it calls the Dispatcher.CriticalInvokeShutdown
instance method.
/// <summary>
/// This method gets called on dispatch of the Shutdown DispatcherOperationCallback
/// </summary>
///<SecurityNote>
/// Critical: Calls critical code: DoShutdown, Dispatcher.CritcalInvokeShutdown()
///</SecurityNote>
[SecurityCritical]
private void ShutdownImpl()
{
// Event handler exception continuality: if exception occurs in Exit event handler,
// our cleanup action is to finish Shutdown since Exit cannot be cancelled. We don't
// want user to use throw exception and catch it to cancel Shutdown.
try
{
DoShutdown();
}
finally
{
// Quit the dispatcher if we ran our own.
if (_ownDispatcherStarted == true)
{
Dispatcher.CriticalInvokeShutdown();
}
ServiceProvider = null;
}
}
All of close Dispatcher
calls there are two types, Application
called when the close is an internal method CriticalInvokeShutdown
.
- Immediately shut down
CriticalInvokeShutdown
, that isSend
the priorityInvoke
method of closureSend
priority callsInvoke
almost equivalent to a direct call (Why are equivalent instead of calling? Because also need to consider returning toDispatcher
the thread where the initialization). - Began to close
BeginInvokeShutdown
, that is the priority of the specifiedInvokeAsync
closed method.
Closed Dispatcher
means that all use Invoke/BeginInvoke/InvokeAsync
tasks will be terminated.
//<SecurityNote>
// Critical - as it accesses security critical data ( window handle)
//</SecurityNote>
[SecurityCritical]
private void ShutdownImplInSecurityContext(Object state)
{
// 省略了一部分代码。
// Now that the queue is off-line, abort all pending operations,
// including inactive ones.
DispatcherOperation operation = null;
do
{
lock(_instanceLock)
{
if(_queue.MaxPriority != DispatcherPriority.Invalid)
{
operation = _queue.Peek();
}
else
{
operation = null;
}
}
if(operation != null)
{
operation.Abort();
}
} while(operation != null);
// 省略了一部分代码。
}
Because of this termination code Dispatcher
execution thread is located, and all Invoke/BeginInvoke/InvokeAsync
code is also executed in this thread, so these tags are not concurrent. The code has been executed in this termination code before, but after this termination code will no longer perform any Invoke/BeginInvoke/InvokeAsync
task.
- All through
Invoke/BeginInvoke/InvokeAsync
the code by this indirect method (such as WPF controls related events) or call, will not sufferApplication.Current
asnull
. - All the UI thread using
async
/await
and use the default code execution context, we will not sufferApplication.Current
asnull
. (This means you are not using.ConfigureAwait(false)
, see when writing an asynchronous method, using ConfigureAwait (false) to avoid deadlock user - walterlv .)
The simplest example code
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace Walterlv.Demo.ApplicationDispatcher
{
class Program
{
[STAThread]
static void Main(string[] args)
{
var app = new Application();
Task.Delay(1000).ContinueWith(t =>
{
app.Dispatcher.InvokeAsync(() => app.Shutdown());
});
Task.Delay(2000).ContinueWith(t =>
{
Application.Current.Dispatcher.InvokeAsync(() => { });
});
app.Run();
Thread.Sleep(2000);
}
}
}
in conclusion
All of the above summary analysis:
- Any
Application
not in the same thread code that are likely to encounterApplication.Current
isnull
. - Any
Application
code same thread, are likely to encounterApplication.Current
isnull
.
This is actually a thread-safety issues. With all business developers can understand the argument description is:
When your application exits, all the UI thread code is no longer executed, so it is safe; but all the non-UI thread code still continue, this time may be encountered at any time Application.Current
property is null.
So, remember all the non-UI thread of code, if required transfer to the UI thread execution, remember the empty sentence:
private void OnUsbDeviceChanged(object sender, EventArgs e)
{
// 记得这里需要判空,因为此上下文可能在非 UI 线程。
Application.Current?.InvokeAsync(() => { });
}
Application.Dispatcher
Instance Properties
About Application.Dispatcher
the possibility to null
analyze due to longer, please see my other blog post:
Reference material
My blog will be starting in https://blog.walterlv.com/ , and will be released from CSDN which featured, but it is rarely updated once released.
If you see any do not understand the contents of the blog, please share. I built dotnet Vocational and Technical College welcome to join.
This work is Creative Commons Attribution - NonCommercial - ShareAlike 4.0 International License Agreement for licensing. Welcome to reprint, use, repost, but be sure to keep the article signed by Lu Yi (containing links: https://walterlv.blog.csdn.net/ ), shall not be used for commercial purposes, a work based on the article be sure to modify the same license release. If you have any questions, please contact me .