ClickOnce Deployment - File Association not Registering

Viewed 5998

So here's a brief summary of the problem:

Summary:

I set fileAssociations of my ClickOnce applications, however, they are not registering when I run/update my program (as in, the .bvr files I am trying to associate have no icon and I can't double-click them to start my application).

Extra Info:

I first tried going to Properties -> Publish -> Options -> File Associations and setting my associations from there. After that failed attempt, I tried setting it directly in app.manifest:

<fileAssociation
xmlns="urn:schemas-microsoft-com:clickonce.v1"
extension=".bvr"
description="Behavior File"
progid="GGS.Behavior"
defaultIcon="bvrico.ico"
/>

I've read so many articles about this that I am starting to get frustrated. Some information:

  1. I've set it to a full-trust application (Under security -> Enable ClickOnce security settings -> This is a full trust application)
  2. I am using .NET 4.0 and Visual Studio 2010
  3. Enable "offline" mode

I also handle the data passed through AppDomain, but I doubt that really changes anything.

I was wondering maybe file associations are only set on install.

Anyways, I would really appreciate some insight on this problem. I would really like file associations in my project.

Thanks everyone in advance.

PS: Tested on Windows XP and Windows 7.

Edit: I also have posted this on Microsoft, btw.

http://social.msdn.microsoft.com/Forums/en-US/winformssetup/thread/d610cd55-f3c7-4775-a417-251261832200

If anyone would like to post an answer on there as well. I really can't figure this one out. :D

6 Answers

The only way to get this to work after a ClickOnce deployment is to add/update several registry entries in code. See below.

It's been 8 years, and even in the latest version of Visual Studio 2019 with Windows 10, the file association settings in ClickOnce deployments still don't work. I suspect this is a Windows version issue, as Microsoft seems to change how file associations are stored in the registry with every new version of Windows. But regardless, Visual Studio and ClickOnce should work for the current Windows release. And it does not.

After completing a clean install of my ClickOnce app, I found this one registry entry for my file extension:

Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.xxxxxx

And it added this registry entry for the ProgID:

Computer\HKEY_CLASSES_ROOT\xxxxxxxxxxx

But it did not add a reference from the file extension to the ProgID.

These two entries do nothing in Windows 10. It does not show the icon, does not open the ClickOnce app when you open the file with that extension, and does not show any ClickOnce option in the Open With lists (as a commenter suggested). Note that the registry entry above was created by the deployment but contains no reference to the ClickOnce application or the ProgID that was set up in the ClickOnce properties. In fact, the only application referenced in that file extension registry entry is to "OpenWith.exe".

Solution

After spending 2 days researching this issue and piecing together various ideas, the solution below is the only one I found that works. Simply execute SetAssociation() every time your app starts (or at least immediately after an install/upgrade), and it will solve the file association problem in all versions of Windows.

using Microsoft.Win32;
using System;
using System.Collections.Generic;

namespace MyLibrary
{
    public class FileAssociations
    {
        [System.Runtime.InteropServices.DllImport("Shell32.dll")]
        private static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);

        private const int SHCNE_ASSOCCHANGED = 0x8000000;
        private const int SHCNF_FLUSH = 0x1000;

        public static bool SetAssociation(string extension, string progId, string fileTypeDescription, string applicationFilePath)
        {
            bool madeChanges = false;
            madeChanges |= SetKeyDefaultValue(@"Software\Classes\" + extension, progId);
            madeChanges |= SetKeyDefaultValue(@"Software\Classes\" + progId, fileTypeDescription);
            madeChanges |= SetKeyDefaultValue($@"Software\Classes\{progId}\shell\open\command", "\"" + applicationFilePath + "\" \"%1\"");
            return madeChanges;
        }

        private static bool SetKeyDefaultValue(string keyPath, string value)
        {
            using (var key = Registry.CurrentUser.CreateSubKey(keyPath))
            {
                if (key.GetValue(null) as string != value)
                {
                    key.SetValue(null, value);
                    return true;
                }
            }

            return false;
        }
    }
}

Then execute this on program start. The first item in Environment.GetCommandLineArgs() contains the full path to your exe:

string[] args = Environment.GetCommandLineArgs();
FileAssociations.SetAssociation(FileExtension, ProgID, FileExtensionDescription, args[0]);

The only problem I found with this approach is if your file association executes your .exe directly, the ClickOnce automatic updates will not work because the update info is stored in the Start Menu shortcut, not with the installed application files.

That is a different problem than the question you asked, but to get around that, if the app starts up with the .exe directly, I programmatically locate the Start Menu shortcut to the ClickOnce app (.appref-ms extension), then restart the app using that path (instead of the .exe), pass it the original command-line arguments that were sent to your .exe, then when it restarts, I retrieve THOSE arguments using AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData.

I found Alan's post above most helpful. It worked as it was written for me on one of my computers, but on another it didn't quite get me there. I was hoping to just add a comment to his post, but Stack Overflow says I don't have the reputation points for that.

If you need it, hope you can still find it here.

I also had to remove a key like the following for each extension mapping:

HKEY_CLASSES_ROOT\AppName.FileType.0

Adding this to Adam's removal of the other .ext keys fixed the problem on my other machine.

Related