How to add a start menu icon for your ClickOnce deployed application using MSBuild?#
If you have an automated build and deployment process using MSBuild, then you likely use the GenerateApplicationManifest and GenerateDeploymentManifest tasks to create the ClickOnce application and deployment manifest, respectively. If you are using these tasks, you may have wondered how you can set an icon (under the start menu) for your application. To have an icon set, you have to do several things:
 
1) Package the icon with the deployment. The easiest way to do this is to add an icon file to your main project--that ensure that it gets deployed with the application.
2) After you add the icon file, right click on the file in Visual Studio, and set the Build Action to Content.
3) At deployment time, set the IconFile attribute on GenerateApplicationManifest to the icon file name (e.g., myapp.ico), you added in step (1).
 
The above will add an iconFile attribute to the assembly description element, e.g., <description iconFile="myapp.ico" />, in the application manifest file (e.g., myapp.exe.manifest). When ClickOnce sees this attribute set, it will look in the ClickOnce deployment for the file and create a Start Menu Icon for your application.
 
An interesting thing to note is that the iconFile attribute is set in the application manifest and not in the deployment manifest. The reason for this is to allow you to change your application icons from one deployment to the next.
 
 
If you are deploying your ClickOnce application using Visual Studio and need to set the Start Mneu icon, do steps (1) and (2), and then go to the Project | Properties and choose the Application tab. Under the Resources group box, set the application icon to the icon you added in step (1). ClickOnce should take care of the rest. You can find out more on this at http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=170920&SiteID=1 .
10/11/2006 9:20:56 AM (Eastern Daylight Time, UTC-04:00) #    Comments [4]  |  Trackback

 

#
A few days ago I was doing a demo on ClickOnce and was asked a few questions that I felt others could benefit from. The first one was with respect to the folders/files generated by Visual Studio when you do deployments and the other one worth mentioning deals with desktop icons and Clickonce.
 
When I publish my application using VS 2005, it puts each deployment in a separate folder. In addition, it creates a [APPLICATION]_[VERSION].application file for each version. Moreover, there is a [APPLICATION].application file too. What is the reasoning behind all of this? Especially, the [APPLICATION]_[VERSION].application files?
 
When you publish an application using VS 2005, VS creates a deployment folder and names it based on your application name and version. The deployment folder contains everything about a particular version. VS also creates a deployment manifest based on the application and version (e.g., MyApp_1_0_0_0.application). Moreover, the firsttime you publish your application, VS creates deployment manifest (e.g., MyApp.application) that is independent of any version. The idea behind these files is to achieve server side rollback. The scenario is for you to do a deployment and send your users the link to the MyApp.application file. This deployment manifest refers to the latest version of the deployment. If you need to do a rollback on the server side for any reason, you can simply delete the MyApp.application and rename the latest version to MyApp.application.
 
Desktop Shortcut and ClickOnce
 
ClickOnce does not support the automatic creation of desktop shortcuts. Microsoft actually says that in their research they found that users don't like their desktop cluttered with icons and so they decided not to support desktop shortcuts with ClickOnce. If you have played around with ClickOnce to even a small degree then you know that ClickOnce also has an API that you can use to customize/supplement your ClickOnce deployment. If your users start asking for a desktop shortcut and you start to look for a way to achieve this, you'll likely end up using the APIs to figure out if the application is running for the firsttime (after a ClickOnce deployment) and if so, then execute some code that will determine the path to the application executable and then create a desktop shortcut. There are a few pitfalls that you have to consider before you jump into using desktop shortcuts with ClickOnce.
 
a) Desktop shortcuts requires use of some COM APIs and thus your application needs to have Full-Trust. See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/e6ac108b-15f6-4a54-891f-589e8b687ace.asp or http://www.knowdotnet.com/articles/createshortcutondesktop.html . If your application does not have full trust, you'll get a SecurityException.
 
b) If your application does not have Full-Trust, then you might be eager to tell your users to just drag the Program Files shortcut for your application onto their desktop and then use that when launching the application. I highly recommend against this and here's why. Lets say you deploy version 1 of your application and a user drags the shortcut from Program Files onto the desktop. You then publish version 2 of your application. When the user goes to launch the application using the shortcut he created, he'll get a message that the application has an update and he will update and everything will be fine. The next day when he comes in and executes the shortcut again, he'll get a message that the application has an update and he will have to update again. This will continue, until there is version 3. When version 3 comes out, the ClickOnce runtime will delete version 1 (ClickOnce maintaines only 2 versions of an application per user). When the user executes the shortcut again, he'll get a message that the shortcut points to a file that doesn't exist. The bottom line is that when the user drags the shortcut to the desktop on version 1, you have no way of updating the target of the shortcut when an update happens--ClickOnce updates the shortcut when you do an update so everything works with the Program Files shortcut.
 
c) Another problem with desktop shortcuts is that you have no way of getting rid of them when your application is uninstalled.
6/20/2006 4:28:45 PM (Eastern Daylight Time, UTC-04:00) #    Comments [3]  |  Trackback

 

Forcing Application Updates#

Forcing ClickOnce Updates

One of the big selling points of ClickOnce is automatic updates. One of the common questions I get with regard to updates is "How can I force an update on the user?"

There are three things to know with respect to forcing updates on users:

1) If your application is an online application, your users will always run the latest version; online applications get downloaded everytime the application is accessed. Thus, with online applications, you get forced-updates by default.

2) If your application is an installed application, you can force updates by using the MinimumRequiredVersion attribute. If you publish your application using Visual Studio, you can set this property from the Updates Dialog.

3) The last thing to note is that if your application is an installed application (and you have not set the MinimumRequiredVersion attribute) ClickOnce will prompt the user with an "Update Available" dialog ONLY if the user launches the application from the Start Menu shortcut. That is, if an application is an installed application and the user launches the application from a URL, ClickOnce forces the update.

 

3/21/2006 3:40:48 PM (Eastern Daylight Time, UTC-04:00) #    Comments [3]  |  Trackback

 

URL Parameter Passing with a ClickOnce Application#

ClickOnce applications can have arguments passed to it via URL paramters (similar to how we pass paramters to web pages). An example URL looks like:

http://mycompany.com/myapp.application?username=user&pass=pwd

If you are thinking of using this feature, there are a couple of things to keep in mind.

1) URL Paramters are passed to your application only if your application is launched from a URL. This means that if you have an installed application and the user launches the application from the Start Menu shortcut, your application will not get URL paramters.

2) This feature works well for online applications because online applications are always launched using the URL (i.e., online applications are not installed applications and thus do not get Start Menu shortcuts).

3) URL Parameter passing is not enabled by default. In Visual Studio 2005, you can enable it by checking the "Allow URL parameters to be passed to this application" checkbox. You can get to this by clicking on the "Options..." button from the Publish tab. If you are using the MAGE tool, you can do this when you create a deployment manifest by editing the "Deployment Options" UI. If you are using MSBuild to generate your ClickOnce application, you need to set the TrustUrlParameters
property on the GenerateDeploymentManifest task.

4) Here is an example of a class that lets you get to the URL parameters collection of your ClickOnce application. Note that the application has to be deployed via ClickOnce for the class to return the URL parameters.

using System;
using System.Collections.Generic;
using System.Windows.Forms;

public sealed class DeploymentHelper
{
    private static Dictionary<string, string> urlParms;

    static DeploymentHelper()
    {
        try
        {
            Uri appUri = System.Deployment.Application.ApplicationDeployment.CurrentDeployment.ActivationUri;
            if (appUri != null)
            {
                URLParameters = GetQueryStringParametersCollection();
            }
        }
        catch (Exception) { }
    }

    public static Dictionary<string, string> URLParameters
    {
        get
        {
            return urlParms;
        }
        private set
        {
            urlParms = value;
        }
    }
   
    private static Dictionary<string, string> GetQueryStringParametersCollection()
    {
        Dictionary<string, string> nameValueTable = new Dictionary<string, string>();

        if (System.Deployment.Application.ApplicationDeployment.IsNetworkDeployed)
        {
            string url = System.Deployment.Application.ApplicationDeployment.CurrentDeployment.ActivationUri.AbsoluteUri;
            string queryString = (new Uri(url)).Query;
            string[] nameValuePairs = queryString.Split('&');
            bool firstVar = true;
            foreach (string pair in nameValuePairs)
            {
                string[] vars = pair.Split('=');
                if (firstVar)
                {
                    firstVar = false;
                    if (vars[0].Contains("?"))
                    {
                        vars = new string[] { vars[0].Trim('?'), vars[1] };
                    }
                }
                if (!nameValueTable.ContainsKey(vars[0]))
                {
                    nameValueTable.Add(vars[0], vars[1]);
                }
            }
        }

        return (nameValueTable);
    }
}

You can get to the URL parameters by using the static property URLParameters:

Dictionary<string,string> urlParms = DeploymentHelper.URLParameters;

Note that the core of the class is the static GetQueryStringParametersCollection() method. I copied most of this method from MSDN, however, the MSDN version has a bug in it so and I had to modify it.


 

3/21/2006 10:32:41 AM (Eastern Daylight Time, UTC-04:00) #    Comments [3]  |  Trackback

 

Setting up CruiseControl.NET with MSBuild#

Just setup CruiseControl.NET (CC.NET) on a fresh install of Windows 2003 server. It went pretty seamless.

For now, we are only doing continuous integration, but very soon we are going to set it up for NUnit and FxCop too. If you plan on using it, here are the steps I followed. Note that we are using Subversion for our source control and that the box had nothing on it.

1. Download and install Subversion (http://subversion.tigris.org/project_packages.html).
2. Create a working directory and get latest from trunk.
3. Download CC.NET (http://confluence.public.thoughtworks.org/display/CCNET/Welcome+to+CruiseControl.NET).
4. Download CC.NET Tray (http://confluence.public.thoughtworks.org/display/CCNET/CCTray).
5. Install CC.NET.
6. Modify CC.NET Server configuration file to monitor your source control working directory and to call MSBuild.
7. Modify CC.NET web dashboard configuration file to show MSBuild output.

CC.NET can provide build updates via email notification or to a utility that you can install on your desktop (CCTray). We decided to go this route rather than have CC.NET email us. The cool thing about the tray utility is that it sits in your system tray and the icon will change when a build starts and the result of the build.

If you have never used CC.NET or the Java version, note that CC.NET has a server application that can be run as an NT Service (named CruiseControl.NET Server). You can configure the service to start automatically via the service control MMC. CC.NET also has a web dashboard that is used to view build outputs. The dashboard application gets installed under Program Files\CruiseControl.NET\webdashboard\.  If you use configure CC.NET to call MSBuild, then you'll want to also add an entry to the dashboard.config file to include a link to the MSBuild output.

Most of the setup of CC.NET was easy, the only problem I had was that I working off of an old configuration file and that one was not configured for MSBuild. Here is the server configuration file (ccnet.config), followed by the dashboard config file.

<!-- ************************************************* -->
<cruisecontrol>
 <project name="YourAppName">
  <!-- after a change is detected, wait 10 mins and then kick off the build-->
  <schedule type="schedule" sleepSeconds="600" />
  <!--set the sourcecontrol type to subversion and point to the subversion exe-->
  <sourcecontrol type="svn">
   <executable>C:\Program Files\Subversion\bin\svn.exe</executable>
   <workingDirectory>D:\CruiseControl\yourapp\build\checkout</workingDirectory>
   <trunkUrl>svn://servername/Projects/yourapp/trunk/Source</trunkUrl>
   <autoGetSource>true</autoGetSource>
   <username>sayed</username>
   <password>password</password>
  </sourcecontrol>
  <tasks>
   <!-- Configure MSBuild to compile the updated files -->
   <msbuild>
    <executable>C:\WINNT\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe</executable>
    <workingDirectory>D:\CruiseControl\YOURAPP\build\checkout\Solutions\YourSolution</workingDirectory>
    <projectFile>YourSolutionFileName.sln</projectFile>
    <buildArgs>/noconsolelogger /p:Configuration=Debug</buildArgs>
    <targets></targets>
    <timeout>15</timeout>
    <logger>ThoughtWorks.CruiseControl.MsBuild.XmlLogger,ThoughtWorks.CruiseControl.MsBuild.dll</logger>
   </msbuild>
  </tasks>
  <!--Publishers will be done after the build has completed-->
  <publishers>
   <xmllogger>
    <logDir>d:\cruisecontrol\yourapp\logs</logDir>
   </xmllogger>
   <email from="alerts@sayedhashimi.com" mailhost="yoursmtp.yourcompany.com" includeDetails="TRUE">
    <projectUrl>http://yourservername/ccnet</projectUrl>
    <users>
     <user name="Sayed" group="developers" address="sayed@sayedhashimi.com"/>
    </users>
    <groups>
     <group name="developers" notification="always">
    </groups>
   </email>
  </publishers>
  <modificationDelaySeconds>10</modificationDelaySeconds>
 </project>

</cruisecontrol>
<!-- ************************************************* -->

Here is the dashboard.config file I am using:

<!-- ************************************************* -->
<?xml version="1.0" encoding="utf-8" ?>
<dashboard>
 <remoteServices>
  <servers>
   <!-- Update this list to include all the servers you want to connect to. NB - each server name must be unique -->
   <server name="local" url="tcp://localhost:21234/CruiseManager.rem" />
  </servers>
 </remoteServices>
 <plugins>
  <farmPlugins>
   <farmReportFarmPlugin />
   <cctrayDownloadPlugin />
  </farmPlugins>
  <serverPlugins>
   <serverReportServerPlugin />
   <serverLogServerPlugin />
   <serverInformationServerPlugin />
  </serverPlugins>
  <projectPlugins>
   <projectReportProjectPlugin />
   <latestBuildReportProjectPlugin />
   <viewAllBuildsProjectPlugin />
  </projectPlugins>
  <buildPlugins>
   <buildReportBuildPlugin>
    <xslFileNames>
     <xslFile>xsl\header.xsl</xslFile>
     <xslFile>xsl\modifications.xsl</xslFile>
     <xslFile>xsl\compile.xsl</xslFile>
     <xslFile>xsl\unittests.xsl</xslFile>
     <xslFile>xsl\MsTestSummary.xsl</xslFile>
     <xslFile>xsl\fxcop-summary.xsl</xslFile>
     <xslFile>xsl\NCoverSummary.xsl</xslFile>
     <xslFile>xsl\SimianSummary.xsl</xslFile>
    </xslFileNames>
   </buildReportBuildPlugin>
   <buildLogBuildPlugin />
   <xslReportBuildPlugin description="MSBuild Output" actionName="MSBuildOutputBuildPlugin" xslFileName="xsl\msbuild.xsl" />
   <xslReportBuildPlugin description="NUnit Details" actionName="NUnitDetailsBuildReport" xslFileName="xsl\tests.xsl" />
   <xslReportBuildPlugin description="NUnit Timings" actionName="NUnitTimingsBuildReport" xslFileName="xsl\timing.xsl" />
   <xslReportBuildPlugin description="FxCop Report" actionName="FxCopBuildReport" xslFileName="xsl\FxCopReport.xsl" />
   <!--
   <xslReportBuildPlugin description="NAnt Output" actionName="NAntOutputBuildReport" xslFileName="xsl\Nant.xsl" />
   <xslReportBuildPlugin description="NAnt Timings" actionName="NAntTimingsBuildReport" xslFileName="xsl\NantTiming.xsl" />
   <xslReportBuildPlugin description="NCover Report" actionName="NCoverBuildReport" xslFileName="xsl\NCover.xsl" />
   <xslReportBuildPlugin description="Simian Report" actionName="SimianBuildReport" xslFileName="xsl\SimianReport.xsl"/>
   -->
   <!-- This is an example of using Project-specific build plugins
   <xslReportBuildPlugin description="My Report" actionName="MyReport" xslFileName="xsl\MyReport.xsl">
    <includedProjects>
     <projectName>My Project</projectName>
    </includedProjects>
   </xslReportBuildPlugin>
   <xslReportBuildPlugin description="My Other Report" actionName="MyOtherReport" xslFileName="xsl\MyOtherReport.xsl">
    <excludedProjects>
     <projectName>My Project</projectName>
    </excludedProjects>
   </xslReportBuildPlugin>
   -->
  </buildPlugins>
 </plugins>
</dashboard>
<!-- ************************************************* -->

2/8/2006 11:46:22 AM (Eastern Standard Time, UTC-05:00) #    Comments [3]  |  Trackback

 

ClickOnce Updates of Data Files#

In my previous post, I talked about how ClickOnce udpates applications but forgot to mention something specific to data files. ClickOnce deployed applications might use files that need to be migrated from one version to another. For example, an access database. ClickOnce supports this but with respect to downloading updates, data files are always downloaded (and need to be synchronized with the version on the client). ClickOnce doesn't even look at the hash when it sees that a file is marked as a data file.

2/8/2006 8:33:01 AM (Eastern Standard Time, UTC-05:00) #    Comments [3]  |  Trackback

 

ClickOnce Application Management#

I was recently asked a question concerning how the publisher of a ClickOnce application can force removal of an assembly after an update. Here is the question:

 

Questions: If my application no longer needs an assembly, does ClickOnce ensure that the file is deleted when I do an update?

 

Answer: ClickOnce installs applications within the %userprofile% folder. At any time, ClickOnce will have up to two versions of an application for a given user. When ClickOnce has to do an update, it's smart enough to only download files that have changed, and not copy files that have been deleted. ClickOnce does updates using the files listed in the application manifest file, on the server, and the files that are in the existing application folder. So image for a second that we have an application with three files: an exe (myapp.exe), a supporting assembly (mysupport.dll) and another supporting assembly named "anothersupport.dll". Lets now assume that we have deployed version 1.0 to our clients.

 

After deploying version 1.0, we decide that mysupport.dll is no longer needed because there is a major security flaw in it. In fact, we decide to purchase the same functionality from a 3rd party that doesn't have any security issues. What happens when we do an update? Does the assembly with the security flaw stay on the user's machine?

 

The answer is yes, but only until you do another update. We said earlier that ClickOnce maintain up to two version of an application for a given user. When you update your application, ClickOnce looks at the files you have listed in the application manifest and the files in the existing application folder on the client, to decide what to download and what not to download. ClickOnce does this to ensure that it doesn't download a file that was not modified. When ClickOnce sees that a file entry exists in the application manifest and does not exist in the existing application directory on the client, it knows that the file was added to the application and so it downloads the file. If it sees that a file is in the application manifest and is also in the existing application directory and the files have the same hash, then it just copies the file from the existing application directory to the new application directory. If a file exists in the application directory that is not in the application manifest, then ClickOnce leaves that file in the existing application directory. The reason it leaves the file is for rollback purposes. ClickOnce, supports "rollback to previous version" so it keeps the existing application just in case the user decides to go back to the old application. Can you get rid of the file manually? Technically you could, but the ClickOnce app store is not meant to be tampered with--ClickOnce manages the app store.

2/7/2006 8:17:41 AM (Eastern Standard Time, UTC-05:00) #    Comments [3]  |  Trackback

 

Working towards an automated ClickOnce Deployment#

If you have an automated build process around a ClickOnce deployment, you should know how ClickOnce determines updates to files. Why is this important? Well, lets say that you are using MSBuild to do scheduled builds every day (say 3 times a day). Your build process starts out by getting the lastest code from your source control system and then builds all the projects and assemblies of your application. It then creates the application and deployment manifest (the ClickOnce manifest files) and then pushes updates to the deployment server.

The potential problem with this approach is that if your build process rebuilds all of the assemblies used by your application, even if they were not modified, then ClickOnce will think the file has changed and will have to download the entire application again--just as if it were the initial install. This is an obvious problem and it has to do with how ClickOnce calculates a change to a file.

ClickOnce is driven by two manifest files: the application manifest and the deployment manifest. The application manifest has details about a particular version of an application. For example, the dependent assemblies of a particular version of an application. The deployment manifest has details about the application as a whole and not specific details about a particular version. The deployment manifest, for example, will have the update policy of the application.

Going back to our problem. I said that the application manifest has details about the files that comprise a specific version of an application. For example, if an application depends on an assembly called "MyCoolAssembly", then the application manifest will have an <dependency> tag for that assembly. Each file in the manifest has an associated hash that ClickOnce uses to determine if the file has changed. Actually, the hash is used for two things: 1) to determine changes to a file and, 2) to detect file tampering.

If your build process recreates assemblies, even if they were not modified, then ClickOnce will not know that the two files are identical. ClickOnce determines changes to files by comparing the file hash on client with what's on the server (in the application manifest). If the two files have the same hash, then ClickOnce doesn't download the file again, it simply copies the file from one directory to another.

There are several things to think about when you start to build an automated ClickOnce deployment.

1. Identify any 3rd party assemblies and be sure not to rebuild these files; put these files in a well-known folder and have the build process copy them.
2. If you have assemblies that you share across projects/solutions, establish a mechanism in your build process that will detect changes to files in these projects and will rebuild only when necessary.
3. The core of your application can be rebuild depending on how large the project is.

Generally, items 1) and 2) will make up a significant part of an application and needs to be addressed properly from the context of a ClickOnce deployment.

2/6/2006 8:58:46 AM (Eastern Standard Time, UTC-05:00) #    Comments [3]  |  Trackback

 

All content © 2008, Sayed Y. Hashimi
On this page
This site
Calendar
<December 2008>
SunMonTueWedThuFriSat
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910
Archives
Sitemap
Blogroll OPML
Disclaimer

Powered by: newtelligence dasBlog 1.8.5223.2

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Send mail to the author(s) E-mail

Theme design by Jelle Druyts