【问题标题】:How can I build a single instance application using Click Once?如何使用 Click Once 构建单实例应用程序?
【发布时间】:2008-10-29 22:56:58
【问题描述】:

我需要一个单实例应用程序(根据这个answer),但它需要通过单击一次来部署。

问题是我要求单击一次不会自动检测更新尝试在应用程序运行时加载较新版本。如果它正在运行,那么我需要激活另一个实例。通常,在选择单击一次链接时,它的第一件事是尝试找到更新。我想拦截它并检查启动正常更新过程之前是否有一个已经在运行的实例。

有谁知道这在 Click Once 部署方案中是如何实现的?

【问题讨论】:

  • 当我多次打开应用程序时,"bool ok" 总是设置为 true。

标签: .net clickonce single-instance


【解决方案1】:

为了解决这个问题,我们构建了一个原型应用程序,它具有以下两个功能。

  1. 一台电脑上的多个实例被禁用。通过 clickonce 部署单实例应用程序。当用户尝试启动应用程序的第二个实例时,会弹出一条消息,指示“另一个实例已在运行”。

  2. 异步检查更新,如果存在则安装更新。如果用户运行新实例时有可用更新,则会弹出一条消息:“有可用更新”。

构建演示应用程序的过程如下:

第 1 步:使用 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() );
        }
    }
}

第 2 步:以编程方式处理更新

在我们这样做之前,我们应该禁用自动 ClickOnce 更新检查(在 Publish -- Updates... 对话框中)。

然后我们创建两个表单:UpdateProgress 和 mainForm,其中 UpdateProgress 表示下载进度,mainForm 表示主应用程序。

当用户运行应用程序时,updateProgress 将首先启动以检查更新。更新完成后,mainForm 将启动,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();
            }
        }
    }
}

该应用程序运行良好,我们希望它能很好地解决问题。
特别感谢Timothy Walters 提供源码

【讨论】:

    【解决方案2】:

    当然 - 您可以禁用自动 ClickOnce 更新检查(在 Publish -> Updates.. 对话框中),然后使用 System.Deployment.Application 命名空间中的对象和命令实用地检查更新.

    退房:

    如果有更新,您可以在实际更新之前执行单实例应用程序检查,方法是调用:

    【讨论】:

      【解决方案3】:

      我认为你不能这样做,因为运行前的检查在你的代码之外。

      但是,您可以更改 clickonce 部署选项以在代码执行期间检查更新。

      如果您需要更多控制,则可以使用ApplicationDeployment UpdateCheckForUpdate 方法对更新过程进行绝对控制。

      【讨论】:

        【解决方案4】:

        我在我的 WPF ClickOnce 应用程序中使用了http://wpfsingleinstance.codeplex.com/,并取得了巨大的成功。我不需要改变任何东西。

        【讨论】:

        • 在哪里添加这一行以及如何声明“虚拟枚举”?
        猜你喜欢
        • 1970-01-01
        • 2020-05-31
        • 2013-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-03
        相关资源
        最近更新 更多