Monday, March 29, 2010

Passing Parameters To ClickOnce Applications

I was doing some research with ClickOnce deployment architecture and found an unexpected challenge: passing command line parameters. This post summarizes a couple of different approaches; hopefully, it will save someone a few hours of trial and error and frantic googling.

Use Query String

ClickOnce applications can be installed from a website, and they can also be invoked from a webpage. All that's needed is a hyperlink that references the .application file, e.g.: http://www.mywebsite.com/foobar/foobar.application. When user clicks the link, Foobar will launch (actually, the same link will install Foobar on user machine). In this scenario, parameters can be passed to the application by adding query string to the URL: http://www.mysite.com/foobar/foobar.application?param1=value1&param2=value2&... . Here's how to do it:

Step 1: Enable URL parameters in ClickOnce application

Open project properties in Visual Studio and click "Publish" tab. Then click "Options" button, which brings up a dialog window. Select "Manifests" from the list, and make sure the checkbox that reads "Allow URL parameters to be passed to application" is checked.



Step 2: Add code to process parameters

In regular Windows applications written in C#, it is enough to declare static void Main(string[] args) in order to get the list of command line parameters in the args array. Unfortunately, this doesn't work with ClickOnce applications - the args array will be empty whether or not URL had any parameters. In order to access them, we need to analyze AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData property.


[STAThread]

static void Main()

{

    Application.EnableVisualStyles();

    Application.SetCompatibleTextRenderingDefault(false);

 

    var args = AppDomain.CurrentDomain.SetupInformation.ActivationArguments;

    var frm = new MainForm();

    if (args != null

    && args.ActivationData != null

    && args.ActivationData.Length > 0)

    {

        var url = new Uri(args.ActivationData[0], UriKind.Absolute);

        var parameters = HttpUtility.ParseQueryString(url.Query);

        // Process parameters here

    }

 

    Application.Run(frm);

}



Add File Type Association

Another interesting approach is to associate the application with a specific file extension. When a file with this extension is downloaded from a web page or opened in Windows Explorer, application will be launched automatically and file name will be passed to it. Prior SP1 release of .NET 3.5, file type association had to be created programmatically (by creating subkeys for the desired extension and a shell\open\command using Microsoft.Win32.Registry API). Visual Studio 2008 SP1 allows to define association declaratively. In the same Options dialog mentioned above, click "File Associations" and fill in required information in the data grid.



Processing logic is very similar to the first scenario - we still need to analyzeAppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationDataproperty. The only difference is that instead of calling HttpUtility.ParseQueryString, we need to extract file name from it.

Saturday, March 13, 2010

Continuous Integration + Continuous Improvement

Long time ago, in the early days of software development, one person could program entire application. Gary Kildall wrote the CP/M operating system, and Wayne Ratliff wrote dBASE, one of the first database engines. Nowadays, the project can get started with one or two developers, but eventually ends up with many more (as management often seems to ignore Brooks's law about adding man-power to a late project).

Once you have several people working on the same codebase, integrating their changes can become a challenge. (I remember one project where two developers decided to work independently and did not attempt to integrate until the code-cutoff day. Sadly but unsurprisingly, the solution didn't build, and since there was no time to solve all the issues, they had to deliver two separate applications instead of one.) The solution, known as continuous integration, is to use the common source code repository and integrate frequently. The first part is obvious, the second may require some explanation.

Many software companies have Build engineers (or even teams of Build engineers), whose main job is to produce builds. Since "integrate" really means "get latest code and build the system", it is theoretically possible to assign this task to Build engineers. However, I do not think it is such a great idea: first of all, the task is boring, and second, humans may have a problem with the "frequently" part. For some projects, it will be enough to run a nightly build, while other will prefer to integrate every time source code is committed to the repository. There is no way a human could be doing that! The best thing to do is automate the task, and there are commercial and open-source systems that can do the job.

A few months ago I installed one such application, open-source product called Cruise Control .NET on a virtual server we use as a build machine. It consists of a Windows service and an ASP.NET web application: service runs integration tasks, web app provides user interface for build status, logs, and reports. Naturally, it supports multiple projects, but project configuration has to be done the old-fashioned way, by manually editing XML in a couple of .config files. Another nice feature is a utility called CCTray; this is a little app that displays an icon in the taskbar. The icon uses traffic light colors to notify user of their project status.

Cruise Control is a great application that I highly recommend, but there is one more concept I wanted to describe in this blog post: continuous improvement, or "Kaizen" in the original Japanese. Kaizen is a philosophy that helps to improve productivity and quality while reducing cost. It applies to different areas: manufacturing, government, banking, healthcare, and its main ideas are individual empowerment and continuous quality cycle.

I think we can successfully apply Kaizen ideas to software development. Most straightforward approach in my opinion is encouraging programmers to do three things:
  • Refactor code to design patterns,
  • Increase unit test code coverage,
  • Fix all bugs (not just those reported by users).
This of course means that our application's codebase will be continuously updated, and I know there are companies out there that will be really uncomfortable with such perspective. However, when source code is sufficiently covered by unit tests, and all tests are executed as part of every build, I see no reason for concern.