Tronald
Tronald

Reputation: 1585

File association causing Clickonce install to fail

I have a WPF application deployed using ClickOnce. It has one custom file association set with it that can open and be opened in the application if that file type is opened. This has been working great, then all of a sudden during an update users started receiving an "Application Could Not Be Started" errors.

Looking into the error details I can see this:

ERROR DETAILS
    Following errors were detected during this operation.
    * [12/8/2020 3:34:54 PM] System.UnauthorizedAccessException
        - Attempted to perform an unauthorized operation.
        - Source: mscorlib
        - Stack trace:
            at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str)
            at Microsoft.Win32.RegistryKey.SetValue(String name, Object value, RegistryValueKind valueKind)
            at System.Deployment.Application.ShellExposure.AddFileAssociation(FileAssociation fileAssociation, DefinitionIdentity subId, Uri deploymentProviderUri)
            at System.Deployment.Application.ShellExposure.AddShellExtensions(DefinitionIdentity subId, Uri deploymentProviderUri, AssemblyManifest appManifest)
            at System.Deployment.Application.ShellExposure.UpdateShellExtensions(SubscriptionState subState, ShellExposureInformation& shellExposureInformation)
            at System.Deployment.Application.ShellExposure.UpdateSubscriptionShellExposure(SubscriptionState subState)
            at System.Deployment.Application.SubscriptionStore.CommitApplication(SubscriptionState& subState, CommitApplicationParams commitParams)
            at System.Deployment.Application.ApplicationActivator.InstallApplication(SubscriptionState& subState, ActivationDescription actDesc)
            at System.Deployment.Application.ApplicationActivator.PerformDeploymentActivation(Uri activationUri, Boolean isShortcut, String textualSubId, String deploymentProviderUrlFromExtension, BrowserSettings browserSettings, String& errorPageUrl, Uri& deploymentUri)
            at System.Deployment.Application.ApplicationActivator.PerformDeploymentActivationWithRetry(Uri activationUri, Boolean isShortcut, String textualSubId, String deploymentProviderUrlFromExtension, BrowserSettings browserSettings, String& errorPageUrl)
--- End of stack trace from previous location where exception was thrown ---
            at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
            at System.Deployment.Application.ApplicationActivator.PerformDeploymentActivationWithRetry(Uri activationUri, Boolean isShortcut, String textualSubId, String deploymentProviderUrlFromExtension, BrowserSettings browserSettings, String& errorPageUrl)
            at System.Deployment.Application.ApplicationActivator.ActivateDeploymentWorker(Object state)

I determined it was the file association as that is the only thing touching the registry by ClickOnce AFIK. After removing it, the issue went away, but now the program has no file extensions. I've tried installing with elevated permissions, but that hasn't fixed the issue either. Adding the extension back caused the issue to return. We didn't touch any of the underlying file association or startup logic on this update so what the heck would be causing this issue?

Has anyone experienced this working with ClickOnce and file associations?

Edit: Using VS2017's Publish->Options to create the associations (which drops the info into the .csproj file).

<ItemGroup>
    <FileAssociation Include=".simsp">
      <Visible>False</Visible>
      <Description>My Description</Description>
      <Progid>MyProgram.Proposal</Progid>
      <DefaultIcon>Proposal.ico</DefaultIcon>
    </FileAssociation>
</ItemGroup>

Also, after uninstalling and removing AppData files related to the app, the app installed on one machine fine, but not the other under the same user account (we use file sync to replicate desktops). This is confusing as I am not sure why the install would fail during Registry entry if it's an issue in the AppData files located in the Users path.

EDIT 3: The issue seems to occur randomly, but we noticed it occurs when a file is opened, causing the ClickOnce app to open and update. Thus far, the issue seems isolated to user using file sync, replicated desktops who have mutliple workstations.

Best practices to work around this? It may just be a ClickOnce limitation.

Upvotes: 2

Views: 456

Answers (2)

AdrAs
AdrAs

Reputation: 684

Looking at the Stacktrace I see the Registry namespace at the top. So in addition to Maxim's answer I'd say that it happens while clickonce tries to set the value of a registry key. You can also set permissions in regdit. Right click a key, select permissions, and make sure read and write are set for the user running the application.

Since you already figured out the registry keys involved you might have a good chance to resolve it by giving writing permission to these keys to the users.

Upvotes: 1

Maxim
Maxim

Reputation: 869

    - Attempted to perform an unauthorized operation.

This error says us - operation blocked by some protection rules, that can be:

  1. UAC
  2. Computer security policy for user
  3. Domain security policy for user

So, you can solve by setting ClickOnce manifest only UAC. I created ClickOnce manifest, that has minimum one file accociation with app, and It works fine. UAC asks user to perfome installation or update app, and all works perfect. But all users don't has any file sync to another computer. If them need my app - them install it on another computer, and then update if needed.

In .proj file I have two general sections of ClicOnce manifest generation:

<ItemGroup>
    <FileAssociation Include=".appex">
      <Visible>False</Visible>
      <Description>Exception info file</Description>
      <Progid>My.AppClient</Progid>
      <DefaultIcon>Images\Exception.ico</DefaultIcon>
    </FileAssociation>
  </ItemGroup>

And prerequsities section:

 <ItemGroup>
    <BootstrapperPackage Include=".NETFramework,Version=v4.7">
      <Visible>False</Visible>
      <ProductName>Microsoft .NET Framework 4.7 %28x86 and x64%29</ProductName>
      <Install>true</Install>
    </BootstrapperPackage>
    <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
      <Visible>False</Visible>
      <ProductName>.NET Framework 3.5 SP1</ProductName>
      <Install>false</Install>
    </BootstrapperPackage>
    <BootstrapperPackage Include="Microsoft.SqlServer.SQLSysClrTypes.12.0.x64">
      <Visible>False</Visible>
      <ProductName>System types Microsoft® CLR for SQL Server® 2014 %28x64%29</ProductName>
      <Install>true</Install>
    </BootstrapperPackage>    
  </ItemGroup>

So, let think abount users from your 'Edit 3'. ClickOnce uses when you share your app with other users, but your application installs only for one user, not for all users on computer. All new installs, or any update of your app by ClickOnce create new folder in AppData directory for this purposes:

  1. Install new version of app
  2. Save rollback operation to previouse version
  3. App can run only that user, who install it. So, file binding also only for this user (because app files into user profile folder)

Also, there are creating registry settings to open your app from new location. This combination (registry settings and app folders with files) not propertly worsk for users wich has a lot of workstations with sync they data between workstations. Or you need properly sync registry with app folders (all of them).

But! In your app manifest may contain prerequisities (as i show here) with external packages, or you can add it in new version of your app. What do you think will be when user update your app on one computer or install it, and than run updated app (that correctly synced) on another computer without this external links? App will be crashed.

So, in my opinion, your solution will be:

  1. Use WIX project to create package, that can be installed for all users on one computer. And try to not store files in user profile folder, that can will broken application when not properly copied to new computer. Or rewrite it by your app, if it not properly copied. Any update you can check on startup, or register update service, to install new version of you app in background.

  2. Use ClickOnce with rules of sync profile files to not copy app files via policy. User once install your app on computer, profile not deleted after user logged off. Files of your app must not be synced. And you try to register file extension on app startup with evaluated privilegies. On another computer user run clean install, if them needs your app, or update (if it alredy installed). In my expirience, i store all of app settings in database. In user registry settings of db server name and database name wich my app connected to.

  3. On app startup you can run code from another user like this. But, ClickOnce installs for current user, and this solution will not work properly.

Upvotes: 3

Related Questions