[轉貼] 如何寫一支自己能夠刪除自己或是更新自己的程式

2020041310:36
出處: https://dotblogs.com.tw/enet/2017/02/24/update_exe_itself
 

如何寫一支自己能夠刪除自己或是更新自己的程式

目前市面上有不同的軟體打包工具,有些甚至提供了完整的更新服務機制框架,讓使用檢查更新後自動執行軟體更新,不過很多時候我們只是想寫出簡單的綠色可攜軟體,當然不可能選擇安裝打包的做法,更不可能利用到龐大的更新服務框架,最理想的是綠色軟體本身即附帶自動更新機制,但很少看過有這麼做的軟體,因為就是小軟體,作法通常也是很簡單,通常都是讓使用者自己上網下載新版本,覆蓋掉就好了.....但人就是懶,我常常在想如果有什麼機制一鍵更新自己,那最方便了,但這麼做其實有一個問題需要處理,執行中的執行檔,理論上不可能自己刪除自己或是更名搬移(一般常識下),那麼如果我要自己更新自己,當我下載了新的執行檔,我要怎麼在執行的過程中自己替換掉自己???這豈不矛盾?

網路上的討論多數是提出借由第二隻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();
        }
    }
}