How to pass arguments to an offline ClickOnce application

In ClickOnce Deployment, it is a common belief that you can not pass arguments to an application unless:

  1. The application is deployed to a web server, and
  2. The application is online-only.

If you are interested in passing query parameters to an online-only application deployed to a web server, check out the article on MSDN. About offline applications, or applications deployed via a file share, that page says this:

 

However, this is no longer the case. I suspect it was changed when they added the ability to do file associations in .NET 3.5 SP-1. It turns out that you can now pass parameters to:

  • an offline ClickOnce application,
  • a ClickOnce application deployed to a file share, and even to
  • an offline ClickOnce application deployed to a file share.

And of course you can pass parameters to an online-only application using query parameters, but we already knew that (see article referenced above).

Here’s how you call the application and pass the arguments:

System.Diagnostics.Process.Start(shortcutPath, argsToPass);

This is how you read the argument string:

//Get the ActivationArguments from the SetupInformation property of the domain.
string[] activationData = 
  AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData;

Here are three different ways to pass and receive arguments:

  1. Pass a file path and name as a URI. This mimics what happens when you set up a file association and double-click on a file with that extension.  The argument will start with “file:”.
  2. Pass a query string with key-value pairs in it. This is the same way you pass and parse query strings for an online-only application. The argument will start with a question mark.
  3. Pass one value or a list of comma-delimited values.

Locate the shortcut for the application.

The first thing you need to do is locate the shortcut in the start menu for the application you want to run — this is made up of the Publisher Name and Product Name. Here is how to find the location of the application’s shortcut:

StringBuilder sb = new StringBuilder();
sb.Append(Environment.GetFolderPath(Environment.SpecialFolder.Programs));
sb.Append("\\");
//publisher name is Nightbird
sb.Append("Nightbird");  
sb.Append("\\");
//product name is TestRunningWithArgs
sb.Append("TestRunningWithArgs.appref-ms ");  
string shortcutPath = sb.ToString();

Call the application with an argument string:

System.Diagnostics.Process.Start(shortcutPath, argsToPass);

The argument string that you pass can not have spaces or double-quotes in it. If you pass [a b c], you will only get [a] on the receiving side. If you pass [“a”,”b”,”c”], you will get [a]. If you need to pass arguments with spaces, pass your arguments as a query string.

How to create the argument string to be passed, and how to parse it on the receiving side.

Case 1: Sending file path and name. This mimics what happens when you use the ClickOnce properties to set up a file association, and the user double-clicks on an associated file.

Build the argument string:

//If you have file associations, 
//and you double-click on an associated file,
//it is passed in to the application like this: 
//file:///c:/temp/my%20test%20doc.blah
//So format it as a URI and send it on its way.
string fileName = @"D:\MyPictures\thePicture.jpg";
Uri uriFile = new Uri(fileName);
string argsToPass = uriFile.ToString();

On the receiving side, retrieve the file path from the URI:

//This is what you get when you set up a file association 
//  and the user double-clicks on an associated file. 
Uri uri = new Uri(activationData[0]);
string fileNamePassedIn = uri.LocalPath.ToString();

Case 2: Sending a querystring

Define the argument string:

//querystring 
string argsToPass = "?state=California&city=San%20Francisco";

Parse the string on the other side:

//NameValueCollection is like a dictionary.
NameValueCollection nvc = 
  System.Web.HttpUtility.ParseQueryString(activationData[0]);
//Get all the keys in the collection, 
//  then pull the values for each of them.
//I know I'm only passing each key once, with one value, 
//  in the querystring.
string[] theKeys = nvc.AllKeys;
foreach (string theKey in theKeys)
{
  string[] theValue = nvc.GetValues(theKey);
  //key is theKey
  //value is theValue[0]
  System.Diagnostics.Debug.Print("Key = {0}, Value = {1}", 
    theKey, theValue[0]);
}

Case 3: Pass a list of comma-delimited values

Define the argument string:

//pass a comma-delimited list of values
//don't use any spaces
string argsToPass = "arg1,arg2,arg3,arg4";

Parse the string on the other side:

//I've only ever seen activationData have one entry,
//  but I'm checking for multiples just in case. 
//This takes each entry and splits it by comma 
//  and separates them into separate entries.            
char[] myComma = { ',' };
foreach (string arg in activationData)
{
  string[] myList = activationData[0].Split(myComma, 
    StringSplitOptions.RemoveEmptyEntries);
  foreach (string oneItem in myList)
    System.Diagnostics.Debug.Print("Item = {0}", oneItem);
}

If you only want to send one argument, just use activationData[0].

Summary and Code Samples

This showed three ways to pass arguments to an offline ClickOnce application, and how to parse them on the receiving side.

Code samples can be found here. They are available in both C# and VB. They were built with VS2008 and they target .NET 3.5 SP-1, which is the minimum version for which this will work.

The first solution (RunningWithArgs) receives the arguments, parses them, and displays them in a listbox. You need to deploy this one to a webserver or a file share and then install it. To determine which kind of argument it is receiving, it checks the first characters of the argument string.

The second solution (CallRunningWithArgs) calls the first one and passes arguments to it. It uses the conventions mentioned previously to determine the kind of arguments being passed. If the first application is installed, you can just run this one out of Visual Studio.

Tags:

56 Responses to “How to pass arguments to an offline ClickOnce application”

  1. Karl Shifflett Says:

    Nice article Robin. Nice code formatting too!

    Cheers,

    Karl

  2. Harald Mühlhoff Says:

    Thanks! Very useful for me! I’m passing the more complex arguments using a named pipe connection but since multiple instances of my app maybe running I have to supply the pipename to the ClickOnce application.

    Best regards,
    Harald

    PS: BTW –

    AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData

    can fail because ActivationArguments will be null if no arguments are supplied. Also you might want to mention that you have to check the “Allow URL parameters to be passed to application check box” in your application settings
    (see http://msdn.microsoft.com/en-us/library/ms172242.aspx for details)

    • robindotnet Says:

      It’s a good point that ActivationData can be null. People should definitely be checking it!

      You do NOT have to check that box to allow URL parameters to be passed… unless your application is online-only and you are using a URL to run it. That’s been around for a long time, but nobody knows you can pass arguments to an offline app or one shared via a network share.

      In my example, I pass arguments three ways — a string of query parameters, a single or comma-delimited list of values, or a file name (which is what happens when you hook up the file association information in the properties). The query parameters are just a way to pass a string of arguments, they aren’t attached to the URL. It just seemed a handy way to send a list of key-value pairs. You could also write a class and serialize it into an XML string and pass that, and deserialize it on the other side back into the class, and that would probably work. Basically, whatever you send has to have no spaces in it, and query strings seemed the easiest way to accomplish that.

      Thanks; hope this helps!
      Robin

  3. Windows Client Developer Roundup for 5/3/2010 - Pete Brown's 10rem.net Says:

    [...] How to pass arguments to an offline ClickOnce application (RobinDotNet) [...]

  4. Mike Says:

    I’m trying to send parameters to an online-only application deployed on a network share. You seem to say that it is possible since SP1 but I haven’t figured out how. Can you help me out?

    Thanks!
    Mike

    • robindotnet Says:

      You can’t send query parameters for an online-only application to a network share. That only works when the application is deployed on a webserver. If you make the application online/offline, though, you can send it query parameters. Can you make your app be online and offline?
      Robin

      • Mike Says:

        OK it confirms what I was thinking. I will continue to use a deployment from a web server.

        Thanks!
        Mike

  5. Mike DePouw Says:

    thanks!

  6. Christian Snijder Says:

    For the offline passing option. Does this mean I have to build a regular application (exe) which has an association to an extention and then passes the arguments to the clickonce application? This would mean I’d need a regular installer to install the ‘launcher’… Am I missing something?

    • robindotnet Says:

      If you are the guy whose thread I am following in the MSDN Forum, and you want to pass info to your app but still have it be online/offline, I’m pretty sure that if you have it as an online/offline, you can still run the program with the query parameters as if it were just an online application. It can also be run from the desktop without query parameters, so if that’s not okay, this won’t work for you. Just make it online/offline and then invoke it through the link to the deployment manifest (.application file) and pass in your parameter(s), and change the program to process it. The processing is the same code either way.

    • robindotnet Says:

      I was messing around with this and found that I can click Start/Run and put in the path to the shortcut and it will run the app. I’m running Windows 7, it looks like this:

      C:\Users\Robin\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\publishingcompany\productname.appref-ms

      So I right-clicked on the desktop and chose new shortcut, and put that link in. And it didn’t work. :-P I know there’s a way to get that to work, or something similar to it, but I can’t remember how. I’ll ping the C/O lead and see if he knows.

      Robin

  7. David Mullin Says:

    I follow how to parse the parameters when I get them – I’m having a hard time figuring out how to run things from the command line so that the app sees the parameters. Let’s say that I’m running the .application file from a network share:

    \\Server\Share\MyApp.application

    My expectation is that I can do something like one of the following:

    file:\\Server\Share\MyApp.application?param=value (fails to run)
    \\Server\Share\MyApp.application ?param=value

    Or some variation thereof. However, no matter what I try, ActivationData only contains a single element, which is the URL of the .application file – I don’t see the parameters anywhere. What am I missing?

  8. TB Says:

    I wonder when the marked bold part of code will ever be processed…


    If (activationData IsNot Nothing AndAlso activationData.Length > 0) Then

    'querystring starts with ?; file association starts with "file:"
    If (activationData.Length = 1 AndAlso activationData(0).Substring(0, 1) = "?") Then
    ProcessQueryString(activationData)
    ElseIf (activationData.Length = 1 AndAlso activationData(0).Length > 5 AndAlso activationData(0).Substring(0, 5).ToLower() = "file:") Then
    ProcessFileAssociation(activationData)
    Else
    ProcessCSVParameters(activationData)
    End If

    Else

    If (activationData Is Nothing) Then
    lstArgs.Items.Add("No arguments passed in.")
    Else
    lstArgs.Items.Add(String.Format("Number of args = {0}", activationData.Length))
    End If

    End If

    • robindotnet Says:

      It will be processed if activationData is not nothing and the activationData.Length is 0. Technically, it should never happen, but where ClickOnce is concerned, a little extra code never hurt anyone. If it DOES ever happen, at least I’ll know about it!
      Robin

  9. Igor Soloydenko Says:

    Why don’t you use the Path.Combine() instead of StringBuilder.Append()?
    I believe that the shown code is able to solve the problem, but it could be better. Don’t show such examples to newbies.

    • robindotnet Says:

      Sorry, I disagree with you. If you are going to concatenate a bunch of strings, you should use either string.format or (better) a Stringbuilder. It is more efficient.

      • Igor Soloydenko Says:

        Well, what you had said is called ‘premature optimization’. If someone will have an inefficient code then he or she has to profile it first and only after that make any changes (probably which you has written). But in all other cases your code is just additional semantic complication. Here you use StringBuilder to combine Pathes. Do you also use Pathes to combine strings in other places? ;)

      • Igor Soloydenko Says:

        And I’m really sorry for my Enlish. It isn’t my native language.

  10. Kit Says:

    Thanks for this article, very helpful. I’m attempting to pass parameters via querystring (similar to Case 2 above). From what I can tell, this will not work when using Chrome but does when using IE. Do you have any insight on this?

    I’m using the web-deployed online/offline method and passing the parameters like: http://servername/MyApp.application?key1=value1&key2=value2

    The app can see the parameters when invoked from IE but the parameters aren’t there when using Chrome.

    Thanks!

    Kit

  11. Didier Says:

    Hi,

    I have downloaded your test sample and when compiling, the followinf error occurs:
    Error 1 Cannot import the following key file: . The key file may be password protected. To correct this, try to import the certificate again or import the certificate manually into the current user’s personal certificate store. TestCallingRunningWithArgs_VB

    ??? Any help welcome :-)

  12. loqu8im Says:

    Hi Robin, do you know if it is possible to embed the said arguments into setup.exe? The goal on our end is to embed referrer information into the setup.exe. Keep up the great work!

  13. Saggy Says:

    Hi Robin,

    This is a nice article and very helpful. I have one question still. For calling and passing the arguments you’re asking to create a Visual Studio solution/app which will execute following line of code:

    System.Diagnostics.Process.Start(shortcutPath, argsToPass);

    Is there a way to call the ClickOnce app’s .application file (which is on file share) and pass it the arguments through a BATCH (.BAT) file or a Shortcut file (.LNK)?

    Thanks in advance.

    • robindotnet Says:

      You can definitely call the deployment manifest and pass the arguments. Just put the URL (or path) to the .application file in the Process.Start statement instead of the shortcut path.

  14. Processing Command Line Arguments in an Offline ClickOnce Application « Developing For .NET Says:

    [...] I hit the mother lode: an article by RobinDotNet explaining How to pass arguments to an offline ClickOnce application.  Robin’s blog is all about ClickOnce, and interestingly enough I found plenty of earlier [...]

  15. Avi Says:

    Hi Robin, followed your post but what I get is:
    key = “” and value = http://myserver/MyApp/MyApp.application in addition since the application is an online/offline once you try to run from the desktop it crashes on the reading parameters function
    what am I doing wrong?

    Avi

    • Avi Says:

      ok, the url I used was http://myserver/MyApp/publish.htm in which it doesn’t work you need to call the application url with the arguments (i.e. http://myserver/MyApp/MyApp.application?key=value)

      10x anyway

      • robindotnet Says:

        My example actually accesses the shortcut to the application and passes the arguments that way. As you have noted, you can also invoke the URL to the deployment manifest and pass the arguments that way.

        • Avi Says:

          what if I run the application from a local path (i.e. file://C:/MyApp.application) is it possible to pass arguments as well ?

          • Avi Says:

            well, it seems that if you take the deployment output of your project, copy it to the machine on which you want to install your Clickonce application and run it from there it will work like a charm.

          • robindotnet Says:

            You can pass arguments whenever you invoke the deployment manifest (.application file) from the original installation location. So in your case, if the deployment folder is on the C drive and all of the files are there, that will work. A better way is to access it through the start menu or the desktop shortcut. To do it through the desktop shortcut, try something like this: %userprofile%\Desktop\GoldMail.appref-ms arg1,arg2,arg3

  16. aVI Says:

    Hi Robin, I followed your post but the result I get parses only the url, I get:
    key = “”
    value = http://myserver/myap/myapp.application

    any idea/

    Thanks,
    Avi

  17. Avi Says:

    hi robin,

    is it possible tp pass arguments (like the querystring for the .application) to the bootsreapper setup.exe ?
    if so can it be passed to the clickonce application?
    what I need is to simulate the web based clickonce aplication call with a querstring (i.e. http://myserver/myap/myapp.application?key1=value1)

    10x

  18. John Says:

    I have a problem when trying to access my application (with arguments) via a link from e g Outlook or MS Word. Application is resident on a file share.

    For example:

    1. Add the following Address as a hyperlink to some text in e g Outlook \\My_NW_Share_Path\FileExtensionTest.application

    Application opens when link is clicked

    2. Same as above but with this address
    \\ My_NW_Share_Path\FileExtensionTest.application?arg=val

    I get a “Cannot find the file \\My_NW_Share_Path\FileExtensionTest.application?arg=val. Verify the path or Internet address is correct.” message.

    Any comments on what is going wrong here?

    • robindotnet Says:

      My guess is that Office security is blocking it because it has query parameters. You could try putting your deployment on a web server somewhere (or in Azure blob storage, I have a blog post about that, it’s dirt cheap) and try putting a URL in there and see if it makes any difference. If you invoke a URL, it might count as “external”, whereas a network drive probably counts as “internal”. You could try putting in a URL to any website, with query parameters, and see if the link is clickable to see if moving your deployment to a webserver would have any impact. If that works, then it’s definitely a permission issue, and probably not something you could work around.

      You can try posting to the Outlook Development forum on MSDN and see if someone there has an idea how to work around it.

      Robin

  19. How to start minimized ClickOnce application on windows startup? | PHP Developer Resource Says:

    [...] long as you are using at least .Net 3.5 SP1 you can pass parameters to the app-ref shortcut, see http://robindotnet.wordpress.com/2010/03/21/how-to-pass-arguments-to-an-offline-clickonce-applicatio… or if that’s TL;DR see Clickonce appref.ms argument Tagged: [...]

  20. Manish Bansal Says:

    Great article, saved my ass! thanks a lot :)

  21. Dylow Says:

    Hi Robin,

    I am working on a ClickOnce Win Forms App in VB.net. The app is going to add it’s self to Schtasks. So I will need to pass an argument to the app when it will run “by it’s self”.

    I did something like this:

    Dim inputArgs As String() = _ AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData

    MsgBox(Len(inputArgs))

    When I call the app from cmd without args, the MsgBox promts “0”. If I add an arg, the application crashes when it reaches the MsgBox line.

    I cannot seem to find a way to pass any arguments…Have you ever encountered this issue ?

    • robindotnet Says:

      You need to check ActivationData for null before trying to convert it into an array. Also, I don’t know how you are calling the application. The only way to pass arguments to a ClickOnce app is using the deployment URL and appending them as query parameters. You can do a process.start on the exe (if you know where it is), but this will not do the “ClickOnce” bits and update the application if there is an update.

  22. George Says:

    Hello

    I would like to install a free aplication (type ClickOnce Application Reference) on a computer separated from internet for offline working (program can work offline, just for install is required internet connection). Normally be installed running a binary file (420 kb) available on the producer server, then install only from internet about 40 mb of data (a offline installer is not available). Can anyone help me with a solution? Thanks.

  23. pascal Says:

    I have built two software which have file associated with them.

    A Carré magique => associated with .magsq

    B labyrinthales => associated with .laby

    those files are in fact xml files read by my softs to build some A4 shit of mathematical materials for pupils to increase their skills.

    I follow this good example to do it :

    http://social.msdn.microsoft.com/Forums/fr-FR/winformssetup/thread/46a4d64c-34f9-4513-aa46-b2187fbdca96

    If i install soft A alone all is ok the icon, the file association works too.

    If i install soft B alone all is ok the icon, the file association works too.

    If i install soft A then soft B, only the file association of the first soft installed works…

    If i install soft B then soft A, only the file association of the first soft installed works…

    I don’t know why? Any idea?

    I look to GUID because the two softs are quiet similar and comes from the same base of vb codes. I discover that they have the same GUID… So i changed the GUID of one of them. I have uninstalled every thing and have installed them again…. but the issue persists argh !

    can you help ?

    soft A link : http://www.scalpa.info/logiciels/laby/index.htm

    soft B link : http://www.scalpa.info/logiciels/carre/index.htm

    thanks and sorry for my approximative english

    pascal

    • robindotnet Says:

      This should work. I even double-checked with the ClickOnce guy at Microsoft. Have you tried installing the applications on another machine to make sure it’s not just your cache and registry being screwed up from the initial installs?
      Robin

      • pascal Says:

        Hello
        Thanks for your reply. I changed the GUID and i signed with a new certificat for every things (each dll and each exe) and now it works: each software has its own fileassociation with its own icon and When I click on a file, previously developed by my software, it opens fine.. thanks!

  24. EddieMurphy Says:

    Is there a way to call my ClickOnce (file share) Application with params from SAP?

    • robindotnet Says:

      SAP? The financial software? I have no idea. You can invoke the application using the file share path and pass parameters to it, like \\myserver\\myshare\myapp.application?param1=hello&param2=world. Is that what you mean?
      Robin

  25. Matt Says:

    When I copied part of your code into my program and compile I get the following error for the line:

    string[] activationData = AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData;

    The error message says NullReferenceException was unhandled. I do not get this problem running your program TestRunningWithArgs. Can you help me with this?

    • robindotnet Says:

      Are you running it in Visual Studio or as a ClickOnce application? That data is NULL (and it should be checked for NULL, which I should have done, but didn’t) if you run it from the exe or from Visual Studio. It has to be running as a ClickOnce application to access that information.

      Robin

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

Join 66 other followers

%d bloggers like this: