如何寫一隻自己能夠刪除自己或是更新自己的程式
目前市面上有不同的軟體打包工具,有些甚至提供了完整的更新服務機制框架,讓使用檢查更新後自動執行軟體更新,不過很多時候我們只是想寫出簡單的綠色可攜軟體,當然不可能選擇安裝打包的做法,更不可能利用到龐大的更新服務框架,最理想的是綠色軟體本身即附帶自動更新機制,但很少看過有這麼做的軟體,因為就是小軟體,作法通常也是很簡單,通常都是讓使用者自己上網下載新版本,覆蓋掉就好了.....但人就是懶,我常常在想如果有什麼機制一鍵更新自己,那最方便了,但這麼做其實有一個問題需要處理,執行中的執行檔,理論上不可能自己刪除自己或是更名搬移(一般常識下),那麼如果我要自己更新自己,當我下載了新的執行檔,我要怎麼在執行的過程中自己替換掉自己???這豈不矛盾?
網路上的討論多數是提出借由第二隻process來進行這種更新任務,這麼設計就簡單多了不是嗎? 但為了要更新,我的綠色可攜軟體需要多出一隻執行檔(當然也是有做法可以把另一隻執行檔偷塞到資源檔內,執行時再解出到隱蔽的暫存目錄區藏起來,但做法就是不太乾脆....)
後來找了一些資料,設計出一個最簡單的方式,雖然不是很確定網路上是否有跟我相似的做法,但就提出一個方法供參考.
我的方式也很簡單,其實就是靠win32的api 打破 執行過程中的執行檔無法被刪除.搬移.更名的常識,反正步驟大概如下
1.自己透過win32 api MoveFileEx 把自身執行檔案搬移到別處.
2.這時候程式還在執行喔!!! 然後繼續下載網路上的更新檔替換到自己原來的位置.
3.關閉程式前透過 cmd.exe 搭配 Process , 建立出二個延遲執行的系統命令 , 一個刪除搬移到別處的執行檔 , 一個啟動新的執行檔.
4.關閉自己
5.前面兩個系統命令延遲時間到,執行命令動作,清除垃圾舊檔,啟動新版本軟體.
ok!
參考兩個連結
https://www.codeproject.com/Articles/31454/How-To-Make-Your-Application-Delete-Itself-Immedia
http://stackoverflow.com/questions/1606140/how-can-a-program-delete-its-own-executable
下面是sample code , 執行範例如最上面的影片
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Net;
//https://www.codeproject.com/Articles/31454/How-To-Make-Your-Application-Delete-Itself-Immedia
//http://stackoverflow.com/questions/1606140/how-can-a-program-delete-its-own-executable
namespace test
{
public partial class Form1 : Form
{
string sources_file = "";
string tmp_file = "";
public Form1()
{
InitializeComponent();
sources_file = Application.ExecutablePath;
tmp_file = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\23123_update_tmp";
}
internal enum MoveFileFlags
{
MOVEFILE_REPLACE_EXISTING = 1,
MOVEFILE_COPY_ALLOWED = 2,
MOVEFILE_DELAY_UNTIL_REBOOT = 4,
MOVEFILE_WRITE_THROUGH = 8
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
private void button1_Click(object sender, EventArgs e)
{
//將原始執行檔搬移,很奇妙的系統API.....可以在執行途中改變執行檔名稱與路徑
MoveFileEx(sources_file, tmp_file, MoveFileFlags.MOVEFILE_REPLACE_EXISTING);
//下載新版執行檔,到原始執行檔位置,或是也可以先搬別處之後再搬回
//更正式的做法會加上與server端的程式版本新就判斷才更新
WebClient wc = new WebClient();
wc.DownloadFile("https://dl.dropboxusercontent.com/u/61164954/update/test.exe", sources_file);
//清除垃圾舊檔
Process P_sources = new Process();
//設定一秒延遲,讓程式順利關閉才能刪除
P_sources.StartInfo = new ProcessStartInfo("cmd.exe", "/C choice /C Y /N /D Y /T 1 & Del " + "\"" + tmp_file + "\"");
P_sources.StartInfo.CreateNoWindow = true;
P_sources.StartInfo.UseShellExecute = false;
P_sources.Start();
//執行新版程式自啟
Process P_new = new Process();
//設定一秒延遲,讓程式順利關閉才能刪除
P_new.StartInfo = new ProcessStartInfo("cmd.exe", "/C choice /C Y /N /D Y /T 1 & " + "\"" + sources_file + "\"");
P_new.StartInfo.CreateNoWindow = true;
P_new.StartInfo.UseShellExecute = false;
P_new.Start();
//關閉程式
Close();
}
}
}