2009.07.03 뇌이뇬 블로깅
-----------------------------------------
원래 코드프로젝트에서 VB로 된 것을 누군가가 댓글에 C# 코드로...
샘플형식으로 변환을 해 놨다 ㅋㅋ
자동 업데이트 프로그램을 만들고자 하는 사람들은 참조해도 좋을듯...
1. Create a project - click File -> New -> Project -> Class Library
1.1. Name it whatever you want (as long as its name is AutoUpdate )
1.2. Rename Class1.cs to AutoUpdate.cs (accepting to rename all references)
1.2. Double click AutoUpdate.cs and paste (replace whatever is in there) the following
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Diagnostics;
using System.Windows.Forms;
/// <summary>
/// Class to update .exe and .dll through HTTP connection
/// </summary>
/// <author>Eduardo Oliveira</author>
/// <Modified_by>Danny (dbembibre@virtualdesk.es)</Modified_by>
/// <history Danny>
/// Changed: C# translated and retouched
/// Changed: Added method IsUpdatable() to verify if a new update exists
/// Changed: Now you can update any file without need of exe file
/// Changed: If something went wrong, you have a collection to rollback all files to the original state
///</history Danny>
/// <Modified>Tiago Freitas Leal, Feb. 2008 - v.2.1.1</Modified>
/// <history TFL>
/// Changed: General refactoring of names
/// Changed: Refactoring of properties to constants (preparing for resources)
/// Changed: Remote URI doesn't depend on assembly name
/// Changed: Ignore heading "." and "\" on file paths
/// Changed: Ignore second instance of same file path
/// Changed: Add "=ExactVersion" option
/// Changed: ".ToDelete" filenames are prefixed with the old filename (avoid duplicate names)
/// Changed: "return" happens always after "Dispose" operations
/// Changed: Merge "IsUpdatable" into "UpdateFiles"
/// Changed: Move delete operations to new public CleanUp() method
/// Changed: Files to be deleted are made NOT ReadOnly before deletion
/// Changed: Delete operates also on sub-directories
///</history TFL>
public class AutoUpdate
{
// <File Path>;<MinimumVersion> [' comments ]
// <File Path>;=<ExactVersion> [' comments ]
// <File Path>; [' comments ]
// <File Path>;? [' comments ]
// <File Path>;delete [' comments ]
// ...
// Blank lines and comments are ignored
// First parameter - file path (eg. Dir\file.dll)
// Second parameter:
// If the version is specified, the file is updated if:
// - it doesn't exist or
// - the actual version number is smaller than the update version number
// If the version is specified precedeed by a "=", the file is updated if:
// - it doesn't exist or
// - the actual version doesn't match the update version
// If it's an interrogation mark "?" the file is updated only if it doesn't exist
// If the second parameter is not specified, the file is updated only if it doesn't exist (just like "?")
// If it's "delete" the system tries to delete the file
// "'" (chr(39)) start a line (or part line) comment (like VB)
// Method return values
// - True - the update was done with no errors
// - False - the update didn't complete successfully: either there is no update to be done
// or there was an error during the update
// NB - "Version" refers to the AssemblyFileVersion as configured in file AssemblyInfo.cs
private const string ToDeleteExtension = ".ToDelete";
private const string UpdateFileName = "Update.txt";
private const string ErrorMessageCheck = "There was a problem checking the update config file.";
private const string ErrorMessageUpdate = "There was a problem runing the Auto Update.";
private const string ErrorMessageDelete = "There was a problem deleting files.";
#region "CleanUp"
public static bool CleanUp()
{
try
{
string file;
DirectoryInfo dir = new DirectoryInfo(Application.StartupPath);
FileInfo[] infos = dir.GetFiles("*" + ToDeleteExtension, SearchOption.AllDirectories);
foreach (FileInfo info in infos)
{
file = info.FullName;
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
return true;
}
catch (Exception ex)
{
MessageBox.Show(ErrorMessageDelete + "\r" + ex.Message);
return false;
}
}
#endregion
#region "UpdateFiles"
public static bool UpdateFiles(string remotePath, bool DoUpdate)
{
if (remotePath == string.Empty)
return false;
if (DoUpdate)
{
// Delete files before updating them
CleanUp();
}
// execute the following line even for check runs
System.Collections.Generic.List<AutoUpdateRollback> rollBackList =
new System.Collections.Generic.List<AutoUpdateRollback>();
string remoteUri = remotePath;
WebClient myWebClient = new WebClient();
bool retValue = false;
try
{
// Get the remote file
string contents = myWebClient.DownloadString(remoteUri + UpdateFileName);
// Strip the "LF" from CR+LF and break it down by line
contents = contents.Replace("\n", "");
string[] fileList = contents.Split(Convert.ToChar("\r"));
// Parse the file list to strip comments
contents = string.Empty;
foreach (string file in fileList)
{
string fileAux;
if ((file.IndexOf("\'") + 1 != 0))
fileAux = file.Substring(0, ((file.IndexOf("\'") + 1) - 1));
else
fileAux = file;
if (fileAux.Trim() != string.Empty)
{
if (!string.IsNullOrEmpty(contents))
contents = contents + (char) (Keys.Return);
contents = contents + fileAux.Trim();
}
}
// Parse the file list again
fileList = contents.Split((char) (Keys.Return));
string[] info;
string infoFilePath;
String infoParam;
List<string> fileNameList = new List<string>();
Version Version1, Version2;
FileVersionInfo fv;
bool IsToDelete;
bool IsToUpgrade;
foreach (string file in fileList)
{
info = file.Split(Convert.ToChar(";"));
infoFilePath = info[0].Trim();
infoParam = info[1].Trim();
while (infoFilePath[0] == '.' || infoFilePath[0] == '\\')
{
infoFilePath = infoFilePath.Substring(1, infoFilePath.Length-1);
}
// ignore path names already on list (duplicates)
if (fileNameList.Contains(infoFilePath.ToLowerInvariant()))
{
continue;
}
// add path name to list
fileNameList.Add(infoFilePath.ToLowerInvariant());
IsToDelete = false;
IsToUpgrade = false;
string fileName = Application.StartupPath + @"\" + infoFilePath;
string tempFileName = Application.StartupPath + @"\" + infoFilePath + DateTime.Now.TimeOfDay.TotalMilliseconds;
bool FileExists = File.Exists(fileName);
if ((infoParam == "delete"))
{
IsToDelete = FileExists; // The second parameter is "delete"
if (DoUpdate)
if (IsToDelete)
rollBackList.Add(new AutoUpdateRollback(fileName, tempFileName + ToDeleteExtension, "Delete"));
}
else if ((infoParam == "?"))
{
// The second parameter is "?" (check if the file exists and it TRUE, do not update
IsToUpgrade = !FileExists;
}
else if (infoParam != string.Empty && (infoParam[0] == '=' && FileExists))
{
// The second parameter starts by "="
// Check the version of local and remote files
fv = FileVersionInfo.GetVersionInfo(fileName);
Version1 = new Version(infoParam.Substring(1, infoParam.Length - 1));
Version2 = new Version(fv.FileVersion);
IsToUpgrade = Version1 != Version2;
IsToDelete = IsToUpgrade;
if (DoUpdate)
if (IsToUpgrade)
rollBackList.Add(
new AutoUpdateRollback(fileName, tempFileName + ToDeleteExtension, "Upgrade"));
}
else if (FileExists)
{
// Check the version of local and remote files
fv = FileVersionInfo.GetVersionInfo(fileName);
// If 2nd parameter is empty, do nothing as file already exists
if (infoParam != string.Empty)
{
Version1 = new Version(infoParam);
Version2 = new Version(fv.FileVersion);
IsToUpgrade = Version1 > Version2;
IsToDelete = IsToUpgrade;
if (DoUpdate)
if (IsToUpgrade)
rollBackList.Add(
new AutoUpdateRollback(fileName, tempFileName + ToDeleteExtension, "Upgrade"));
}
}
else
{
IsToUpgrade = true;
}
if (DoUpdate)
{
if (IsToUpgrade)
myWebClient.DownloadFile(remoteUri + infoFilePath, tempFileName);
// Rename the file for future deletion
if (IsToDelete)
File.Move(fileName, tempFileName + ToDeleteExtension);
// Rename the temporary file name to the real file name
if (IsToUpgrade)
File.Move(tempFileName, fileName);
}
if (IsToUpgrade || IsToDelete)
retValue = true;
}
// This reruns the updated application
//Process.Start(Application.ExecutablePath);
// Don't use it here or you will end in an endless loop.
}
catch (Exception ex)
{
MessageBox.Show("There was an error. Trying to roll back");
if (DoUpdate)
{
foreach (AutoUpdateRollback rollBack in rollBackList)
{
if (rollBack.Operation == "Delete" || rollBack.Operation == "Upgrade")
{
if (File.Exists(rollBack.Renamed))
File.Move(rollBack.Renamed, rollBack.Original);
}
}
MessageBox.Show(ErrorMessageUpdate + "\r" + ex.Message + "\r" + "Remote URI: " + remoteUri);
}
else
MessageBox.Show(ErrorMessageCheck + "\r" + ex.Message + "\r" + "Remote URI: " + remoteUri);
retValue = false;
}
finally
{
myWebClient.Dispose();
// execute the following line even for check runs
rollBackList.Clear();
}
return retValue;
}
#endregion
}
public class AutoUpdateRollback
{
#region Properties
private string _renamed, _original, _operation;
public string Operation
{
get { return _operation; }
set { _operation = value; }
}
public string Original
{
get { return _original; }
set { _original = value; }
}
public string Renamed
{
get { return _renamed; }
set { _renamed = value; }
}
#endregion
#region Constructor
public AutoUpdateRollback(string Original, string Renamed, string Operation)
{
_original = Original;
_renamed = Renamed;
_operation = Operation;
}
#endregion
}
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Diagnostics;
using System.Windows.Forms;
/// <summary>
/// Class to update .exe and .dll through HTTP connection
/// </summary>
/// <author>Eduardo Oliveira</author>
/// <Modified_by>Danny (dbembibre@virtualdesk.es)</Modified_by>
/// <history Danny>
/// Changed: C# translated and retouched
/// Changed: Added method IsUpdatable() to verify if a new update exists
/// Changed: Now you can update any file without need of exe file
/// Changed: If something went wrong, you have a collection to rollback all files to the original state
///</history Danny>
/// <Modified>Tiago Freitas Leal, Feb. 2008 - v.2.1.1</Modified>
/// <history TFL>
/// Changed: General refactoring of names
/// Changed: Refactoring of properties to constants (preparing for resources)
/// Changed: Remote URI doesn't depend on assembly name
/// Changed: Ignore heading "." and "\" on file paths
/// Changed: Ignore second instance of same file path
/// Changed: Add "=ExactVersion" option
/// Changed: ".ToDelete" filenames are prefixed with the old filename (avoid duplicate names)
/// Changed: "return" happens always after "Dispose" operations
/// Changed: Merge "IsUpdatable" into "UpdateFiles"
/// Changed: Move delete operations to new public CleanUp() method
/// Changed: Files to be deleted are made NOT ReadOnly before deletion
/// Changed: Delete operates also on sub-directories
///</history TFL>
public class AutoUpdate
{
// <File Path>;<MinimumVersion> [' comments ]
// <File Path>;=<ExactVersion> [' comments ]
// <File Path>; [' comments ]
// <File Path>;? [' comments ]
// <File Path>;delete [' comments ]
// ...
// Blank lines and comments are ignored
// First parameter - file path (eg. Dir\file.dll)
// Second parameter:
// If the version is specified, the file is updated if:
// - it doesn't exist or
// - the actual version number is smaller than the update version number
// If the version is specified precedeed by a "=", the file is updated if:
// - it doesn't exist or
// - the actual version doesn't match the update version
// If it's an interrogation mark "?" the file is updated only if it doesn't exist
// If the second parameter is not specified, the file is updated only if it doesn't exist (just like "?")
// If it's "delete" the system tries to delete the file
// "'" (chr(39)) start a line (or part line) comment (like VB)
// Method return values
// - True - the update was done with no errors
// - False - the update didn't complete successfully: either there is no update to be done
// or there was an error during the update
// NB - "Version" refers to the AssemblyFileVersion as configured in file AssemblyInfo.cs
private const string ToDeleteExtension = ".ToDelete";
private const string UpdateFileName = "Update.txt";
private const string ErrorMessageCheck = "There was a problem checking the update config file.";
private const string ErrorMessageUpdate = "There was a problem runing the Auto Update.";
private const string ErrorMessageDelete = "There was a problem deleting files.";
#region "CleanUp"
public static bool CleanUp()
{
try
{
string file;
DirectoryInfo dir = new DirectoryInfo(Application.StartupPath);
FileInfo[] infos = dir.GetFiles("*" + ToDeleteExtension, SearchOption.AllDirectories);
foreach (FileInfo info in infos)
{
file = info.FullName;
File.SetAttributes(file, FileAttributes.Normal);
File.Delete(file);
}
return true;
}
catch (Exception ex)
{
MessageBox.Show(ErrorMessageDelete + "\r" + ex.Message);
return false;
}
}
#endregion
#region "UpdateFiles"
public static bool UpdateFiles(string remotePath, bool DoUpdate)
{
if (remotePath == string.Empty)
return false;
if (DoUpdate)
{
// Delete files before updating them
CleanUp();
}
// execute the following line even for check runs
System.Collections.Generic.List<AutoUpdateRollback> rollBackList =
new System.Collections.Generic.List<AutoUpdateRollback>();
string remoteUri = remotePath;
WebClient myWebClient = new WebClient();
bool retValue = false;
try
{
// Get the remote file
string contents = myWebClient.DownloadString(remoteUri + UpdateFileName);
// Strip the "LF" from CR+LF and break it down by line
contents = contents.Replace("\n", "");
string[] fileList = contents.Split(Convert.ToChar("\r"));
// Parse the file list to strip comments
contents = string.Empty;
foreach (string file in fileList)
{
string fileAux;
if ((file.IndexOf("\'") + 1 != 0))
fileAux = file.Substring(0, ((file.IndexOf("\'") + 1) - 1));
else
fileAux = file;
if (fileAux.Trim() != string.Empty)
{
if (!string.IsNullOrEmpty(contents))
contents = contents + (char) (Keys.Return);
contents = contents + fileAux.Trim();
}
}
// Parse the file list again
fileList = contents.Split((char) (Keys.Return));
string[] info;
string infoFilePath;
String infoParam;
List<string> fileNameList = new List<string>();
Version Version1, Version2;
FileVersionInfo fv;
bool IsToDelete;
bool IsToUpgrade;
foreach (string file in fileList)
{
info = file.Split(Convert.ToChar(";"));
infoFilePath = info[0].Trim();
infoParam = info[1].Trim();
while (infoFilePath[0] == '.' || infoFilePath[0] == '\\')
{
infoFilePath = infoFilePath.Substring(1, infoFilePath.Length-1);
}
// ignore path names already on list (duplicates)
if (fileNameList.Contains(infoFilePath.ToLowerInvariant()))
{
continue;
}
// add path name to list
fileNameList.Add(infoFilePath.ToLowerInvariant());
IsToDelete = false;
IsToUpgrade = false;
string fileName = Application.StartupPath + @"\" + infoFilePath;
string tempFileName = Application.StartupPath + @"\" + infoFilePath + DateTime.Now.TimeOfDay.TotalMilliseconds;
bool FileExists = File.Exists(fileName);
if ((infoParam == "delete"))
{
IsToDelete = FileExists; // The second parameter is "delete"
if (DoUpdate)
if (IsToDelete)
rollBackList.Add(new AutoUpdateRollback(fileName, tempFileName + ToDeleteExtension, "Delete"));
}
else if ((infoParam == "?"))
{
// The second parameter is "?" (check if the file exists and it TRUE, do not update
IsToUpgrade = !FileExists;
}
else if (infoParam != string.Empty && (infoParam[0] == '=' && FileExists))
{
// The second parameter starts by "="
// Check the version of local and remote files
fv = FileVersionInfo.GetVersionInfo(fileName);
Version1 = new Version(infoParam.Substring(1, infoParam.Length - 1));
Version2 = new Version(fv.FileVersion);
IsToUpgrade = Version1 != Version2;
IsToDelete = IsToUpgrade;
if (DoUpdate)
if (IsToUpgrade)
rollBackList.Add(
new AutoUpdateRollback(fileName, tempFileName + ToDeleteExtension, "Upgrade"));
}
else if (FileExists)
{
// Check the version of local and remote files
fv = FileVersionInfo.GetVersionInfo(fileName);
// If 2nd parameter is empty, do nothing as file already exists
if (infoParam != string.Empty)
{
Version1 = new Version(infoParam);
Version2 = new Version(fv.FileVersion);
IsToUpgrade = Version1 > Version2;
IsToDelete = IsToUpgrade;
if (DoUpdate)
if (IsToUpgrade)
rollBackList.Add(
new AutoUpdateRollback(fileName, tempFileName + ToDeleteExtension, "Upgrade"));
}
}
else
{
IsToUpgrade = true;
}
if (DoUpdate)
{
if (IsToUpgrade)
myWebClient.DownloadFile(remoteUri + infoFilePath, tempFileName);
// Rename the file for future deletion
if (IsToDelete)
File.Move(fileName, tempFileName + ToDeleteExtension);
// Rename the temporary file name to the real file name
if (IsToUpgrade)
File.Move(tempFileName, fileName);
}
if (IsToUpgrade || IsToDelete)
retValue = true;
}
// This reruns the updated application
//Process.Start(Application.ExecutablePath);
// Don't use it here or you will end in an endless loop.
}
catch (Exception ex)
{
MessageBox.Show("There was an error. Trying to roll back");
if (DoUpdate)
{
foreach (AutoUpdateRollback rollBack in rollBackList)
{
if (rollBack.Operation == "Delete" || rollBack.Operation == "Upgrade")
{
if (File.Exists(rollBack.Renamed))
File.Move(rollBack.Renamed, rollBack.Original);
}
}
MessageBox.Show(ErrorMessageUpdate + "\r" + ex.Message + "\r" + "Remote URI: " + remoteUri);
}
else
MessageBox.Show(ErrorMessageCheck + "\r" + ex.Message + "\r" + "Remote URI: " + remoteUri);
retValue = false;
}
finally
{
myWebClient.Dispose();
// execute the following line even for check runs
rollBackList.Clear();
}
return retValue;
}
#endregion
}
public class AutoUpdateRollback
{
#region Properties
private string _renamed, _original, _operation;
public string Operation
{
get { return _operation; }
set { _operation = value; }
}
public string Original
{
get { return _original; }
set { _original = value; }
}
public string Renamed
{
get { return _renamed; }
set { _renamed = value; }
}
#endregion
#region Constructor
public AutoUpdateRollback(string Original, string Renamed, string Operation)
{
_original = Original;
_renamed = Renamed;
_operation = Operation;
}
#endregion
}
2. Add a new project of type Windows Application named "AutoUpdateTest"
2.1. Add a reference to the AutoUpdate project
2.2. Rename Form1.cs to Startup.cs
2.3. Click View Code and paste (replace whatever is in there) the following
[-] Collapse
using System;
using System.Diagnostics;
using System.Windows.Forms;
namespace AutoUpdateTest
{
public partial class Startup : Form
{
public Startup()
{
InitializeComponent();
}
private void Startup_Load(object sender, EventArgs e)
{
Show();
//Change to reflect your RemotePath
string RemotePath = "http://localhost/AutoUpdateTest/";
label1.Text = RemotePath;
Form helper = new Updated.ShowVersionForm();
helper.Show();
MessageBox.Show("Checking if update is needed...");
if (AutoUpdate.UpdateFiles(RemotePath, false))
{
MessageBox.Show("Update is needed.");
if (AutoUpdate.UpdateFiles(RemotePath, true))
{
MessageBox.Show("Auto Update succedeed!");
Dispose();
Process.Start(Application.ExecutablePath);
Application.Exit();
}
}
else
{
MessageBox.Show("No update is available.");
AutoUpdate.CleanUp();
}
}
private void button1_Click(object sender, EventArgs e)
{
Close();
}
}
}
using System.Diagnostics;
using System.Windows.Forms;
namespace AutoUpdateTest
{
public partial class Startup : Form
{
public Startup()
{
InitializeComponent();
}
private void Startup_Load(object sender, EventArgs e)
{
Show();
//Change to reflect your RemotePath
string RemotePath = "http://localhost/AutoUpdateTest/";
label1.Text = RemotePath;
Form helper = new Updated.ShowVersionForm();
helper.Show();
MessageBox.Show("Checking if update is needed...");
if (AutoUpdate.UpdateFiles(RemotePath, false))
{
MessageBox.Show("Update is needed.");
if (AutoUpdate.UpdateFiles(RemotePath, true))
{
MessageBox.Show("Auto Update succedeed!");
Dispose();
Process.Start(Application.ExecutablePath);
Application.Exit();
}
}
else
{
MessageBox.Show("No update is available.");
AutoUpdate.CleanUp();
}
}
private void button1_Click(object sender, EventArgs e)
{
Close();
}
}
}
3. On IIS create a virtual directory named "AutoUpdateTest"
3.1. Put your sample files in there.
3.2. Put there an Update.txt like this one
[-] Collapse
\Updated2.exe ; 'G't Updated2.exe if it doesn't exist (ignore "\")
.\Updated3.exe ; ? 'G't Updated3.exe if it doesn't exist (ignore ".\")
.\Updated3.exe ; delete 'T'is line is ignored as it's the second instance of the same file path
..\Updated4.exe ; =1.0.0.3 'I' actual Updated4.exe isn't this version, get it (ignore "..\")
Updated5.exe ; delete 'D'lete Updated5.exe
Updated.exe ; 1.0.0.3 'I' actual Updated.exe version is smaller than 1.0.0.3, get it
'U'date2.exe ; delete 'i'nore this line
't'e lines below just test the same features on sub-directories
TST\TSTUpdated.dll ; 1.0.0.1
TST\SubSub\SUBUpdated.dll ; 1.0.0.1
\TST\SubSub\Slash.dll ; 1.0.0.1
.\TST\SubSub\DotSlash.dll ; 1.0.0.1
.\TST\SubSub\Trash.dll ; delete
4. Build and execute AutoUpdateTest.exe
'Coding > C#, .Net Framework' 카테고리의 다른 글
[펌] 윈도우 서비스 (Windows Service) 디버깅하기 (0) | 2009.08.09 |
---|---|
.Net Framework Version 별 확인 (0) | 2009.08.09 |
.NET Framework 3.0 환경 구축 (0) | 2009.08.09 |