1

Sorry for the lengthy post, I just want to illustrate my situation as best as possible. Read the items in bold and check the code if you want the quick gist of the issue.

I use ClickOnce to deploy a C# application, and have opted to have my application check for updates manually using the ApplicationDeployment Class rather than letting it do the update checking for me.

The program is a specialized network scanner that searches for network devices made by the company I work for. Once the main window is loaded, a prompt is displayed asking if the user would like to scan the network. If they say Yes, a scan begins which can take a minute or two to complete depending on their network settings; otherwise it just waits for the user to do some action.

One of the last things I do in Form_Load is create a new thread that checks for updates. This had all been working fine for several months through about 12 releases and has suddenly stopped working. I didn't change the update code at all, nor change the sequence of what happens when the app starts.

In staring at the code, I think I see why it is not working correctly and wanted to confirm if what I think is correct. If it is, it begs the question as to why it DID work before - but I'm not too concerned with that either.

Consider the following code:

frmMain.cs

private void Form1_Load(object sender, EventArgs e)
{
    // set up ui, load settings etc

    Thread t = new Thread(new ParameterizedThreadStart(StartUpdateThread));
    t.Start(this);
}

private void StartUpdateThread(object param)
{
    IWin32Window owner = param as IWin32Window;

    frmAppUpdater.CheckForUpdate(owner);
}

frmAppUpdater.cs

public static void CheckForUpdate(IWin32Window owner)
{
    if (ApplicationDeployment.IsNetworkDeployed) {
        Console.WriteLine("Going to check for application updates.");
        parentWindow = owner;

        ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
        ad.CheckForUpdateCompleted += new CheckForUpdateCompletedEventHandler(ad_CheckForUpdateCompleted);
        ad.CheckForUpdateProgressChanged += new DeploymentProgressChangedEventHandler(ad_CheckForUpdateProgressChanged);

        ad.CheckForUpdateAsync();
        // CAN/WILL THE THREAD CREATED IN FORM1_LOAD BE TERMINATED HERE???
    }
}

When the CheckForUpdateAsync() callback completes, if no update is available the method call simply returns; if an update IS available, I use a loop to block until 2 things occur: The user has dismissed the "Would you like to scan prompt" AND no scan is currently running.

The loop looks like this, which takes place in ad_CheckForUpdateCompleted:

while (AppGlobals.ScanInProgress || AppGlobals.ScanPromptVisible) {
    System.Threading.Thread.Sleep(5000);
}

I sleep for 5 seconds because I figured this was happening in a separate thread and it has seemed to work well for a while.

My main question about the above code is:
When ad.CheckForUpdateAsync(); is called from CheckForUpdate does the thread I created in Form1_Load terminate (or might it terminate)? I suspect it may because the subsequent Async call causes the method to return, and then start another thread?

The only reason I am confused is because this method WAS working for so long without hanging the application and now all of the sudden it hangs and my best effort at debugging revealed that it was that Sleep call blocking the app.

I'd be happy to post the full code for frmAppUpdater.cs if it would be helpful.

drew010
  • 68,777
  • 11
  • 134
  • 162
  • I haven't updated Visual Studio at all or changed the target .NET version of the app, but it may be that the issue only happens on machines with SP1 of .NET 4 Client Profile. I'm looking into that now. The installed version where I am seeing problems are `4.0.30319`. I'm going to try to see if the issue does not occur on .NET 4 SP(0). – drew010 Feb 22 '12 at 22:20

2 Answers2

1

When ad.CheckForUpdateAsync(); is called from CheckForUpdate does the thread I created in Form1_Load terminate (or might it terminate)?

If the CheckForUpdateAsync() call is asynchronous then yes, the thread will terminate, no it won't otherwise.

If you suspect the Sleep to have caused the application hang then these two variables AppGlobals.ScanInProgress and AppGlobals.ScanPromptVisible are probably always set to true! You should start looking at the code that is setting them to true and see what is going on there.

In order to avoid an application hang, you could introduce a variable to avoid sleeping indefinitely:

int nTrials = 0;
while ((AppGlobals.ScanInProgress || AppGlobals.ScanPromptVisible) && (nTrials < 5)) {
    System.Threading.Thread.Sleep(5000);
    nTrials++;
}
// Check the results and act accordingly

I personally do not like using Sleep for thread synchronization. .NET offers a bunch of classes that are perfect for thread synchronization, WaitHandle being one of them.

GETah
  • 20,922
  • 7
  • 61
  • 103
  • As best I can tell, the program doesn't even get that far anymore - if it does, it is now running that loop in the main UI thread as opposed to wherever it seemed to be running before where we didn't experience this issue. Those values do get changed to false, but now that the app hangs it never gets that far because it won't respond to input on the Yes/No prompt. – drew010 Feb 22 '12 at 22:23
  • The `System.Threading.Thread.Sleep(5000);` should never be executed in the main UI thread as it will cause your application to hang for 5 seconds at least (if it loops only once) – GETah Feb 22 '12 at 22:32
  • It was my understanding that it was being run from a separate thread before as it never caused the app to hang even while it was sitting at that point for several minutes waiting for the app to terminate. For some reason, something has changed the behavior of the app and threads. I agree I should have used a WaitHandle though. Thanks for pointing out about the thread terminating, it must be what is happening but for some reason wansn't happening right away before. – drew010 Feb 22 '12 at 22:34
  • If you want to confirm that indeed the code is running in the UI thread, just call InvokeRequired() if it returns true, then you're not on the UI thread and the UI thread is hanging for some other reason. –  Feb 23 '12 at 00:22
1

See this post at Asynchronous Delegates Vs Thread/ThreadPool?

your form load method seems to be doing synchronous work. you mention that you are using clickonce deployment. Has the binary location changed after the previous release or has permissions on this resource changed. Looks like the work (checkupdates) in the Thread is never finishing and is never handed back to the form.

as an immediate fix, I would change the Thread approach to Delegate - if you use delegate, then this becomes less of a customer issue (the form will respond to end user) but the underlying problem remains.

as the next step, i would go through http://msdn.microsoft.com/en-us/library/ms229001.aspx and do the troubleshoot

Community
  • 1
  • 1
Krishna
  • 2,451
  • 1
  • 26
  • 31
  • Thanks I'll look into that. I confirmed with Wireshark that the application manifests and the deploy file for the new version DO get downloaded by the hung app. I think the thread is now being joined to the main thread whereas before it was not for some reason. – drew010 Feb 22 '12 at 22:22
  • The binary and permissions on the clickonce app have not changed - Since I confirmed the manifests are being downloaded, `ad.CheckForUpdateAsync();` which now makes me think the thread has terminated and now the async callback is happening on the main thread where before it was not. I'm investigating as to whether or not this may be due to .NET 4 SP1 updates. – drew010 Feb 22 '12 at 22:28