代码之家  ›  专栏  ›  技术社区  ›  Ben Laan

如何使用Click Once构建单实例应用程序?

  •  15
  • Ben Laan  · 技术社区  · 17 年前

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

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

    有人知道在一次点击部署场景中如何实现这一点吗?

    4 回复  |  直到 13 年前
        1
  •  21
  •   ping    17 年前

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

    1. 一台PC上的多个实例被禁用。通过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以检查更新。更新完成后,将启动主窗体并隐藏更新进度。

    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
  •  5
  •   jitbit    17 年前

    当然 -您可以禁用自动的ClickOnce更新检查(在“发布”->更新..对话框),然后使用 System.Deployment.Application 用于实际检查更新的命名空间。

    退房:

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

        3
  •  0
  •   Richard Harrison    17 年前

    我认为您不能这样做,因为运行前检查超出了您的代码范围。

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

    如果您需要更多的控制,那么您可以使用 ApplicationDeployment Update CheckForUpdate 方法对更新过程具有绝对值。

        4
  •  0
  •   Mark mergoc    13 年前

    我用过 http://wpfsingleinstance.codeplex.com/ 在我的WPF ClickOnce应用程序中非常成功。我不需要改变任何东西。

    推荐文章