Pregunta

Necesito tener una aplicación de instancia única (según esto respuesta ), pero debe implementarse haciendo clic una vez.

El problema es que requiero que el clic una vez no detecte automáticamente una actualización e intente cargar una versión más nueva mientras se ejecuta la aplicación. Si se está ejecutando, entonces necesito que la otra instancia se active. Por lo general, al seleccionar un enlace Hacer clic una vez, lo primero que hace es intentar encontrar una actualización. Quiero interceptar esto y buscar una instancia que ya se esté ejecutando antes para iniciar el proceso de actualización normal.

¿Alguien sabe cómo es esto posible dentro de un escenario de implementación Click Once?

¿Fue útil?

Solución

Para abordar el problema, creamos una aplicación prototipo que tiene las siguientes dos funcionalidades.

  1. Se deshabilitan varias instancias en una PC. Una aplicación de instancia única se implementa a través de clickonce. Cuando un usuario intenta iniciar una segunda instancia de la aplicación, aparecerá un mensaje emergente que indica que "Otra instancia ya se está ejecutando".

  2. Comprueba una actualización de forma asíncrona e instala la actualización si existe. Un mensaje: " Hay una actualización disponible " aparecerá si hay una actualización disponible cuando un usuario ejecuta una nueva instancia.

El proceso para compilar la aplicación de demostración es el siguiente:

Paso 1: detecte una aplicación de instancia activa utilizando la clase Mutex.

namespace ClickOnceDemo
{
    static class Program
    {
        /// summary>
        /// The main entry point for the application.
        /// /summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault( false );
            bool ok;
            var m = new System.Threading.Mutex( true, "Application", out ok );
            if ( !ok )
            {
                MessageBox.Show( "Another instance is already running.", ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString() );
                return;
            }
           Application.Run( new UpdateProgress() );
        }
    }
}

Paso 2: maneja la actualización mediante programación

Antes de hacerlo, debemos deshabilitar la comprobación automática de actualizaciones de ClickOnce (en el cuadro de diálogo Publicar - Actualizaciones ...).

Luego creamos dos formas: UpdateProgress y mainForm, donde UpdateProgress indica el progreso de la descarga y mainForm representa la aplicación principal.

Cuando un usuario ejecuta la aplicación, updateProgress se iniciará primero para verificar si hay actualizaciones. Cuando se complete la actualización, se iniciará mainForm y se ocultará updateProgress.

namespace ClickOnceDemo
{
public partial class UpdateProgress : Form
 {
  public UpdateProgress()
        {
            InitializeComponent();
            Text = "Checking for updates...";

            ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
            ad.CheckForUpdateCompleted += OnCheckForUpdateCompleted;
            ad.CheckForUpdateProgressChanged += OnCheckForUpdateProgressChanged;

            ad.CheckForUpdateAsync();
       }

        private void OnCheckForUpdateProgressChanged(object sender, DeploymentProgressChangedEventArgs e)
        {
            lblStatus.Text = String.Format( "Downloading: {0}. {1:D}K of {2:D}K downloaded.", GetProgressString( e.State ), e.BytesCompleted / 1024, e.BytesTotal / 1024 );
            progressBar1.Value = e.ProgressPercentage;
        }

        string GetProgressString( DeploymentProgressState state )
        {
            if ( state == DeploymentProgressState.DownloadingApplicationFiles )
            {
                return "application files";
            }
            if ( state == DeploymentProgressState.DownloadingApplicationInformation )
            {
                return "application manifest";
            }
            return "deployment manifest";
        }

        private void OnCheckForUpdateCompleted(object sender, CheckForUpdateCompletedEventArgs e)
        {
            if ( e.Error != null )
            {
                MessageBox.Show( "ERROR: Could not retrieve new version of the application. Reason: \n" + e.Error.Message + "\nPlease report this error to the system administrator." );
                return;
            }
            if ( e.Cancelled )
            {
                MessageBox.Show( "The update was cancelled." );
            }

            // Ask the user if they would like to update the application now.
            if ( e.UpdateAvailable )
            {
                if ( !e.IsUpdateRequired )
                {
                    long updateSize = e.UpdateSizeBytes;
                    DialogResult dr = MessageBox.Show( string.Format("An update ({0}K) is available. Would you like to update the application now?", updateSize/1024), "Update Available", MessageBoxButtons.OKCancel );
                    if ( DialogResult.OK == dr )
                    {
                        BeginUpdate();
                    }
                }
                else
                {
                    MessageBox.Show( "A mandatory update is available for your application. We will install the update now, after which we will save all of your in-progress data and restart your application." );
                    BeginUpdate();
                }
            }
            else
            {
                ShowMainForm();
            }
        }

        // Show the main application form
        private void ShowMainForm()
        {
            MainForm mainForm = new MainForm ();
            mainForm.Show();
            Hide();
        }

        private void BeginUpdate()
        {
            Text = "Downloading update...";
            ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
            ad.UpdateCompleted += ad_UpdateCompleted;
            ad.UpdateProgressChanged += ad_UpdateProgressChanged;

            ad.UpdateAsync();
        }

        void ad_UpdateProgressChanged( object sender, DeploymentProgressChangedEventArgs e )
        {
            String progressText = String.Format( "{0:D}K out of {1:D}K downloaded - {2:D}% complete", e.BytesCompleted / 1024, e.BytesTotal / 1024, e.ProgressPercentage );
            progressBar1.Value = e.ProgressPercentage;
            lblStatus.Text = progressText;
        }

        void ad_UpdateCompleted( object sender, AsyncCompletedEventArgs e )
        {
            if ( e.Cancelled )
            {
                MessageBox.Show( "The update of the application's latest version was cancelled." );
                return;
            }
            if ( e.Error != null )
            {
                MessageBox.Show( "ERROR: Could not install the latest version of the application. Reason: \n" + e.Error.Message + "\nPlease report this error to the system administrator." );
                return;
            }

            DialogResult dr = MessageBox.Show( "The application has been updated. Restart? (If you do not restart now, the new version will not take effect until after you quit and launch the application again.)", "Restart Application", MessageBoxButtons.OKCancel );
            if ( DialogResult.OK == dr )
            {
                Application.Restart();
            }
            else
            {
                ShowMainForm();
            }
        }
    }
}

La aplicación funciona bien y esperamos que sea una buena solución para el problema.
Gracias especiales a Timothy Walters por proporcionar el código fuente

Otros consejos

Seguro : puede desactivar la comprobación automática de actualizaciones de ClickOnce (en el cuadro de diálogo Publicar - > Actualizaciones ..), luego usar los objetos y comandos en el Espacio de nombres System.Deployment.Application para verificar pragmáticamente las actualizaciones.

Echa un vistazo:

Si hay una actualización, puede hacer sus comprobaciones de aplicación de instancia única antes de actualizar, llamando a:

No creo que puedas hacerlo así, ya que la verificación antes de ejecutar está fuera de tu código.

Sin embargo, puede cambiar las opciones de implementación de clickonce para buscar actualizaciones durante la ejecución del código.

Si necesita más control, puede usar Actualización de ApplicationDeployment o CheckForUpdate métodos para tener absoluto sobre el proceso de actualización.

Utilicé http://wpfsingleinstance.codeplex.com/ en mi aplicación WPF ClickOnce con gran éxito. No tuve que cambiar nada.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top