
July 20, 2004
Download the accompanying source code
Introduction
Sometimes the best installer is none at all. What could be simpler than copying
your executable to a web server and letting your users run the application directly
from there? All you have to do to rollout a new version is to copy a new set
of assemblies and supporting files to the virtual directory on the site. Users
are automatically running the latest version. It sounds too good to be true.
Well, like everything that sounds too good to be true, there’s the “yeah,
but” part. In this case, the “yeah, but” part is security.
In this article, we’ll look at the restrictions imposed by the .NET security
policy, and how you can work within them.
The ability to run a .NET executable residing on a web server is a technology
developed in response to a number of limitations with other environments used
for building web-based applications. Most notably, that web forms create a user
experience that runs the gamut from painful to downright unworkable. As an example,
I was posting a blog post a couple weeks ago. I made a simple typo, and hit
the “backspace” button. Unfortunately, I bumped a mouse button and
focus had moved off the edit control. The sad result: I lost a two page post.
Similar problems occur in every web application everyday. Another advantage
of Windows forms is that you have a richer palette of tools to develop windows
forms applications than you have for web forms applications. Would you rather
write applications in C# or VB.NET, or is EcmaScript your language of choice?
In short, a browser was built for browsing. The lower cost of development and
deployment has made web applications ubiquitous. These applications are exactly
where you should look for opportunities to make use of zero deployment applications.
You can improve the user experience by replacing the web interface with a downloadable
smart client.
This form of zero-touch deployment works only for applications that are 100%
managed code. The CLR can verify the safety of a managed application. If your
application contains any unmanaged code, even raw memory access in C#, the CLR
will not run your application.
As an example, I wrote a small windows forms application that interacts with
a web service. It’s a simple app (see figure 1) that returns random quotes.
You can also search for quotes. The application uses a web service to retrieve
the quotes. It’s the kind of application you probably write everyday:
make a request to a server, and display the results.
Using Windows Forms as the UI makes it very easy to implement a number of improvements
that would be difficult in a web application using Web forms as an interface.
As you can see, I’ve added tabs for the current session and previous session
history. The second tab displays the history of all the quotes you’ve
ever requested. This history, and in fact all information about this users client
sessions, are kept at the client. There is no need to keep any user state at
the server. This keeps your server code more cohesive. All in all, your application
gives the user a richer experience, and your server code is simpler. Add that
to the simpler deployment, and this is a technique you must have in your arsenal.
The simplest installer you’ll ever build.
Building the client application follows all the same techniques you’ve
already employed for a web service client. Add your web reference, and build
the code for the application. Once you’ve built the application, you need
to place the executable in some virtual directory on your web server. That’s
it. You’re done. Well, unless you expect any future updates. So, let’s
discuss how to prepare for that eventuality. I personally like to create a separate
server directory for the application, and for each version. I created that directory
under the web service implementation for this article to make the download a
little simpler. (See figure 2.) Once you’ve created the server directories
and added the executable, it will make your users happy to have a web page to
launch your application. See below:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Random Quote Client Launcher</title>
<meta name="GENERATOR"
content="Microsoft Visual Studio .NET 7.1">
<meta name="ProgId"
content="VisualStudio.HTML">
<meta name="Originator"
content="Microsoft Visual Studio .NET 7.1">
</head>
<body>
<P>This page contains the random quote client.</P>
<P>Click <a href="ClientApplication/1.0.0.0/RandomQuoteClient.exe">
here to run the sample.</P>
</body>
</html>
This page won’t win any design awards, but it gets the job done. The
<a> tag specifies a link that references the executable on the server.
When the user clicks it, as long as he has the .NET Framework installed, the
client application runs. (You can also install a shortcut on each users’
desktop to point to the final target:
http://<server>/ClientApplication/1.0.0.0/RandcomQuoteClient.exe) You
can place any future updates in other directories on the server. By updating
the link, you can change the version that your users get. If you want to go
a step farther, you can map a virtual directory to the current version and change
that mapping so that your users always get the current version.
Of trust and security
The Quote client runs because it is fully managed code, and the CLR can verify
that it is reasonably well behaved. By reasonably well behaved, that means it
does not have any buffer overruns, does not access raw memory, or use native
libraries or COM objects that might do any of those nasty actions. But that
doesn’t mean it’s completely safe. Managed code can create and delete
files, modify the registry, access network resources, and do many other useful,
but possibly dangerous, actions. So the CLR places numerous restrictions on
downloaded executables to protect your computer. You get a restricted version
of the file open dialog: you can’t save, create folders, or delete anything.
You can only use “safe printing”, not the full printing mechanisms.
You cannot use the Reflection.Emit classes. Your application is given restricted
network access: it can only access the site from which it was downloaded. Finally,
you can’t access the registry. Some of these restrictions are lifted when
the downloaded assembly comes from a trusted intranet site. Applications that
come from the same domain are not fully trusted, but have more trust than the
big wild internet. I prefer to aim for the least privileges and try to allow
an application to be run from anywhere on the web. This technique lets me raise
security restrictions for my local intranet, and provides yet more safety for
my users. Which brings me to Isolated Storage.
Isolated Storage is a mechanism whereby your assembly or application domain
gets assigned a special directory for data storage. This isolation is a two
way street: Your application can write in its own isolated storage location,
regardless of where it was accessed. Also, no other application can access that
directory and modify your files. Isolated storage is your application’s
private space on the disk to store date. But, it is not hidden or encrypted.
Do not use isolated storage as a means to store passwords or other sensitive
information, unless you encrypt it with other means as well.
I added a permanent history feature to the random quotes sample to give you
a flavor for using isolated storage. When the application exits, it writes all
the current quotes to a file called “QuoteHistory.txt” in its isolated
storage directory:
private void Form1_Closing(
object sender, System.ComponentModel.CancelEventArgs e)
{
// Save the current quotes to an isolated storage location:
IsolatedStorageFile iso =
IsolatedStorageFile.GetUserStoreForDomain( );
IsolatedStorageFileStream historyStream =
new IsolatedStorageFileStream
("QuoteHistory.txt", FileMode.Create, iso);
StreamWriter wr = new StreamWriter( historyStream );
foreach( string quote in this.textBoxHistory.Lines)
wr.WriteLine(quote);
foreach( string quote in this.textBoxCurrent.Lines)
wr.WriteLine(quote);
wr.Close();
}
This code should look very familiar, except for the first two statements. The
IsolatedStorageFile.GetUserStoreForDomain() method returns an IsolatedStorageFile
object that represents the Isolated Storage space for this application and this
user. In Windows XP, this will map to some location under C:\Documents and Settings\<username>\Local
Settings\Application Data\Isolated Storage. The IsolatedStorageStream constructor
creates a file in that isolated storage space. After that, it’s just like
any other file stream.
If you’re saving information to Isolated Storage, you should read that
information back in.
private void Form1_Load(object sender, System.EventArgs e)
{
// Save the current quotes to an isolated storage location:
IsolatedStorageFile isoStore =
IsolatedStorageFile.GetUserStoreForDomain( );
string[] files = isoStore.GetFileNames("QuoteHistory.txt");
if ( files.Length > 0 )
{
StreamReader reader = new StreamReader(
new IsolatedStorageFileStream(
"QuoteHistory.txt", FileMode.Open,isoStore));
string quote = reader.ReadLine();
while( quote != null )
{
textBoxHistory.Text += quote;
textBoxHistory.Text += System.Environment.NewLine;
quote = reader.ReadLine( );
}
reader.Close();
}
}
Once again, the code should look mostly familiar to you. We’ll retrieve
our own Isolated Storage folder and check for the existence of the history file.
If the file is found, its contents are read, and added to the history pane.
There are system limitations on the size of the Isolated Storage, that are
configured through the .NET Framework Configuration tool. The default size for
the internet zone is 10240 Megabytes. That would let you download a lot of quotes.
But, it should still prevent malicisous applications from flooding the users’s
disk. Applications downloaded from intranet or other trusted sites, have larger
allocations.
Look for opportunities
All in all, zero deployment is a step up from web forms. And, you can’t
argue with the simplicity of the deployment model. Look for those applications
where the download is small enough that your users are not waiting forever.
(Microsoft Word would be a bad candidate for zero deployment). Also, consider
how much access to protected system resources do you really need. A lot of the
applications you write on a day to day basis really can be satisfied with minimal
access to the local machine’s resources. When that’s the case, consider
a Windows Forms application that can be accessed from a web page. The CLR uses
CAS and verifiable code to ensure safety for your users, and you can give them
greater functionality and simpler access to updates.
Figures
Authors
 |
A commercial software developer and co-founder of SRT Solutions, Inc., Bill Wagner facilitates adoption of .NET in clients' product and enterprise development. His principal strengths include the core framework, the C# language, Smart Clients, and service oriented architecture and design. In 2003 Microsoft recognized his .NET expertise appointing him Regional Director for Michigan.
A frequent writer and speaker, Bill's work is published in ASP.NET Pro and Visual Studio Magazine. He is the author of C# Core Language Little Black Book and Effective C#.
|
|