This page describes my implementation of Check For Updates, which allows
OurWord to update itself. This has proven necessary in order to keep all of the users of
a data repository up-to-date with the code that understands the files within the repository.
Without it, if I changed the format of a data file, older versions of OurWord would not
handle that data file properly. Now, when a user requests a Send/Receive, OurWord first checks
for updates and installs them if found; so that there are no longer any compatibility issues
among the computers in a project.
Check for Updates is a three-step process:
1. OurWord invokes SetupManager.CheckForUpdates in-process. This method
downloads the Manifest from the Internet and checks its version number to see if it is later than
the installed version. If it is, then it downloads all of the files that have changed into a
Download folder. It then invokes "..\Download\SetupOurWord.exe update", and shuts OurWord down.
2. SetupOurWord.exe launches, and invokes SetupManager.FinishUpdate. Now
that OurWord has shut down, its first order of business is to remove any stale files that are no
longer needed. It then installs files from the Download folder into the application folder, and into
other destination folders; unzipping as appropriate. Note that SetupOurWord.exe is itself copied into
the application folder, as it will be needed for the next CheckForUpdates command.
When it is finished, is invokes OurWord and then shuts itself down.
3. OurWord launches and the user is back in control.
Initial installation is a similar process, and uses much of the same code:
1. The user navigates to the
Download Page
and downloads SetupOurWord.exe to his computer.
2. He double-clicks on SetupOurWord.exe, and answers the Windows User Access Control
prompt. Since SetupOurWord is launched without the -update command argument, it clears out anything in the
install folder. It then downloads all of the files from the Internet. Finally it invokes
"..\Download\SetupOurWord.exe update", and shuts itself down.
3. The process continues in the same manner as the Check For Updates
defined above, now at Step #2.
A. Installation is done under the user's Application Data folder.
In Windows 7 this is "C:\Users\JoeSmith\AppData\Local\OurWord." The download folder is a
subfolder, "C:\Users\JoeSmith\AppData\Local\OurWord\Download." It is necessary to install here,
rather than under Program Files, because people who log into Windows with a Limited User account
only have access to this area. Thus any software that sports a CheckForUpdates feature will
avoid the Program Files destination.
B. The Manifest is an xml file that contains a list of the files to download.
OurWordManifest.xml is stored in the application's install folder. The CheckForUpdates process downloads
the Internet version of OurWordManifest.xml to the Download folder, and then compares the contents of the
two files to determine what to download and install. When the update is complete, the downloaded manifest
is then moved into the application folder and becomes the current manifest.
The length attribute gives the file's length, which is used to see if
the installed file is different from the one on the Internet; and in the progress indicator to
indicate the progress of download operation.
The hash attribute is a hex form of the
SHA-1 160-bit
hash. It is claimed by Visual Studio's documentation that "small changes to the data
result in large, unpredictable changes in the hash." We use the hash, together with the
length attribute, to determine whether or not to download a file; and then as a checksum afterwards to determine if
the download was successful.
The filename attribute is, of course, the name of the file
to be downloaded.
C. OurWord's post-build process includes steps to build the manifest:
1. All built files are copied into a special Deploy folder, with
commands along the lines of
copy "$(TargetDir)OurWord.exe" "$(SolutionDir)Deploy"
2. OurWordSetup.exe is called with a special flag that causes it to
build a manifest of all of the files in the Deploy folder, and then place the manifest there. The
command is
"$(TargetDir)OurWordSetup.exe" -g "$(SolutionDir)Deploy".
D. Deployment is then simply a matter of uploading everything in the Deploy folder
to the appropriate place on the Internet; which is a folder under OurWordSoftware.org.
E. No provision for Patches-To-Stable versus Development releases has been
made at this time. One possible approach would be to use a normal setup program for development releases, with
CheckForUpdates disabled.
F. No provision has been made for modifying the Registry. Use of the Registry
is somewhat contrary to the philosophy both of CheckForUpdates installation, and for cross-platform;
and OurWord is moving away from its use.
G. Zip files are supported. The use of zip files serves both to reduce
the number of bytes a user must download from the Internet, but also to organize install files
as part of a project.The filename indicates the destination as follows:
The first 'word' of the filename must be one of the recognized possibilities:
- app - the root will be the application folder
- mydocs - the root will be the user's "My Documents" folder
- langdata - the root will be the Language Data folder
(that is, C:\Users\JoeSmith\AppData\Local\Language Data)
Subsequent 'words' up until the final 'word' indicate optional subfolders under this root.
For example,
"app.loc.Localizations.zip" has all of its files unzipped in the "{app}\loc" folder.
"app.SupportingDlls.zip" has all of its files unzipped in the "{app}" folder. (This is
where I put things like Chorus, Palaso, LinqBridge, etc. that do not change when I build my solution.)
"langdata.OurWordSample.Sample.zip" has its files unzipped in the
"C:\Users\JoeSmith\AppData\Local\Language Data\OurWordSample" folder.
H. The ability to unzip files is handled by the
ICSharpCode.SharpZipLib library.
As a post-build step, I use Microsoft's ILMerge program to combine the SetupManager code with this library
into a single executable, SetupOurWord.exe. This ensures that the installer has the ability to unzip files,
independently of whatever is going on in the file download/copy process. The SharpZipLib weighs in at 188Kb, so
the cost of this is that these bytes are downloaded during the update process if the SetupManager code has changed.
(However I anticipate that SetupManager will rarely change.)
I. There is no good way to create a desktop shortcut. After some research via Google
I finally went with including the IWshRuntimeLibrary and creating a shortcut at runtime if one doesn't
already exist. The process is described
here.
No question this will not work on the Linux port. I call the method to create the shortcut as part of
OurWord's Main method, which means that if one doesn't exist, it will be created. For MTTs this behavior
should be fine; but power users who hate desktop icons will be unhappy that the OurWord shortcut keeps
reappearing.
J. Mercurial is installed as just another zip. This is actually independent of
this installer and I only make this note as an FYI. I include a file called app.mercurial.zip, which places
the Mercurial tree as a folder underneath the OurWord install folder. At runtime I then set temporary
environment variables to tell the system where to find mercurial's hg.exe file. Because Mercurial is now
included in OurWord's installation, I can be assured that I know what version of Mercurial all users are
using; which should help if an issue with Mercurial ever shows up.
K. It is possible to install without an Internet connection. One of the things
that the SetupManager does in response to the -g flag, during OurWord's post-build process,
is to create OurWordSetupFiles.zip. When SetupOurWord is run with no flags, if it finds OurWordSetupFiles.zip
in the same folder, it installs the files within it rather than doing a download. So a person could put these two
files on a memory stick and accomplish installations on a group of computers without going to the Internet.
(Subsequent CheckForUpdates actions still require the Internet, however.)
My implementation has the following shortcomings which I hope to address soon:
1. No means of creating items in the Start Menu. For OurWord we've not really needed
a Start Menu item, so I've not worried about it. The solution is probably similar (and equally distasteful)
to the desktop shortcut described above.
2. Names and destinations are hard-coded as consts. Because SetupOurWord.exe must
run as a standalone, I could not call it as a library and pass values in. So things like the Internet
location are hard-coded. If you wish to use the code, and can think of a way to generalize this, let's talk!
3. Unit tests do not handle the some issues, in particular, those operations
which involve downloading from the Internet.
Since OurWord is open-source you are certainly invited to use the code; but I'd sure appreciate
it if you'd help me make it better.