Splash Screen To Improve WPF Application Perceived Cold Startup Performance
Note: The original sample had a dependency on milcore.dll (which was renamed to Wpfgfx_v0300.dll in .Net 3.5 Sp1) so it did not work on .Net 3.5 Sp1 or later. On Jan,13, 2009 I update the sample not to depend on milcore.dll (it now uses Marshal.Release instead). I also updated the Resource compile command.
For 3.5 Sp1 it is recommended hat you simply use the new built-in Splash Screen APIs. (See blog).
As was mentioned in the Improving WPF applications Startup Time blog, disk IO has huge affect on application cold start time.
When a WPF application is launched for the first time after reboot, it needs to access the disk to load many of Common Language Runtime (CLR) and WPF code pages that otherwise may be present in the OS memory manager's standby list.
One of suggestions mentioned in that blog, was to add a splash screen that will show as-soon-as-possible and will be followed by the main WPF app window once the code on the start up path is executed. This will present the user with some quick UI and will improve the overall user-perceived responsiveness of your application.
This blog provides a basic sample that demonstrates how this can be done.
The main idea is to use as little as possible managed code during startup and to use interop to display as-quickly-as-possible an unmanaged Win32 windows on the screen.
The sample provided shows a static image in that first Splash window but your application can display anything it wants (maybe as series of images to adversities some of the application features).
The sample code allows you to select either a .BMP or a .PNG image. These images are compiled into a win32 (.rc) resource file.
Why PNG vs. BMP ?
The advantage of using a .PNG image over .BMP for your splash screen is that you will have better control on your Splash UI (for example, provide transparency if combined with UpdateLayeredWindow() API [which is not used in this sample]) and also will reduce the overall size of your app. Therefore, it will improve performance by decreasing the coldstart and application deployment time .
In this sample the PNG version of the app is about 7 times smaller than the BMP one (560Kb vs. 76Kb)
Unlike other samples that uses GDI+, this sample uses WIC (Windows Imaging Components) to process the .PNG image (see code in CreateBitmapBits() ).
Using WIC to process .PNG requires 7 additional DLLs (in addition to what the .BMP version requires) that are loaded at startup (milcore.dll, oleaut32.dll, psapi.dll, WindowsBase.dll, System.dll, WindowsCodecs.dll and xpsp2res.dll)
At first look this may appear costly, however, these DLLs are required anyway by a typical WPF app, so the time spent on loading these DLLs is not wasted and the overall startup performance should not decrease.
Another approach is to use GDI+ to process the .PNG image, however, this approach is not recommended as will load the GDI+ DLLs which are not required by a typical WPF application and therefore will increase the overall startup time.
By the default the sample is set to use .PNG resource. To change and use a .BMP, perform the following steps:
1) Simply undefined the “USE_PNG” from the ‘Pre Build Events’ define in the ‘Build Event’ project property page.
E.g. do: "$(DevEnvDir)..\tools\bin\rc.exe" /u USE_PNG /i "$(DevEnvDir)..\..\vc\PlatformSDK\Include" /r "$(ProjectDir)win32res.rc"
2) Remove the ‘USE_PNG’ ‘Conditional compilation symbols’ from the ‘Build’ project property page.
Here are the steps I took to create this sample:
1. We create a win32 resource file. For example:
2. The Win32 window uses a Win32 resource file that is built using a custom pre-build event in the project build. In Visual Studio 2005 we add a pre-build event and use a resource compiler to compile the resource file . The following command is used. You may need to adjust the paths to your system:
3. In the code, we first override the application Main method.
4. We read either the PNG or BMP image from the app resource. In the PNG case we used the WIC Interop API to covert the PNG image to a bitmap format bits.
5. We then create a win32 window using pInvoke.
6. We then override the Application class we override the InitializeComponent() method and load the application main window. Notice that we no longer have an Application XAML file
Results and summary:
Examining the modules (a good tool to do so is the Process Explorer) that are loaded by the win32 splash window and then by the main WPF application window can explain why the splash window starts up much faster than the main WPF window. You can easily see that in the .BMP case, the WPF main app window loads 21 additional modules.
In addition to slowness because of disk IO, when the WPF platform starts it needs to perform some initialization which causes additional delay to application startup time. For example, when WPF starts for the first time it needs to launch the PresentationFontCache service as well as to initialize the DirectX device among other things.
The splash window technique can really help end-users with an improved perceived startup time and give the impression that the app is more responsive.
Testing on my (slow) XP machine after reboot (coldstart), the Splash screen appeared after ~2-3 sec while the main application windows appeared after ~13 seconds, a significant 11 sec improvement.
Improving the startup time of WPF application is something that Microsoft is working very hard to improve in coming versions. Until such improvements are built-in the platform, it is recommended that you use the Splash screen technique as well as some of the other ideas mentioned in the Improving WPF applications Startup Time blog.
You can download the sample from here.
As mentioned in this article , you can expect that Windows accelerators (in specific Prefetch and Superfetch ) will approve application startup time depends on how frequently the user uses the WPF application or other managed apps
Special thanks to Adam Smith who helped review this blog.