Using ClickOnce getting two instances of the progr

2019-08-18 06:48发布

问题:

I've started getting ClickOnce setup and using the code below after calling Application.Restart(); I end up with two instances of the application (the newly updated one and the previous version).

    private static void CheckForUpdate() {
            ApplicationDeployment updateCheck = ApplicationDeployment.CurrentDeployment;
            UpdateCheckInfo info = updateCheck.CheckForDetailedUpdate();
            if(info.UpdateAvailable) {
                updateCheck.Update();
                MessageBox.Show( "The application has been upgraded, and will now restart." );
                Application.Restart();
            }
    }//this has try/catch but didn't think it would be relevant 
     //since no exception is being thrown

This is my first time using click once and while I'm amazed at the simplicity of it I wasn't quite sure WHERE to put the manual check (I don't want the users to have a choice or see the ugly update window).

I've placed it both in my MainForm.cs and my Program.cs with the same results for both. I'm including my Main() just in case it's relevant.

[STAThread]
static void Main() {
    CheckForUpdate();
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault( false );
    Application.Run( new MainForm() );
}

回答1:

Ok, figured it out. The link provided by Jeremy Thompson gave me some ideas...mainly the delay, but without implementing a sleeping thread (since in the instance of that code simply delayed the start of the app).

What I ended up doing was implementing an optional Parameter in my default constructor that started a timer (set for 1.5 seconds, but could be modified as needed if app needed more startup time). When the Timer's Tick event fired THAT'S when I called Application.Restart(). My app has some Active Directory calls and Database Calls to get itself setup like it should and I'm guessing this is what caused the unexpected results, but I'm not sure.

NEW CONSTRUCTOR...

public MainForm(bool WasUpdated = false) {
    InitializeComponent();
    if(!WasUpdated) {
        instance = this;
        Icon = DesktopInterface.MainForm.Properties.Resources.favicon;
        tabPages = DesktopInterface.MainForm.Setup.TabSetup.GetTabs();
        foreach(var page in tabPages) {
            tabControl1.TabPages.Add( page );
        }
    } else {
        restartTimer.Start();
    }
}

and my modified Program.cs

[STAThread]
static void Main() {
    CheckForUpdate();
    SetAddRemoveProgramsIcon();
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault( false );
    Application.Run( new MainForm(WasUpdated) );
}
//WasUpdated is a class level variable that gets set inside of the CheckForUpdate method

Full CheckForUpdate() Method....

private static void CheckForUpdate() {
            try {
                ApplicationDeployment updateCheck = ApplicationDeployment.CurrentDeployment;
                UpdateCheckInfo info = updateCheck.CheckForDetailedUpdate();
                if(info.UpdateAvailable) {
                    updateCheck.Update();
                    WasUpdated = true;
                    MessageBox.Show( "The application has been upgraded, and will now restart." );
                }
            } catch(DeploymentDownloadException ex) {
                MessageBox.Show( "Automatic Update Failed.  Error: " + ex );
            } catch(InvalidDeploymentException ex) {
                MessageBox.Show( "Automatic Update Failed.  Error: " + ex );
            } catch(DeploymentException ex) {
                MessageBox.Show( "Automatic Update Failed.  Error: " + ex );
            } catch(Exception ex) {
                MessageBox.Show( "Automatic Update Failed.  Error: " + ex );
            }
        }

What I'll end up doing after posting this is switching the CheckForUpdate() to have a return value and then just passing that to the MainForm Constructor instead of having a class level flag in Program.cs