This will be enough to satisfy me for now. On to the fun stuff.
Auto-Deployment #1
For basic auto-deploy functionality, we first need to remove the pesky
generic form that has been added to the deployment project and replace it with
an empty class. You may find use for the form later, but for now we�re
doing basics only. So we now have a class, AutoUpdaterMain, to start
working with.
Obviously we�re going to need a standard static void Main() method here or
the compiler is going to get very angry at us. So we�ll remove the class�s
constructor (not needed) and replace it with the following:
[STAThread]
static void Main()
{
}
There. We now have an app that does nothing. Not a lot of
fun. Let�s make it show our UI. Add a reference to your UI project
in your deployment project. Open your main class, make sure you have a
using System.Windows.Forms reference, and add the following code to your main
function (or something similar based on your project and form name):
AutoUpdaterTemplate.UI.Win.MainForm form = new AutoUpdaterTemplate.UI.Win.MainForm();
Application.Run(form);
Congratulations. You now have a running app. Try it out.
Not only that, but you�ve now written all the code you need for auto-updating
scenario number 1. Don�t believe me? I�ll show you. But first,
one minor change. Open the AssemblyInfo.cs file located in your UI
project. Change this line:
[assembly: AssemblyVersion("1.0.*")]
to this:
[assembly: AssemblyVersion("1.0.0.0")]
You�ll thank me later.
Now create a directory somewhere on your computer and copy the deployment exe
and the UI dll from their respective debug folders into it. Next, create a
virtual directory, called AutoUpdater, from within IIS and aim it at this new
directory. Finally, type this into your web browser:
http://localhost/AutoUpdater/AutoUpdaterTemplate.Deployment.exe
Viola, your app runs. But is it auto-updating? Sure it is.
Here�s the test. Open your Visual Studio command prompt and type gacutil
/ldl. This will list all of the files in your GAC�s temporary download
cache. See your UI dll? See the version? It should be
1.0.0.0 Ok, now change something about your UI�s form. I�ll set my
label to this:
Now
change that AssemblyVersion to look like this:
[assembly: AssemblyVersion("1.0.0.1")]
recompile, copy the new dll into your IIS
directory, and run it in your browser again. You�ll see that whatever you
changed showed up. Not only that, but try the gacutil /ldl command
again. Now two versions of the UI dll are listed. That�s right, the
CLR determined that the version in your cache was old and downloaded the new
one. True .Net auto-deployment.
Auto-Deployment #2
So far, we would have to require that a user
always follow a link to our app. While this is fine for many applications,
we�d like to make it as simple as running any other app on our computer.
No browser required. So we�ll press on.
Ideally we�d like this to be as lightweight
as possible, the best-case is having just a stub exe on the machine, and
possibly it�s related app.config file. However, this means the UI dll
won�t be local on the user�s machine. Goodbye reference. We�ll
remove that from our deployment project now. However, now that we don�t
have a reference to the dll, the code in our main method is broken. We
can�t create an instance of our main UI form in the same way. What�s the
answer? Reflection. As I shall show you now.
This article is not intended to teach you
reflection. It�s much too broad a topic and far outside my current
scope. As related to the current problem, it will be enough for you to
know that reflection is going to allow us to create objects from within
assemblies that we don�t have direct references to.
The first issue we need to tackle is that of
how our exe knows where to look for our UI dll. Normally the CLR would use
the CodeBase property of our exe. This is a reference to the place that
the exe is located. However, this is no good in our case. We need a
way to tell the CLR where to look. Or, better yet, we�ll get the dll
ourselves. This will involve using the Assembly class, which will force us
to add using System.Reflection to our list of using statements. We�ll now
start to rebuild our main method in this new manner. We find we can load
the assembly ourselves using the Assembly class�s wonderful LoadFrom
method. Here�s what we get:
[STAThread]
static void Main()
{
try
{
string codeBase = "http:";
Assembly uiAssembly = Assembly.LoadFrom(codeBase +
"AutoUpdaterTemplate.UI.Win.dll");
}
catch(Exception err)
{
MessageBox.Show(err.Message);
}
}
I�ve chosen to abstract out the codeBase for
reasons you�ll see in a minute. Now that we have an instance of our UI dll
we�ll need a little reflection to pull out our form. This line should do
the trick:
Type type = uiAssembly.GetType("AutoUpdaterTemplate.UI.Win.MainForm", true, false);
Finally, we�ll plug that form back into our
application with this:
Application.Run((Form)Activator.CreateInstance(type));
And that�s it. You now have a fully
functioning stub exe. The problem? Remember the codeBase
variable? Yep, terrible idea to hard code that. We�re going to look
to the System.Configuration namespace to fix this. Go ahead and add that
using reference now.
Making It Flexible
System.Configuration is going to allow us to
pull dynamic settings from an App.config file. And so, we�re going to need
one of those. Let�s add one to our deployment project. In our new
App.config file we�re going to need an appSettings element as well as a codeBase
element within that. When all is said and done, our App.config file should
look like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key=�codeBase� value=�http:
</appSettings>
</configuration>
Now we can change that variable
initialization line in our main method and we wind up with this:
[STAThread]
static void Main()
{
try
{
string codeBase =
ConfigurationSettings.AppSettings["codeBase"].ToString();
Assembly uiAssembly = Assembly.LoadFrom(codeBase +
"AutoUpdaterTemplate.UI.Win.dll");
Type type = uiAssembly.GetType("AutoUpdaterTemplate.UI.Win.MainForm",
true, false);
Application.Run((Form)Activator.CreateInstance(type));
}
catch(Exception err)
{
MessageBox.Show(err.Message);
}
}
This is a much better setup because we can
now change the location of our UI dll without having to recompile, we merely
change the config file.
You�ll now be happy to see that if you copy
the deployment exe and it�s related config file to any location on your machine
the app runs as expected.
BUT�is it auto-updating? Well, do the
test yourself. Change your UI. Update the AssemblyVersion.
Copy it to your IIS directory. Now re-run the app from wherever you copied
the exe on your machine. Did it work? Of course it did. Would
I steer you wrong?
Good To Know
FYI: The AssemblyVersion update?
Yep, the CLR uses the version to determine whether or not to pull the new
assembly. You don�t need to update the version every time, but the new
functionality won�t be downloaded unless you do, or unless you run the gacutil
/cdl command. This will clear your GAC�s temporary download cache, forcing
the CLR to download whatever version is available.
Summing Up
That�s it. We�ve now created an
auto-updated app and managed to deploy it in two different scenarios. It�s
just that easy. But is it? Sadly no. All of this has worked,
and will work fine for very simplistic apps, but we must remember that
auto-deployed apps in the previous cases are pulling resource from across the
internet, and thus are subject to the mightly lock down Microsoft has placed on
the InternetZone. To accomplish more advanced features with our apps we�ll
need to start looking into such topics as Code Access Security and Strong
Naming. Alas, those are topics for a more non-introductory article.
And if any of you out there are interest, just let me know, and I shall be happy
to provide.
Good Luck.
Travis Merkel
v-trmerk@microsoft.com