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.

4 comments:

mftolgb said...

Hi,

How to debug application whenever user is going to download the application manifest file in browser along with the query string information such as user name and password.

and where to catch all these query string information i mean in which file.

Thanks

RM said...

Although I haven't tried it myself in a ClickOnce application, adding this line to the code might help:
System.Diagnostics.Debugger.Break();

I often use this technique to debug Windows Services.

Anonymous said...

Hi there! I simply would like to give you a huge thumbs up for your great info you have
got here on this post. I will be returning to your site for more soon.

Jeff Staddon said...

Note: Opening up the app in Chrome will not work properly because Chrome downloads the executable then you run it--that second step drops off the parameters. However, this method should still work if you write a custom "launcher" application that directly calls the URL and runs it.