Splash Screen To Improve WPF Application Perceived Cold Startup Performance
Technorati Tags: WPF,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).
Summary:
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.
Download Location:
You can download the sample from here.
Note:
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.
SplashScreenSample_1_10_2009.zip
Comments
Anonymous
January 23, 2008
made some small bug fixes to my WPF Calculator sample app (that is/was already included in the .Net 3Anonymous
March 12, 2008
hey, i had this working but I upgraded my project to vs2008. There is no rc.exe in the tools bin folder or anywhere in the Visual Studio 9.0 tree. I also did not see it in the vs2008 sdk - any ideas? Should I just use the rc.exe in vs2005? thanks, DanAnonymous
May 05, 2008
When I create a png file with rounded corners and transparent background the background of the corners on the splash becomes black. Any idea why this happens? Best regards, JesperAnonymous
May 13, 2008
Mint bizonyára hallottátok már, megjelent a .net 3.5 SP1 első bétája, vele aAnonymous
May 15, 2008
Anybody have a working version for Visual studio 2008? This does not working with it since the rc.exe is missingAnonymous
June 13, 2008
Having same issue here with sample not working on VS2008. I have tried using rc.exe for VS2005 but this does not do it.Anonymous
June 17, 2008
The comment has been removedAnonymous
June 17, 2008
The comment has been removedAnonymous
June 24, 2008
Excellent, thanks. Works for me now.Anonymous
June 26, 2008
Add the WS_EX_TOOLWINDOW flag to keep the splash window from showing up on the taskbar. private const uint WS_EX_TOOLWINDOW = 0x00000080; _splashScreenHwnd = CreateWindowEx( CS_HREDRAW | CS_VREDRAW | WS_EX_TOOLWINDOW, etc.Anonymous
August 12, 2008
Summary: To improve the perception of a more responsive startup experience many WPF applications addedAnonymous
August 26, 2008
Hi, I was recently told that MILCore is legitimately removed (also seen this in person) by the .net 3.5 SP1 installer on XP. Basically want to make sure you are aware anyone using this code just entered a world of hurt if they distributed on XP. Is there an explanation of why you felt MilCore.dll dependency was required for the png loading of the splash? what are the side effects if you just remove MILCore dependency entirely and let the common runtime handle the garbage collection or use the Marshal.Release() on the pointers? Any help would be much appreciated. Thanks, DAnonymous
September 01, 2008
As D. mentioned , since this sample has a dependency on milcore.dll (which was renamed to Wpfgfx_v0300.dll in .net 3.5 Sp1) it will not work on recently .net 3.5 Sp1. For 3.5 Sp1 it is recommended hat you simply use the new built in Splash Screen APIs. (See this blog: http://blogs.msdn.com/jgoldb/archive/2008/08/12/what-s-new-in-wpf-3-5-sp1-splash-screen-to-improve-perceived-startup-perf.aspx) The sample was written well before the decision to rename MILCore.dll in 3.5 Sp1 was made. I’ll try to fix the sample so that it does not depend on milcore.dll. Jossef.Anonymous
September 03, 2008
A simple solution would be to allow the CLR to make the Release calls itself and handle the reference counts. (you would just remove any part of the sample that contains reference to milcore) DAnonymous
September 03, 2008
It appears after inspecting the memory usage with swapping the call for Marshal.Release that using Marshal.Release may be more robust. I did speak with someone from msft and they seemed to be under the impression however that the CLR would take care of the interface references in this case. DAnonymous
October 01, 2008
I wanted to show a splash screen very quickly, so I wrote a small native application that can do so (without loading the CLR or any WPF assemblies). The native application launches my WPF application after the splash screen is displayed. I've blogged all the code required to create this application at: http://code.logos.com/blog/2008/09/displaying_a_splash_screen_with_c_introduction.htmlAnonymous
October 13, 2008
Hi - I am using Method 3 by specifying open and close by myself. I am using an app with a NavigationWindow. It appears that the SplashScreen is automatically closed by itself, even if I don't call close, when the NavigationWindow is navigated. Why is this? I still have the UI to load, etc., and don't want to be closed quite yet.... ?Anonymous
December 17, 2008
I compiled and built the code with .net 3.5 sp1 installed and it works fine.Anonymous
April 16, 2009
Is it possible to get rid of the window border around the splash image? (I can't make my application depend on 3.5 SP1 yet...)Anonymous
May 03, 2009
You know the drill. Raw/unedited conversations from our internal discussions.  Subject: Datagrid