Command-Line into ClickOnce


The company I’m with right now has totally bought into ClickOnce. I’ll admit that before I started working here I’d never heard of it. In theory it seems like a nice idea, and generally it seems to work well, but like a lot of Microsoft products, it’s kind of stagnating. I get the impression Microsoft is calling it done on this one, and if you google it, you’ll get some great results like these.

I’m not here right now to discuss the merits or downsides of ClickOnce, I’m just going to describe my (painful) experiences attempting to get command-line access into my application.

I’m a big fan of shortcuts, especially AutoHotKey, so I thought I’d create a shortcut to some of the most-used functions. I’ve done it before and it worked great, how much different could this be…

First things first, ClickOnce doesn’t tell you where it’s installed, so part one of this, is find the application you’ve deployed. The best way I could find to do this was to go to the Start Menu and locate the shortcut. Normally, this would be just a shortcut, and point you in the right direction, right? Nope, it’s actually an application reference, which it turns out, you can use to reference the real app.

On my Win 8.1 system I found this shortcut here – C:\Users\**YOU**\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\MyAwesomeApp.

The application I was trying to use here was a WinForms app, so YMMV if you’re trying this in WPF or straight up in a console app. Hopefully it’s similar enough that you can hack along here with me.

OK, locate the application entry point, in WinForms this is Program.cs. You’ll need to add a check for the args being passed in and, in my case, attach a console app so the user can see what’s actually happening as you’re working.

static class Program
    private static extern bool AttachConsole(int dwProcessId);
    private const int ATTACH_PARENT_PROCESS = -1;

    static void Main()

    private static void Initialize()
        // Read the command line arguments
        string[] args = AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData;

        if (args == null || args.Length == 0)
            Application.Run(new Form1());
            // Only attach the console when using the command line

            CmdLineHandler cmdLineObject = new CmdLineHandler(args[0].Split('!'));

So, as you can see, I’m PInvoking the console control (who doesn’t love a good pinvoke). In Initialize(), I’m checking to see if any args are being passed to the ClickOnce app using


This way I can start up the regular old forms app if no args are passed, or jump to the console app if there are. Then I’ve created a nice CmdLineHandler class to handle all the string parsing magic to make this work.

Here’s the important part, I’m serious, don’t skim over this part – you CANNOT use comma or space to separate your args when command-lining into a ClickOnce app. OK, I think I’ve bolded enough to make that stand out. For some reason, the comma in your args get’s swallowed. I’m not sure where it happens, but I know it happened every single time (I tried a bunch of different ways). Feel free to give it a try, hey, if you get it working, let me know!

OK, so I’m splitting those input args by !, which looks weird, but works fine. Once you’ve got the args, it’s up to you, I’m sure you’ll be fine.

Here’s the final command to my app

C:\Users\**YOU**\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\MyAwesomeApp -option1!yes!-option2!no

Again, I’m not endorsing ClickOnce, or this really, but if you’re stuck in this situation at work, this is my workaround.