Question

We wrote a ClickOnce application that runs on all the PCs at the office here. Today the .NET runtime crashed and logged the following event in the Event Log:

Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.UnauthorizedAccessException
Stack:
    at System.IO.Directory.DeleteHelper(System.String, System.String, Boolean)
    at System.IO.Directory.Delete(System.String, System.String, Boolean)
    at System.IO.Directory.Delete(System.String, Boolean)
    at System.Deployment.Application.TempFile.DisposeUnmanagedResources()
    at System.Deployment.Application.DisposableBase.Finalize()

The application does use System.Deployment to check for new version periodically and download them in the background. That way the new version is ready to go when they launch the application next time.

My BackgroundUpdater class looks like this (in case it's related):

class BackgroundUpdaterService : IBackgroundUpdaterService
{
    private readonly BackgroundWorker backgroundWorker = new BackgroundWorker();
    private readonly DispatcherTimer checkForUpdatesTimer = new DispatcherTimer();

    public BackgroundUpdaterService()
    {
        this.backgroundWorker.WorkerSupportsCancellation = true;
        this.backgroundWorker.DoWork += (s, e) =>
            {
                // Check if the application was deployed via ClickOnce.
                if (!ApplicationDeployment.IsNetworkDeployed)
                {
                    e.Result = UpdateStatus.NotDeployedViaClickOnce;
                    return;
                }

                if (this.backgroundWorker.CancellationPending) return;

                var updateCheck = ApplicationDeployment.CurrentDeployment;

                UpdateCheckInfo info;
                try
                {
                    info = updateCheck.CheckForDetailedUpdate();
                }
                catch (DeploymentDownloadException)
                {
                    e.Result = UpdateStatus.DeploymentDownloadException;
                    return;
                }
                catch (InvalidDeploymentException)
                {
                    e.Result = UpdateStatus.InvalidDeploymentException;
                    return;
                }
                catch (InvalidOperationException)
                {
                    e.Result = UpdateStatus.InvalidOperationException;
                    return;
                }

                if (this.backgroundWorker.CancellationPending) return;

                if (info.UpdateAvailable)
                {
                    e.Result = info.IsUpdateRequired 
                        ? UpdateStatus.UpdateRequired 
                        : UpdateStatus.UpdateAvailable;
                }
                else
                {
                    e.Result = UpdateStatus.NoUpdateAvailable;
                }
            };
        this.backgroundWorker.RunWorkerCompleted += (s, e) =>
            {
                var result = (UpdateStatus) (e.Result);
                this.UpdateRequired =
                    result == UpdateStatus.UpdateAvailable
                    || result == UpdateStatus.UpdateRequired;
                if(!this.UpdateRequired) // stop looking if there is one
                {
                    this.checkForUpdatesTimer.Start();
                }
            };

        this.checkForUpdatesTimer.Interval = new TimeSpan(hours: 0, minutes: 15, seconds: 0);
        this.checkForUpdatesTimer.Tick += (s, e) =>
            {
                this.checkForUpdatesTimer.Stop();
                this.backgroundWorker.RunWorkerAsync();
            };
        this.checkForUpdatesTimer.Start();
    }

    private readonly object updateRequiredLock = new object();
    private bool updateRequired;
    public bool UpdateRequired
    {
        get
        {
            lock(this.updateRequiredLock)
            {
                return this.updateRequired;
            }
        }
        private set
        {
            lock(this.updateRequiredLock)
            {
                this.updateRequired = value;
            }
        }
    }

    public bool Update()
    {
        if(!this.UpdateRequired)
        {
            return false;
        }
        bool result;
        try
        {
            var updateCheck = ApplicationDeployment.CurrentDeployment;
            updateCheck.Update();
            result = true;
        }
        catch (DeploymentDownloadException)
        {
            result = false;
        }
        return result;
    }

    public void Dispose()
    {
        this.checkForUpdatesTimer.Stop();
        this.backgroundWorker.CancelAsync();
        Thread.Sleep(100);
        this.backgroundWorker.Dispose();
    }
}

Anyone else run into this exception from the System.Deployment namespace? What could be the root cause of this?

Was it helpful?

Solution

yes we did run into this issue :) see my answer here: https://stackoverflow.com/a/13711535/1246870 - in 2 words, it's a bug in ClickOnce related to frequent checks for updates.

the only workaround i was able to come up with was to increase timer interval to a larger value. If you have any better ideas i would love to hear them.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top