この問題に取り組むために、次の2つの機能を備えたプロトタイプアプリケーションを作成しました。
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の自動更新チェックを無効にする必要があります([公開]-[更新...]ダイアログで)。
次に、UpdateProgressとmainFormの2つのフォームを作成します。ここで、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();
}
}
}
}
アプリケーションはうまく機能し、問題の良い解決策になることを願っています。ソースコードを提供してくれたTimothyWalters
に特に感謝します