BTW, I was told that all this code will also runs perfectly on the upcoming Windows 7.
What is the Vista Bridge?
I was introduced to the Vista Bridge late last summer when I was asked to do a presentation on that topic for the Microsoft TechDays event in Montréal.
Apart from being a real bridge in Portland, Oregon (see Wikipedia) it is a library that you can use from VB.Net and/or C# that gives you access to features provided by the Vista OS that are not directly available from .Net (for all kinds of good reasons – good or bad).
I consider this component much like the VB Power Packs I have already covered in March 2008 in the way that it is a temporary stage before being part of the official Visual Studio package. Instead of waiting for the next big release or service pack, you can start using this component a lot faster. For the team that develops it, it is also a good way of getting feedbacks.
The whole purpose of the VistaBridge is to offer features that are built into the Windows Vista platform but which are not available directly through the .Net Framework. It is provided much as a “sample” library which means that it doesn’t get the same level of support.
How can I get it?
The answer is easy but wait before navigating go to Microsoft downloads or MSDN because you won’t find it there.
Instead you will find it on MSDN Code Gallery which is yet another official Microsoft from where you can download sample applications and code snippets. The good news is that you don’t get only the component itself, you also get all the source code.
The other good news is that because it is distributed aside the official roadmap, chances are that we will get versions (or fixes) much faster.
The first time that component was made available to developers, it was through the Windows Vista SDK. This component was so deeply hidden into a ZIP file that most developers never found it. If you installed the SDK, have a look in C:\Program Files\Microsoft SDKs\Windows\v1.0\samples\CrossTechnologySamples.zip – you will find VistaBridge.zip in there. That was version 1.0. Then the same version (1.0) was available on MSDN.
After many months of waiting (due to the legal side of distributing source code and not to the content of the component), a new version has finally been made available in November of 2008. We now have access to the version 1.3.1 of the Vista Bridge and this article will target this specific version.
What’s in it?
There are a couple of interesting features that you can hardly get otherwise. In this article, I will explore only 3 of those features here:
What’s in the downloadable demo?
At the root of the ZIP file, you will find the 2 VistaBridge DLLs (VistaBridgeLibrary.DLL and VistaBridgeControls.DLL). Those DLLs were compiled with the version 1.3.1 as found on MSDN Code Gallery.
You will also find 2 sub-folders, each containing a different solution. Those solutions demonstrate the 3 features covered here.
I strongly encourage you to download the version that is on MSDN Code Gallery as it contains some more demos (and some are even WPF demos).
Detecting Windows version
You should always check which version of Windows you are running before trying to use one of the features of this article. The best way to do it is to query the My.Computer.Info.OSVersion and/or the My.Computer.Info.OSFullName properties (see the bottom of the form shown in figure 1) and branch to a different form if required.
The only feature presented in this article that can scale down is the Command Link. Instead of showing a nicely formatted button, it will display a big ugly one. All other topics of this article will trigger and exception.
The Command Link
The first feature I want to explore, is call the CommandLink. It is a control. That means that you need to add the VistaBridgeControls DLL to your toolbox and then you will be able to drag a CommandlinWinForms control to your Windows Forms. The very same control also exists for WPF application (in the same DLL).
Once you created an instance on a form, you will be able to find that you already know how to use this control because it is nothing else then a button! But this new button has 3 characteristics:
About the 2 sections of text, the first line can be considered has the title of the button and is set using the Text property. The second section has a smaller font and is set using the NoteText property. This NoteText section will only have effect if the FlatStyle property is set to System. Otherwise it displays a big regular button.
You will also notice that there is an icon right next to the button’s title. This icon is either a green arrow icon or a shield icon. It can be set using the Boolean ShieldIcon property (which is set to False by default). You should set this property to true (if you want to be a good Windows Vista citizen) when you know that the action that will be performed by clicking this button will require elevated privileges.
Figure 1: The CommandLInk button in action.
To react to this button, you have to use the click event just like for a regular button.
If you try to run an application displaying one of those controls on an older version of Windows, you will get a big ugly button (no icon and no additional text zone) but the application will work just as well.
I admit, this control is not very exciting but I promise that the best is yet to come.
The Task Dialog
Are all the questions an application has to ask a user can always be answered by Yes/No, OK/Cancel? The exact purpose of this TaskDialog class is to be able to offer users a good dialog to which he/she doesn’t have to say Yes or No. It is re-using the Command Links we just covered but it is much more then this as we will see here. The figure 2 gives you an example of one dialog which is much more complete then the plain old MessageBox.
Figure 2: The Task Dialog
If you have downloaded the code of this article, this dialog is still in the “UI Demo” solution but don’t look for a form that contains this dialog because it is all done by code and it is not as bad as it first appear.
So let’s break the code required to build this dialog. The first thing you need to do is to declare an instance of this dialog. I first thought that the constructor would have exposed many overloads to initialize some of the dialog properties but you cannot pass any values to the constructor. This is the dialog declaration:
Dim td As New TaskDialog
The first thing we see at the top of this form is the icon that is set using the MainIcon property which exposes an enumeration of 4 values (Error, Information, Shield, and Warning). The same icon is displayed in the title bar of the dialog.
Then comes the title of the dialog that you can set using the Caption property.
At the top-right of the dialog, you currently see a red-X letting you close this dialog. You can hide it and force the user to select one of the options by setting the Cancelable property to False.
The first 2 blocks of text inside the dialogs are set using the Instruction and the Content properties.
So far, the required code is:
td.MainIcon = TaskDialogStandardIcon.Information td.Cancelable = True td.Caption = "This is a sample Task Dialog" td.Instruction = "Follow these instructions to complete the task" td.Content = "This is the place where more detailed explanation goes. There is room" + _ " for plenty of text so you can cover a lot of ground for users who can't decide" + _ " which option to select."
Dim clStartOver As New TaskDialogCommandLink( _ "clStartOver", _ "Start Over", _ "Start again from the beginning") AddHandler clStartOver.Click, AddressOf clStartOver_Click td.Controls.Add(clStartOver)
Private Sub clStartOver_Click(ByVal sender As Object, ByVal e As System.EventArgs) MessageBox.Show("You chose to start over") End Sub
td.CheckBoxText = "You can provide an option like Always Use Saved File here."
You might also want to provide a footer text to your dialog. To achieve this, you have to set the FooterText property. This is the code required:
td.HyperlinksEnabled = True td.FooterText = "This footer text can explain even more of what is going on " + _ "(<A href=""http:=www.emoreau.com"">www.emoreau.com</A>)." AddHandler td.HyperlinkClick, AddressOf TaskDialogHyperlinkClick
Private Sub TaskDialogHyperlinkClick(ByVal sender As Object, _ ByVal e As Microsoft.SDK.Samples.VistaBridge.Library.TaskDialogHyperlinkClickedEventArgs) Process.Start(e.LinkText) End Sub
td.ExpansionMode = TaskDialogExpandedInformationLocation.ExpandContent td.CollapsedControlText = "Click here to expand" Dim strExpandedControlText As String strExpandedControlText = "This is where you would start writing you biography!" + _ Environment.NewLine + _ "This is a true story..." td.ExpandedControlText = strExpandedControlText td.ExpandedText = <S>This text will appear just under the Instruction section and before the first Command Link This is a true story... Line 1 Line 2 Line 3 Line 4 Line 5 Line 6 Line 7 Line 8 Line 9 Line 10 Line 11 Line 12 Line 13 Line 14 Line 15 Line 16 Line 17 Line 18 Line 19 Line 20 </S>.Value
We are now ready to show the dialog. We simply have to call the Show method like this (retrieving the results is optional but I need it):
Dim tdr As TaskDialogResult = td.Show()
If tdr.CheckBoxChecked Then MessageBox.Show("the Checkbox is CHECKED") Else MessageBox.Show("the Checkbox is NOT CHECKED") End If
The bad thing with this dialog is that outside the long list of properties covered here (and there are even some more) it is not very customizable. Say for example that you would like to display 2 checkboxes, you would be required to create a dialog yourself.
I found a little “issue” when playing with this control. If you set the ExpandedControlText to a long string value, an empty spot to fit that string value will appear at the bottom of your dialog even if your text is not in the expanded mode. I have added a comment to the MSDN Code Gallery and we will see how fast (or slow) the team will react!
When you download the package from the MSDN Code Gallery, you have other examples of this dialog with progress bar and other nice features that I haven’t explained here.
The Application Restart and Recovery
Since a long time, some applications like Microsoft Word and even Visual Studio automatically save your current document every X minutes so that the application can recover from it if anything bad happens. But this action consumes resources (disk space and CPU) just in case something goes bad (and I really hope your computer does not have to recover from those files every day!). It is a rudimentary mechanism but it works to some extent. What if Word automatically saves every 10 minutes and the application crashes after 9 minutes? You just lost 9 minutes of works.
Windows Vista offers a brand new mechanism that uses a lot less resources for the “just in case something bad ever happens”. This new mechanism is named “Application Restart and Recovery”. Instead of automatically saving data every X minutes, this new mechanism detect that the application has problems and let Vista sends an event to the crashing application letting it persist any information required and can also restart the application automatically. Isn’t it nice?
This mechanism is really a 2 step thing. In the first step, Vista tells you that you are going to die really soon and that is the right time to write your last wills. In the second step, which is fully optional, Vista resurrects you. These 2 steps are really independent from each other.
Before going on with the example, I must warn you of something. When I first start playing with this feature, I had the surprise that it wasn’t working “as advertized”. My application was always dying silently until I found a setting in the Windows Vista Control Panel which was short-circuiting this mechanism. As shown in figure 3, users have the choice of an automatic check or to be asked for how to deal with the problem. This dialog is available by opening the “Problem Reports and Solutions” applet from the Control Panel and selecting “Change Settings”.
Figure 3: Problem Reports and Solutions settings
I strongly suggest that you open the RestartDemo solution found in the downloadable file, that you build it and that you run the demo application by double clicking the RestartDemoVB.exe file that you will find in your debug folder after the build process. If you try to run the application from within Visual Studio IDE, the debugger tries to do is best not to have your application killed but this time that’s exactly what you want.
When you start the application, you see a very simple form. Write something into the textbox and wait at least one minute (the timer label background will turn green after one minute as shown in figure 4) before pushing one of the buttons. The minute that you have to wait is a protection. You can imagine in some weird circumstances an application crashing when it tries to start, would trigger the process of killing itself and recover to crash again. This is not very helpful!
Figure 4: This application will crash soon!
Now click the “Fatal Error” button (which will trigger a fatal exception in the application). If your “Problem Reports and Solutions” settings are not set to the automatic mode, you will see the dialog shown in figure 5. The exact wording of the first question (which is a Command Link in case you haven’t seen it) may differ depending on your Internet connection state (I was commuting to work with no Internet connection when I took this snapshot).
Figure 5: Restart?
There is absolutely no added value in having users selecting the first option (connected or not) because you cannot set the location to check for online solution to your own web server. It will always go to Microsoft servers and will never find a solution because your application is not registered there. If you click the “Restart the program” link, the dialog shown in figure 6 will then appear.
Figure 6: The application is now writing its last wills
While the figure 6 dialog is displayed on the screen, code of your application is currently running (and we will talk of the required code later). For this demo, this dialog will appear for quite a long time because fake loops are made to spend time so that you have the chance to see that dialog (and for me to capture it). In your real life application, the time it is displayed varies according to the work your application has to do before being killed.
If you have selected to restart the application, it will then restart. The first line of the textbox (this is a limitation of the demo application and not the mechanism) will be reinserted into the textbox just like it was before dying.
Now add something else to the textbox and when the label will turn green again (after a minute), click the second button titled Hang. Notice that the timer is not updated anymore. You will see a tight loop in the code later to simulate this situation. After about 5 seconds, try to move the application and Windows will tell you that the application is not responding (see figure 7).
Figure 7: Application is not responding
If you kill the non-responding application (the simplest way is by clicking the red X at top-right), you will get a dialog a bit different than the previous one as shown in figure 8. You will now have of the choice of restarting, closing or waiting.
Figure 8: The application died again
If you select the restart button, your application will restart and restore its current state.
I just demonstrate how this feature is working but said nothing about the required code yet. The persisting of the current state and the recovery is not done automatically, you have to write some code!
This simple application starts with the Main method in the Module1.vb file. It detects if a command line arguments was passed to it and call the form’s constructor using this argument which in this case will be the path of the file containing the data to restore after a crash. This is the important code of the Main method:
Dim recoverypath As String = "" Dim args() As String = Environment.GetCommandLineArgs() If (args.Length > 1) Then recoverypath = args(1) End If Application.Run(New Form1(recoverypath))
Public Sub New(ByVal pRecoveryPath As String) Me.New() If Not String.IsNullOrEmpty(pRecoveryPath) _ AndAlso File.Exists(pRecoveryPath) _ Then Me.lblRecoveryPath.Text = pRecoveryPath Dim r As StreamReader = File.OpenText(pRecoveryPath) textBox1.Text = r.ReadLine() _TextboxText = textBox1.Text r.Close() r.Dispose() r = Nothing '//should clean away the file - I am leaving it so it can be '//examined while debugging/understanding the process, or so '//restarts can be repeated under the debugger End If End Sub
The following section of code that we will explore is contained into the Form_Load event. The code there is responsible of registering to the notifications sent by Windows Vista.
There are 2 notifications to which you can register as shown in this example:
'Register the recovery notification Dim recoverySettings = New RecoverySettings( _ New RecoveryCallback(AddressOf SaveForRecover), Nothing, 5000) ArrManager.RegisterForApplicationRecovery(recoverySettings) 'Register the Restart Notification Dim restartSettings = New RestartSettings( _ Convert.ToChar(34) + _RecoveryPath + Convert.ToChar(34), RestartRestrictions.None) ArrManager.RegisterForApplicationRestart(restartSettings)
We now need to provide the method that will save the data from your dying application to a data store. Remember that it is not the time of being fancy as your application is currently dying. You better write the data you want into the simplest form you can.
Private Function SaveForRecover(ByVal Parameter As RecoveryData) As Integer Dim w As StreamWriter = File.CreateText(_RecoveryPath) w.WriteLine(_TextboxText) w.Close() '//simulated delay For i As Integer = 1 To 10 Dim b As Boolean = Me.Ping() If (b) Then Return 0 System.Threading.Thread.Sleep(2000) Next ArrManager.ApplicationRecoveryFinished(True) Return 0 End Function
The _RecoveryPath is a class level variable. You may write your file anywhere but keep in mind that your user might not have enough privileges to write anywhere. A good idea is to use the GetFolderPath and the ApplicationData special folder. This is how I declared my variable:
Private ReadOnly _RecoveryPath As String = _ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), _ "App Recov.txt")
_TextboxText = textBox1.Text
Private Function Ping() As Boolean 'ApplicationRecoveryInProgress returns TRUE if I hit the Cancel button Dim blnEsc As Boolean = ArrManager.ApplicationRecoveryInProgress() If blnEsc Then MessageBox.Show("Cancelled") End If Return blnEsc End Function
Just in case you wander how I made the application crashed or not responding, here is the code behind the 2 buttons:
Private Sub Hang_Click() Handles Hang.Click Do While True 'nothing to ensure that we loop tight! Loop End Sub Private Sub FatalError_Click() Handles FatalError.Click timer1.Enabled = False Dim zero As Integer = 0 Dim i As Integer = Convert.ToInt32(3 / zero) End Sub
Testing the recovery is really simple. You just need to provide the same parameters that would be provided by the code in the “Command line arguments” textbox of your project properties (as shown in figure 9), set a breakpoint into the constructor that accepts the argument (the one that fills the textbox) and you are done.
Figure 9: Setting the Command line arguments
If you have to trace the method that persist the data to a file, it is a bit more complicated but it can be done. This is the recipe you need to follow in this particular order (otherwise it is likely to fail):
Figure 10: Attaching the debugger to the process
Alternatives
You won’t find easy alternatives to all those features.
If you (or your users) are not running Windows Vista yet and still want to use something similar to the Command Links and/or the Task dialog, I found a Vista-like TaskDialog control that is compatible with Windows XP on CodeProject.
Conclusion
I am a bit disappointed of the support (or lack of support I should say) we get from the VistaBrdige team on MSDN Code Gallery. I posted a bug a long time ago about the ExpandedText property and had no comments since then.
If you as a developer and your users are using Windows Vista, you definitely need to download it and start using it today.
Start with the new Task Dialog. Your users will thank you for sure.