LOB (Line of Business) applications executing within a web browser are fine, but if we think from the perspective of a final user, it is not the option that they would choose first. This is due to the fact that:
- Having a desktop application is easy. It can be directly accessed by double-clicking so that it is launched quickly.
- Most of the time, it is necessary to leave Silverlight sandbox application. This happens, for example, when we need to access the filesystem, or with a special hardware via COM (such as an ATM).
- It is also crucial that, when executing an application, it allows us to work offline.
Similarly, from the point of view of the user, there are certain restrictions which are normally not well accepted, even though they are 100 percent desktop (namely WPF). It would be great to have the possibility of eliminating them:
- Installing a desktop application can carry problems, depending on the OS and installation details. It would be fantastic if they could run under a standard, which is independent from those details.
- In order to update a version already installed, it must be uninstalled and installed again. Wouldn't it be preferable that the application itself detected if there is a new version and updated automatically (even though this process is not dramatic)?
- Usually, desktop applications only work on a particular OS (that is, Windows). We would love to execute it in different platforms (for example, Mac).
- A desktop application normally has quite relaxed permissions. Hence, we dream of an application having restricted access to the hardware and software of our machine. It would only have permissions if allowed.
- A desktop application needs a given version of the .NET Framework installed on the machine.
In this chapter, you will learn how to install and execute our applications as Out of Browser (OOB), work with elevated permissions in and out of the browser, support cross domain calls, and work with the WebBrowser control.
Out of Browser (OOB)
Out of Browser (OOB) applications are executed out of the web browser, or that is the impression they give.
Regarding the final user, an OOB application is similar to a desktop application; the user installs it, gets a direct access icon to it, and when executed, it runs under a standard window, as shown in the following screenshot. It can even be uninstalled through the Control Panel.
From the point of view of the Software Developer, an OOB application still runs under a hidden browser, that is, it is a Silverlight application with the same features as an application running under a browser.
Until the arrival of Silverlight 5, the main difference with an application executing in the browser was the fact that it could work with trusted permissions and perform operations, which the sandbox normally does not allow (accessing the filesystem, executing commands via COM+, and so on).
Executing an application in OOB mode
Allowing a Silverlight application to be executed as an OOB is very easy. We just need to tick a checkbox in the project settings. Let's see how to do it.
First, create a new Silverlight application called 01_Simple_OOB_App. In the MainPage, a text block must be added displaying the message Simple OOB App. When you execute it, it will look as a standard in-browser application, as shown in the following screenshot:
In order to enable OOB mode, follow these steps:
1. Right-click on the Silverlight project root. When the context menu is displayed, click on Properties:
2. Select the Silverlight tab and tick the checkbox with the message Enable running application out of the browser:
3. When executing the application, it still runs in the browser but, if we right-click on it, there will be a new option Install 01_Simple_OOB Application onto this computer… in the context menu, as shown in the following screenshot:
4. If we click on that option, we get a message prompt asking to install the app, as shown in the following screenshot:
5. Once we click OK, the application is installed and ready to be used as OOB.
It is great to use apps as a final user, but what happens with developers? In case we want to debug our OOB application, will we have to install it every time we run the environment? Of course not. In the project properties (Context menu | Properties), the Debug tab allows us to choose that the application starts up straight in OOB mode (the Silverlight project will have to be chosen as a start-up project):
Enhancing the experience—tooling up and updating
As we have already seen, enabling the OOB mode in our application is something quite straightforward. Nevertheless, the following doubts may have arisen:
- In some cases, I want to know if my application is running in normal or OOB mode to, for instance, show one UI or another. How can I do that?
- How can I know if the application is already installed?
- When installing from a not very intuitive contextual menu, is there any way to display our own UI in order to allow the user to install the application?
- How do I uninstall an OOB application?
- I would like to let the users install my application from a CD, eliminating the need to be connected to the Web. Is that possible?
- When a new version of my application is uploaded to the production server, is there any way to detect updates and install new versions automatically?
In-browser/OOB detection
Sometimes, the UI we want to show differs depending on whether the application is executing in the browser or as an OOB application. It may also happen that, when it is in-browser, we only want to display a button to install it.
In order to know the mode we are working with, Silverlight offers a function on the level of the application called IsRunningOutOfBrowser, which returns true or false depending on whether the application is executing in OOB mode or not.
To see how it works, let's go back to the previous example, TestOOB, and indicate whether the application is executing in OOB mode or not in the TextBlock we use in the main page. It can be done using the following steps:
1. Open the project previously created (Simple_OOB_Application).
2. Add an ID to the TextBlock of the main page (it will be named tbStatus):
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock x:Name="tbStatus" Text="Simple OOB App"
FontSize="16"/>
</Grid>
3. In the constructor of the page, check the value of the application variable IsRunningOutOfBrowser, which will display one of two messages depending on the state.
public MainPage()
{
InitializeComponent();
if (App.Current.IsRunningOutOfBrowser == true)
{
tbStatus.Text = "I'm running out of browser";
}
else
{
tbStatus.Text = "I'm running in browser";
}
}
4. Therefore, in case we are executing in OOB mode, the following message will be displayed:
Detecting the application installed
Another common scenario arises when the application is being executed in the browser and we need to know if it has already been installed as OOB. For instance, a common example of this is when we want to display a message asking the user whether to install the application or not. We can make use of the application variable InstallState, which returns a type enumerated with one of the following values: NotInstalled, Installed, Installing, or InstallFailed.
Thus, in the sample application that has been created, it could be checked whether the application is installed or not in the following way:
public MainPage()
{
InitializeComponent();
if (App.Current.InstallState == InstallState.Installed)
{
tbStatus.Text = "Application Installed";
}
else
{
tbStatus.Text = "Application not installed";
}
}
Installing the custom interface
As mentioned previously, installing the application from the contextual menu of our Silverlight application was not intuitive at all. Perhaps it could be possible to place a button so that the user could begin the installation. Up to what point can we customize the installation process of our Silverlight application? Silverlight allows us to launch the installation process by code, as long as it comes from a user petition (such as a click on a button). The dialog which asks if we want to install the application cannot be customized.
To install the application, the method Install of Application must be called. Let's add this functionality to our basic sample:
1. Add a button to the main window:
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock x:Name="tbStatus" Text="Simple OOB App" FontSize="16"/>
<Button Content="Install" Height="23" HorizontalAlignment="Left" Margin="142,55,0,0"
Name="button1" VerticalAlignment="Top" Width="75" />
</Grid>
2. Subscribe to the Click event:
<Button Content="Install" Height="23" HorizontalAlignment="Left"
Margin="142,55,0,0" Name="button1" VerticalAlignment="Top"
Width="75" Click="button1_Click" />
3. Check if the application has already been installed in the handler of the button and add the call to Install:
private void button1_Click(object sender, RoutedEventArgs e)
{
if (Application.Current.InstallState == InstallState.NotInstalled)
{
Application.Current.Install();
}
else
{
MessageBox.Show("Application already installed");
}
}
4. Execute the application. When you click on the new button, the dialog box asking you if you want to install the application will be displayed, as shown in the following screenshot:
Uninstalling an OOB application
To uninstall a Silverlight application, the user can choose among several options:
- They can right-click on the application and, in the contextual menu, choose the Remove this application… option, as shown in the following screenshot:
- It can be uninstalled right from the Windows Control Panel:
The customization of the installation is an improvement that is pending to be added to Silverlight.
Offline installation
Sometimes, we face scenarios where it is necessary for the user to be able to install the application from a CD, or that an administrator can make an implementation. This scenario is not ideal for Silverlight applications (normally, the user downloads them from a URL, either intranet or extranet and, moreover, benefits from automatic updates).
If you face this situation, you may wonder whether it is more convenient to orient development to WPF-Click once, or use the method explained by Tim Heuer in his blog: Installing Silverlight Offline (http://timheuer.com/blog/archive/2008/09/29/install-silverlight-2-rc0-offline.aspx).
Even though this method allows us to install our OOB application in a silent mode,
it has certain limitations:
- If the user does not have the Silverlight plug-in installed, they will need to be connected to the Internet to download it from the Microsoft website
- The installation in silent mode is not valid for trusted OOB applications
Updates
OOB applications are installed on our own machine, which means there are a lot of advantages, such as fast boot, offline work mode, and so on. Nonetheless, what happens if we upload an update to the server? Is there a way to update our local application? Of course there is. What's more? It is possible in an easy and powerful way. Let's see it in action:
In the app.cs file, subscribe to the event named CheckAndDownloadUpdateCompleted and, later, make the asynchronous petition to check if there is a new update and, if so, download it automatically. The following is the source code:
private void Application_Startup(object sender, StartupEventArgs e)
{
CheckAndDownloadUpdateCompleted +=
new CheckAndDownloadUpdateCompletedEventHandle
r(Application_CheckAndDownloadUpdateCompleted);
CheckAndDownloadUpdateAsync();
(…)
}
private void Application_CheckAndDownloadUpdateCompleted(object sender, CheckAndDownloadUpdateCompletedEventArgs e)
{
MessageBox.Show("Application updated, please restart.");
}
|
|
Updates will work as long as our XAP is not in a file that needs security.
|
|
Offline work
An advantage of working with OOB applications is that, once they have been installed, it is not necessary to have an Internet connection to execute them (they are downloaded locally). What does this mean? It is possible to implement an offline work method for our applications, which permits, for example, that a user on a flight can work with the application and, later, when they have an Internet connection, they can synchronize the data with the server.
To work offline, it is necessary to have a repository to store reference data, as well as changes or new creations. If our Silverlight applications normally work within a sandbox and we do not have access to the HDD of the local machine, what can we make use of? In such cases, we have two options: either requiring elevated permissions for our application to execute (this will be dealt with later) or making use of the Silverlight Isolated Storage.
Isolated Storage is a virtual filesystem that allows a Silverlight application to store data in an invisible folder in the machine. By default, a Silverlight application can use 2 MB of storage. Isolated Storage is a 10 MB OOB application, but the user can
be asked to increase the quota.
Let's see a simple sample of how to read and write data in the Isolated Storage. This example is available in the online material on http://bit.ly/5gEwuM.
You can create a file in the Isolated Storage using the following code:
using System.IO.IsolatedStorage;
using System.IO;
(…)
private void WriteContentToIsoStorage(string content, string filename)
{
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(filename, FileMode.Create, isf))
{
using (StreamWriter sw = new StreamWriter(isfs))
{
sw.Write(content);
sw.Close();
}
}
}
}
You can read the information using the following code:
using System.IO.IsolatedStorage;
using System.IO;
(…)
private string LoadContentFromIsoStorage(string filename)
{
string data = String.Empty;
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(filename, FileMode.Open, isf))
{
using (StreamReader sr = new StreamReader(isfs))
{
string lineOfData = String.Empty;
while ((lineOfData = sr.ReadLine()) != null)
data += lineOfData;
}
}
}
return data;
}
|
|
To continue learning how this works, you can follow this link: http://bit.ly/qkY9UM.
|
|
How it works
Normally, when data have to be obtained or updated, we communicate with the server via a web service to get or modify this information.
To avoid this connection, we can do the following: first, get the most commonly read data and store them in a cache; second, the user creates his/her own entries, which are stored in the local repository (Isolated Storage). Once the user has an Internet connection again, offline data is sent to be synchronised in the repository.
Offline work is not as easy as it may sound. Another book could be written only on this! If you want further information about this, we can recommend the following session by Steve Lasker, Offline Microsoft Silverlight Applications (http://bit.ly/ltIxNt).
Breaking the sandbox—trusted applications
Up to now, we have seen how an OOB Silverlight application runs within a sandbox. That is to say, its access to certain resources is limited or restricted. What happens if we need to perform operations that the sandbox has restricted due to safety reasons? To solve this issue, there are applications with elevated permissions:
- A trusted OOB application is a program the user trusts (similar to when a desktop application is installed on our machine).
- Its XAP file is signed with a certificate, which ensures its trustworthy origin.
- It has elevated permissions, which makes it possible, for example, to make calls to COM components, P/Invoke calls, or access the local filesystem.
- For the application to be installed/executed, it needs the express authorization of the user.
Enabling trusted mode
How can an OOB application be enabled to require elevated permissions? To configure a Silverlight program that already exists, follow these steps:
1. Go to the Properties tab of the Silverlight Project (contextual menu of the project, Silverlight | Properties) and click on the button Out-of-Browser Settings…:
2. A dialog is displayed, where the option Require elevated trust when running outside the browser must be checked, as shown in the following screenshot:
After these steps, our application will be enabled as OOB trusted. However, if a user tries to install it, they will receive a warning indicating the source that published the application cannot be trusted. To eliminate this message, we will have to use our enterprise's certificate or acquire a new one (we can buy one or, if it is an intranet, our IT administrator can generate a new one). This operation can be performed from the Signing tab in the project's properties (right-click on Silverlight project | Properties | Signing).
Advantages of trusted applications
- Now we have our trusted application, which has the following additional advantages:
- We can access the filesystem of the machine (from the Silverlight 5 version).
- We can make calls to COM components. We can read or write to the registry (only current user entry), call other executable files, and so on.
- It is possible to make P/Invoke calls.
- We can make petitions to a URL, even if they are cross domain. It is not necessary that the server has a cross-domain policy enabled.
- It is possible to integrate a WebBrowser control in our application. That is, we can display HTML in our Silverlight application and interact with it.
- Real windows can be created. Normally, when new windows are created in a Silverlight application, they are a fantasy; that is to say, they are not physical windows, but they are displayed within the main one. If our application is trusted (only SL version 5), we can create floating windows and, for instance, they can be displayed on several monitors.
Accessing files
Our trusted application has access to the local filesystem (if it is Silverlight 5; in case it is Silverlight 4, it can only access folders, such as My Documents and via COM calls it's possible to gain additional access). The operations on System.IO, which previously returned safety errors, will now be executed flawlessly.
As a sample, let's see how the content of the Program Files file can be listed in our trusted Silverlight application:
public class ViewModel
{
public ObservableCollection<string> DirNames { get; set; }
public ViewModel()
{
DirNames = new ObservableCollection<string>();
// Let's try to enumerate the directories that are
// under Program Files, this operation would throw an
// exception if the application is not a trusted one.
DirectoryInfo di = new DirectoryInfo("C:\\Program Files");
foreach (var info in di.EnumerateDirectories())
{
DirNames.Add(info.FullName);
}
}
}
Making calls to COM+
We can make calls to COM components, which open plenty of doors, such as reading and (partially) writing on the registry, communicating with devices, launching apps, and even using Excel Automation.
However, the main disadvantage of this approach is that it only works in Windows. So if our application needs to run in Mac, this code must be isolated and will not offer this functionality for that platform.
|
|
If you need to detect under which OS a Silverlight application is running, you can use Environment.OSVersion.
|
|
Let's see a couple of examples of how to make these calls.
Writing an entry on the registry
In order to do so, create a new Silverlight Application project and tick the checkboxes (Enable Running application out of the browser, Out-of-Browser Settings | Require elevated trust when running outside the browser).
Add the reference to the Microsoft.CSharp DLL (Add Reference | .NET | Microsoft.CSharp):
Make sure this DLL and System.Core are referenced:
You can write the entry on the registry using the following code:
using System.Runtime.InteropServices.Automation;
(…)
private void button1_Click(object sender, RoutedEventArgs e)
{
using (dynamic shell = AutomationFactory.CreateObject("WScript.Shell"))
{
shell.RegWrite(@"HKCU\Software\MyTest", "");
}
}
The result is as shown in the following screenshot:
Executing notepad from our application
Another interesting feature is the ability of launching other applications from our trusted application. In the following code we will launch a Notepad.
using System.Runtime.InteropServices.Automation;
(…)
private void button1_Click(object sender, RoutedEventArgs e)
{
dynamic cmd = AutomationFactory.CreateObject("WScript.Shell");
cmd.Run(@"c:\Windows\notepad.exe", 1, true);
}
P/Invoke
As an innovation, Silverlight 5 includes call support via P/Invoke. Platform Invocation Services allows managed code to call unmanaged functions that are implemented in a DLL. This is only allowed for full-trust applications.
Let's see how this works by creating a sample, which calls a kernel32.dll method reproducing system beeps. Follow the steps mentioned next:
1. Create a new Silverlight project (application) and configure it to be an OOB application with trusted permissions.
2. Add a new class (by means of Add New Class) called MyBeep. Implement the Beep method of the Kernel 32 DLL.
using System.Runtime.InteropServices;
public class MyBeep
{
[DllImport("User32.dll")]
private static extern Boolean MessageBeep(UInt32 beepType);
public void PlaySound(BeepTypes type)
{
if (!MessageBeep((UInt32)type))
{
throw new Exception("Beep failed!");
}
}
}
public enum BeepTypes : uint
{
Ok = 0x00000000,
Error = 0x00000010,
Warning = 0x00000030,
Information = 0x00000040
}
public static class BeepTypeExtensions
{
public static BeepTypes AsBeepTypeEnum(this string beepType)
{
BeepTypes beepTypeEnum;
return Enum.TryParse(beepType,true,out beepTypeEnum)
? beepTypeEnum
: BeepTypes.Error;
}
}
3. In the main page (mainpage.xaml), add a button to be bound to the Click event as follows:
<Button Content="Beep !!"
Click="button1_Click" />
4. In the Code-Behind (MainPage.cs), make the call to the method defined in the MyBeep class.
private void button1_Click(object sender, RoutedEventArgs e)
{
MyBeep myBeep = new MyBeep();
myBeep.PlaySound(BeepTypes.Information);
}
|
|
If an exception appears when executing the action, check the application settings and make sure that it is running in OOB trusted mode.
|
|
Cross-domain calls
When executing our Silverlight application within the sandbox, we can make calls to services, or ask for resources, which are in our domain. However, if we try to access resources in a different domain (namely a feed of Apple's film trailers) this call can return an error since, due to safety reasons, these calls are restricted unless the web service authorizes them explicitly. The following are the solutions we have within sandbox:
- The server may have an XML file with the cross-domain policy indicating we have access to these resources. It can be checked by launching a query to the Apple feed and using the Fiddler packages sniffer. We will then realize if this server exposes a cross-domain policy.
- It is possible to implement a service in our server acting as a proxy. The bad thing about this approach is that we overload the server and the response to the client is slower.
If our application is executed as trusted, this restriction does not exist. We can make the petition to a cross domain without having the cross-domain policy enabled. For instance, we can make a query to the Apple feed to check new cinema openings.
private void button1_Click(object sender, RoutedEventArgs e)
{
WebClient wc = new WebClient();
wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);
wc.DownloadStringAsync(new Uri("http://www.apple.com/trailers/home/xml/current.xml", UriKind.Absolute));
}
void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
MessageBox.Show(e.Result);
}
WebBrowser control
Another interesting control we can only use in trusted OOB applications is the WebBrowser control. This allows us to view HTML pages within our Silverlight application. Besides, if the page is in the same domain, we can even interact with it via Silverlight | JavaScript.
How it works
Let's check how this control works by creating a simple example:
1. Create a new Silverlight project (application) and configure it so that it is executed as an OOB application with trusted permissions.
2. In the XAML, add the namespace System.Windows.Controls and instantiate the control:
<UserControl x:Class="WebBrowser.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/
markup-compatibility/2006"
xmlns:controls="clr-namespace:System.Windows.Controls;
assembly=System.Windows"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<controls:WebBrowser Source="http://www.bing.com"/>
</Grid>
</UserControl>
3. The result we obtain is shown in the following screenshot:
Limitations to take into account:
- Control is placed on top of the ZOrder. In case we want to show a ChildWindow, for example, this will not be visible at first. To give it visibility, we need to create a rectangle using a WebBrowser Brush, make a static picture to the WebBrowser Control content and, when the dialog box is shown, hide the WebBrowser showing our rectangle.
- For safety reasons, we cannot capture the moment when the user navigates from one page to another; neither can we make JavaScript calls from Silverlight if the page belongs to a different domain.
- It is a heavy control, so it is not a good idea to create and destroy it dynamically quite often.
Real windows
Another advantage of trusted OOB applications is the fact that they can show physical windows.
At the moment, these windows are not modal in Silverlight 5. The following is how it works:
1. Create a Windows object and assign the content property of this control to
a given UserControl.
2. Then, we see how this sort of window can be displayed:
private void button1_Click(object sender, RoutedEventArgs e)
{
Window wnd = new Window();
wnd.Width = 500;
wnd.Height = 350;
wnd.Title = "This is a test window";
//We indicate here a custom user control to display in the
//new window
wnd.Content = new MyControl();
wnd.Visibility = Visibility.Visible;
}
3. The result is shown in the following screenshot:
In-browser trusted applications
Having trusted applications, which are executed as if they were desktop applications, is a powerful and interesting idea, but what happens if we need to have elevated permissions for applications running in the browser? For instance, a bank's intranet.
Silverlight 5 incorporates in-browser trusted applications, whose main features are as follows:
- It is a specific functionality for enterprise applications.
- The administrator controls which applications can be executed via
group policy.
- The user is not asked and neither is the application installed. It is marked
as valid, so it will be executed by the administrator.
- It can be integrated as part of an HTML website, without the need to give elevated permissions to the entire site.
On the other hand, the doubt arises, when developing, shall we create our own test certificate? The answer is no. If we execute from localhost, the restriction is not applied.
Thus, in the example that was previously implemented (getting the content of C:\Program Files), we only need to select the properties of our Silverlight project (right-click on Silverlight | Properties) and select the option Require elevated trust when running in-browser, as shown in the following screenshot:
When executing the application, it can be seen running flawlessly within the browser.
As we pointed out earlier, if the application in production is displayed, it will fail as it is not executed from localhost. The following are the necessary steps to avoid it:
- The XAP file has to be signed with a certificate (this can be seen in depth in Chapter 11, Security).
- The network administrator has to specify a setting for the trusted in-browser applications to be executed in all machines. Particularly, the flag of the registry entry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Silverlight\AllowElevatedTrustAppsInBrowser must have the value of 1.
- The network administrator has to add the certificate in which the XAP has been signed with as a trusted certificate (CurrentUser\Trusted Publishers).
For more information about the display process, please visit the link http://bit.ly/kKbdpl.
|
|
In order to know if the application is running with elevated permissions, check the flag Application.Current.HasElevatedPermissions.
|
|
LOB application case study: applying what we have learned
As final users, one of the disadvantages of using web applications is the fact that we have to remember URLs, open the browser, and so on. Isn't it easier to double-click on a desktop icon and run an application? That is what has been added to our booking application:
- We detect whether it is running or not within the browser
- If so, an option is shown to install the application as OOB
- Then, we install the application as OOB
Thus, when executing the application within the browser, a new option is added in the navigation menu, as shown in the following screenshot:
When clicking the Install button, the confirmation dialog appears and the application gets installed (then, go to the desktop icon and execute it).
It is necessary to bear in mind that the application item has been customized. This can be set up in the project properties, Out-of-Browser Settings.
Summary
The capabilities offered by Out of Browser (OOB) applications are amazing. If we also add the possibility of elevating permission (in-browser and OOB trusted), the result is a light web application that can be almost as powerful and functional as a desktop application. Remember this chapter when a client indicates non-standard requirements of a web application (such as the integration of the application in an ATM, accessing the local filesystem, and so on). Anyway, before accepting, make the possible concept tests and ensure the functionality is covered.
Additional resources
If you need to dive deeper into any of the features presented in this chapter, you can check the following links:
- How far can I get using COM+? This post by Justin Angels summarizes it nicely at http://bit.ly/4Ama4H
- In-browser http://bit.ly/kKbdpl and settings http://bit.ly/oKFbzG
- P/Invoke, how does it work? http://bit.ly/r2DTQF
- P/Invoke and Silverlight, an excellent introduction by Vikram Pendse http://bit.ly/qI3XhN
- How to create physical windows, by Pete Brown http://bit.ly/gZ4w7S