Quando arriva il momento della distribuzione dell'applicativo, nel limite del possibile preferisco usare ClickOnce (con qualche attenzione magari, come mostrato qui) perchè la trovo utilissima perchè si arrangia a controllare la presenza sul pc di tutto quello che serve, impostabile nei "Prerequisiti" del progetto.
Anche se a mio avviso ci sono ancora delle piccole lacune nella tecnlogia, come ad esempio se c'è un proxy di mezzo con authenticazione non funziona, piuttosto che non si può impostare una directory iniziale a piacimento e qualche altra piccola cosetta...direi che è proprio una manna dal cielo spesso e volentieri.
Ma c'è una cosa trovo fastidiosa: non poter forzare sempre l'aggiornamento ad ogni nuova pubblicazione.
L'opzione ci sarebbe dalla maschera delle Impostazioni -> Publish -> Updates -> Specify minimum version, ma sinceramente non ho molta voglia di stare ogni volta ad aggiornare il numero con quello corrente, (che non si fa in automatico e bisogna ogni volta metterlo a mano...)
Perciò spesso mi affido a una maschera che è quasi sempre identica tra i vari applicativi e non fa altro che utilizzare la classe ApplicationDeployment ed andare a controllare se esiste una nuova versione ed in quel caso aggiornare, e poi riavviare l'applicazione.
Il risultato quindi è rimpiazzare la maschera di update standard con una a nostro piacimento:
Il codice è abbastanza semplice, ma prima di far apparire la maschera, bisogna dire all'applicazione che il check delle nuove versioni venga fatto dopo che l'applicazione parte:
Successivamente da codice invece, andiamo a controllare l'update da codice facendo partire prima la "form di aggiornamento" (es: FormUpdating) dove viene fatto il controllo se è presente una nuova release ed aggiornato di conseguenza, oppure semplicemente si esce senza fare altro, in modo che il thread possa lanciare la nostra maschera principale (es: MainForm) con il metodo Run.
Tutto questo è fattibile nell'Entry Point del programma, quindi il codice va dentro alla classe Program.cs:
static class Program
{
///
/// The main entry point for the application.
///
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
//--- prima di partire controlliamo se è stata distribuita una nuova versione
new FormUpdating().ShowDialog();
//--- poi proseguiamo con la maschera principale dell'applicazione
Application.Run(new MainForm());
}
}
Cosa fa la FormUpdating? Niente di speciale, nel load va a verificare se c'è un update tramite la classe ApplicationDeployment come dicevamo appunto, la quale espone una serie di eventi belli e pronti per gestire l'update:
private void FormUpdating_Load(object sender, EventArgs e)
{
if (ApplicationDeployment.IsNetworkDeployed)
{
this.Show();
this.Refresh();
//--- controllo se c' un update e poi lo scarico altrimenti esco
ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
ad.CheckForUpdateProgressChanged += new DeploymentProgressChangedEventHandler(ad_CheckForUpdateProgressChanged);
ad.CheckForUpdateCompleted += new CheckForUpdateCompletedEventHandler(ad_CheckForUpdateCompleted);
ad.UpdateProgressChanged += new DeploymentProgressChangedEventHandler(ad_CheckForUpdateProgressChanged);
ad.UpdateCompleted += new AsyncCompletedEventHandler(ad_UpdateCompleted);
ad.CheckForUpdateAsync();
}
else
Fine(); //--- esco e torno al thread principale
}
Dopodichè basta un minimo di codice per visualizzare una progressbar:
void ad_CheckForUpdateProgressChanged(object sender, DeploymentProgressChangedEventArgs e)
{
try
{
progressBar1.Maximum = Convert.ToInt32(e.BytesTotal / 1024);
progressBar1.Value = Convert.ToInt32(e.BytesCompleted / 1024);
progressBar1.Refresh();
percLabel.Text = (progressBar1.Value / progressBar1.Maximum) * 100 + "%";
percLabel.Refresh();
}
catch { }
}
void ad_CheckForUpdateCompleted(object sender, CheckForUpdateCompletedEventArgs e)
{
if (e.Error != null)
Fine();
if (e.UpdateAvailable)
{
aggiornato = true; //--- mi segno che ho aggiornato, quindi nel Fine devo riavviare
messaggioLabel.Text = "Updating...";
//--- eseguo l'update vero e proprio in maniera async
ApplicationDeployment.CurrentDeployment.UpdateAsync();
}
else
Fine(); //--- esco e torno al thread principale
}
void ad_UpdateCompleted(object sender, AsyncCompletedEventArgs e)
{
Fine();
}
Infine il codice del metodo "Fine" richiamato da più parti, verifica soltanto se abbiamo aggiornato l'applicazione e quindi si rende necessario il Restart, oppure se non abbiamo trovato niente di nuovo, e deve uscire direttamente senza fare altro:
private void Fine()
{
this.DialogResult = DialogResult.OK;
this.Close(); //--- esco e torno al thread principale
//--- se ho aggiornato riavvio anche l'applicazione
//--- in modo che il Fmw mi installi la nuova versione
if (aggiornato)
Application.Restart();
}
Chiaramente con il comando "Restart" fa morire l'app e la riavvia quindi ripassa per il load di questa maschera, ma questa volta non trova più aggiornamenti (dato che li ha appena installati) e quindi esce diretta facendo finalmente partire la MainForm.
Questo FormUpdating è un trucchetto che ho usato spesso e si è rivelato molto comodo, soprattutto per il fatto che non chiede niente all'utente che avvia l'applicazione (che potrebbe porsi problemi filosofici sul fatto che sia il caso o meno di aggiornare...) sia perchè mi capita spesso che nelle prime fasi di distribuzione dell'applicativo, magari ad uso interno, in seguito a vari bug fix vorrei essere sicuro che tutti siano allineati all'ultima versione.