最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

c# 使用handle.exe解決程序更新文件被占用的問題

 更新時間:2021年03月10日 08:30:42   作者:夢在旅途  
這篇文章主要介紹了c# 使用handle.exe解決程序更新文件被占用的問題,幫助大家更好的理解和學習使用c#,感興趣的朋友可以了解下

我公司最近升級程序經(jīng)常報出更新失敗問題,究其原因,原來是更新時,他們可能又打開了正在被更新的文件,導致更新文件時,文件被其它進程占用,無法正常更新而報錯,為了解決這個問題,我花了一周時間查詢多方資料及研究,終于找到了一個查詢進程的利器:handle.exe,下載地址:https://technet.microsoft.com/en-us/sysinternals/bb896655.aspx,我是通過它來找到被占用的進程,然后KILL掉占用進程,最后再來更新,這樣就完美的解決了更新時文件被占用報錯的問題了,實現(xiàn)方法很簡單,我下面都有列出主要的方法,一些注意事項我也都有說明,大家一看就明白了,當然如果大家有更好的方案,歡迎交流,謝謝!

IsFileUsing:

判斷文件是否被占用

[DllImport("kernel32.dll")]
public static extern IntPtr _lopen(string lpPathName, int iReadWrite);
 
[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr hObject);
 
public const int OF_READWRITE = 2;
public const int OF_SHARE_DENY_NONE = 0x40;
public readonly IntPtr HFILE_ERROR = new IntPtr(-1);
private bool <strong>IsFileUsing</strong>(string filePath)
{
    if (!File.Exists(filePath))
    {
        return false;
    }
    IntPtr vHandle = _lopen(filePath, OF_READWRITE | OF_SHARE_DENY_NONE);
    if (vHandle == HFILE_ERROR)
    {
        return true;
    }
    CloseHandle(vHandle);
    return false;
}

GetRunProcessInfos:

獲取指定文件或目錄中存在的(關聯(lián)的)運行進程信息,以便后面可以解除占用

/// <summary>
/// 獲取指定文件或目錄中存在的(關聯(lián)的)運行進程信息,以便后面可以解除占用
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
private Dictionary<int, string> GetRunProcessInfos(string filePath)
{
 
    Dictionary<int, string> runProcInfos = new Dictionary<int, string>();
    string fileName = Path.GetFileName(filePath);
    var fileRunProcs = Process.GetProcessesByName(fileName);
    if (fileRunProcs != null && fileRunProcs.Count() > 0)
    {
        runProcInfos = fileRunProcs.ToDictionary(p => p.Id, p => p.ProcessName);
        return runProcInfos;
    }
 
    string fileDirName = Path.GetDirectoryName(filePath); //查詢指定路徑下的運行的進程
    Process startProcess = new Process();
    startProcess.StartInfo.FileName = RelaseAndGetHandleExePath();
    startProcess.StartInfo.Arguments = string.Format("\"{0}\"", fileDirName);
    startProcess.StartInfo.UseShellExecute = false;
    startProcess.StartInfo.RedirectStandardInput = false;
    startProcess.StartInfo.RedirectStandardOutput = true;
    startProcess.StartInfo.CreateNoWindow = true;
    startProcess.StartInfo.StandardOutputEncoding = ASCIIEncoding.UTF8;
    startProcess.OutputDataReceived += (sender, e) =>
    {
        if (!string.IsNullOrEmpty(e.Data) && e.Data.IndexOf("pid:", StringComparison.OrdinalIgnoreCase) > 0)
        {
            //var regex = new System.Text.RegularExpressions.Regex(@"(^[\w\.\?\u4E00-\u9FA5]+)\s+pid:\s*(\d+)", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            var regex = new System.Text.RegularExpressions.Regex(@"(^.+(?=pid:))\bpid:\s+(\d+)\s+", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
            if (regex.IsMatch(e.Data))
            {
                var mathedResult = regex.Match(e.Data);
 
                int procId = int.Parse(mathedResult.Groups[2].Value);
                string procFileName = mathedResult.Groups[1].Value.Trim();
 
                if ("explorer.exe".Equals(procFileName, StringComparison.OrdinalIgnoreCase))
                {
                    return;
                }
 
                //var regex2 = new System.Text.RegularExpressions.Regex(string.Format(@"\b{0}.*$", fileDirName.Replace(@"\", @"\\").Replace("?",@"\?")), System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                var regex2 = new System.Text.RegularExpressions.Regex(@"\b\w{1}:.+$", System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                string procFilePath = (regex2.Match(e.Data).Value ?? "").Trim();
 
                if (filePath.Equals(procFilePath, StringComparison.OrdinalIgnoreCase) || filePath.Equals(PathJoin(procFilePath, procFileName), StringComparison.OrdinalIgnoreCase))
                {
                    runProcInfos[procId] = procFileName;
                }
                else //如果亂碼,則進行特殊的比對
                {
                    if (procFilePath.Contains("?") || procFileName.Contains("?")) //?亂碼比對邏輯
                    {
                        var regex3 = new System.Text.RegularExpressions.Regex(procFilePath.Replace(@"\", @"\\").Replace(".", @"\.").Replace("?", ".{1}"), System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                        if (regex3.IsMatch(filePath))
                        {
                            runProcInfos[procId] = procFileName;
                        }
                        else
                        {
                            string tempProcFilePath = PathJoin(procFilePath, procFileName);
 
                            regex3 = new System.Text.RegularExpressions.Regex(tempProcFilePath.Replace(@"\", @"\\").Replace(".", @"\.").Replace("?", ".{1}"), System.Text.RegularExpressions.RegexOptions.IgnoreCase);
                            if (regex3.IsMatch(filePath))
                            {
                                runProcInfos[procId] = procFileName;
                            }
                        }
                    }
                    else if (procFilePath.Length == filePath.Length || PathJoin(procFilePath, procFileName).Length == filePath.Length) //其它亂碼比對邏輯,僅比對長度,如果相同交由用戶判斷
                    {
                        if (MessageBox.Show(string.Format("發(fā)現(xiàn)文件:{0}可能被一個進程({1})占用,\n您是否需要強制終止該進程?", filePath, procFileName), "發(fā)現(xiàn)疑似被占用進程", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
                        {
                            runProcInfos[procId] = procFileName;
                        }
                    }
                }
            }
        }
    };
 
    startProcess.Start();
    startProcess.BeginOutputReadLine();
    startProcess.WaitForExit();
 
    return runProcInfos;
}

上述代碼邏輯簡要說明:創(chuàng)建一個建程來啟動handle.exe(以資源形式內(nèi)嵌到項目中),然后異步接收返回數(shù)據(jù),并通過正則表達式來匹配獲取進程數(shù)據(jù),由于handle.exe對于中文路徑或文件名兼容不好,返回的數(shù)據(jù)存在?或其它亂碼字符,故我作了一些特殊的模糊匹配邏輯;

RelaseAndGetHandleExePath:

從項目中釋放handle.exe并保存到系統(tǒng)的APPData目錄下,以便后續(xù)直接可以使用(注意:由于handle.exe需要授權(quán)同意后才能正常的使用該工具,故我在第一次生成handle.exe時,會直接運行進程,讓用戶選擇Agree后再去進行后面的邏輯處理,這樣雖能解決問題,但有點不太友好,目前一個是中文亂碼、一個是必需同意才能使用handle.exe我認為如果微軟解決了可能會更好)

private string RelaseAndGetHandleExePath()
{
    var handleInfo = new FileInfo(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\SysUpdate\\handle.exe");
    if (!File.Exists(handleInfo.FullName))
    {
        if (!Directory.Exists(handleInfo.DirectoryName))
        {
            Directory.CreateDirectory(handleInfo.DirectoryName);
        }
 
        byte[] handleExeData = Properties.Resources.handle;
        File.WriteAllBytes(handleInfo.FullName, handleExeData);
 
        var handleProc = Process.Start(handleInfo.FullName);//若第一次,則彈出提示框,需要點擊agree同意才行
        handleProc.WaitForExit();
    }
 
    return handleInfo.FullName;
}

PathJoin:

拼接路徑(不過濾特殊字符),由于handle.exe對于中文路徑或文件名兼容不好,返回的數(shù)據(jù)存在?或其它亂碼字符,如查采用:Path.Combine方法則會報錯,故這里自定義一個方法,只是簡單的拼接

/// <summary>
/// 拼接路徑(不過濾殊字符)
/// </summary>
/// <param name="paths"></param>
/// <returns></returns>
private string PathJoin(params string[] paths)
{
    if (paths == null || paths.Length <= 0)
    {
        return string.Empty;
    }
 
    string newPath = paths[0];
 
    for (int i = 1; i < paths.Length; i++)
    {
        if (!newPath.EndsWith("\\"))
        {
            newPath += "\\";
        }
 
        if (paths[i].StartsWith("\\"))
        {
            paths[i] = paths[i].Substring(1);
        }
 
        newPath += paths[i];
    }
 
    return newPath;
}

CloseProcessWithFile:

核心方法,關閉指定文件被占用的進程,上述所有的方法均是為了實現(xiàn)該方法的功能

private void CloseProcessWithFile(string filePath)
{
    if (!IsFileUsing(filePath)) return;
 
    ShowDownInfo(string.Format("正在嘗試解除占用文件 {0}", _FilePaths[_FileIndex]));
 
    var runProcInfos = GetRunProcessInfos(filePath); //獲取被占用的進程
 
 
    System.IO.File.WriteAllText(Path.Combine(Application.StartupPath, "runProcInfos.txt"), string.Join("\r\n", runProcInfos.Select(p => string.Format("ProdId:{0},ProcName:{1}", p.Key, p.Value)).ToArray()));//DEBUG用,正式發(fā)布時可以去掉
 
    var localProcesses = Process.GetProcesses();
    bool hasKilled = false;
    foreach (var item in runProcInfos)
    {
        if (item.Key != currentProcessId) //排除當前進程
        {
            var runProcess = localProcesses.SingleOrDefault(p => p.Id == item.Key);
            //var runProcess = Process.GetProcessById(item.Key);
            if (runProcess != null)
            {
                try
                {
                    runProcess.Kill(); //強制關閉被占用的進程
                    hasKilled = true;
                }
                catch
                { }
            }
        }
    }
 
    if (hasKilled)
    {
        Thread.Sleep(500);
    }
}

上述代碼邏輯簡要說明:先判斷是否被占用,若被占用,則獲取該文件被占用的進程列表,然后獲取一下當前操作系統(tǒng)的所有進程列表,最后通過進程ID查詢得到排除當前程序自己的進程ID(currentProcessId = Process.GetCurrentProcess().Id)列表,若能獲取得到,表明進程仍在運行,則強制終止該進程,實現(xiàn)解除文件占用

注意:KILL掉占用進程后,可能由于緩存原因,若直接進行文件的覆蓋與替換或轉(zhuǎn)移操作,可能仍會報錯,故這里作了一個判斷,若有成功KILL掉進程,則需等待500MS再去做更新文件之類的操作;

以上就是c# 使用handle.exe解決程序更新文件被占用的問題的詳細內(nèi)容,更多關于c# 使用handle.exe解決程序更新問題的資料請關注腳本之家其它相關文章!

相關文章

  • c#預處理指令分析

    c#預處理指令分析

    這篇文章主要介紹了c#預處理指令的相關資料,幫助大家更好的理解和學習c#,感興趣的朋友可以了解下
    2020-08-08
  • c#中SAPI使用總結(jié)——SpVoice的使用方法

    c#中SAPI使用總結(jié)——SpVoice的使用方法

    最近使用C#重做了點名系統(tǒng)(要用到TTS,讓計算機點名)使用了SAPI,在這里總結(jié)一下SpVoice的使用方法。
    2011-10-10
  • C#泛型實例詳解

    C#泛型實例詳解

    這篇文章主要介紹了C#泛型,對于C#初學者來說是有必要牢固掌握的內(nèi)容,需要的朋友可以參考下
    2014-08-08
  • C#實現(xiàn)農(nóng)歷日歷的方法

    C#實現(xiàn)農(nóng)歷日歷的方法

    這篇文章主要介紹了C#實現(xiàn)農(nóng)歷日歷的方法,詳細分析了使用C#實現(xiàn)農(nóng)歷日歷的完整步驟,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-01-01
  • C# 如何使用ajax請求

    C# 如何使用ajax請求

    這篇文章主要介紹了C# 如何使用ajax請求,文中講解非常細致,代碼幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-07-07
  • C#實現(xiàn)六大設計原則之依賴倒置原則

    C#實現(xiàn)六大設計原則之依賴倒置原則

    這篇文章介紹了C#實現(xiàn)六大設計原則之依賴倒置原則的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-02-02
  • C#如何將List<string>轉(zhuǎn)換為List<double>

    C#如何將List<string>轉(zhuǎn)換為List<double>

    這篇文章主要介紹了C#如何將List<string>轉(zhuǎn)換為List<double>問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • C#中的Task.WhenAll和Task.WhenAny方法介紹

    C#中的Task.WhenAll和Task.WhenAny方法介紹

    這篇文章介紹了C#中的Task.WhenAll和Task.WhenAny方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • 通過C#實現(xiàn)在Word中插入或刪除分節(jié)符

    通過C#實現(xiàn)在Word中插入或刪除分節(jié)符

    在Word中,分節(jié)符是一種強大的工具,用于將文檔分成不同的部分,每個部分可以有獨立的頁面設置,如頁邊距、紙張方向、頁眉和頁腳等,本文將介紹如何使用一個免費的.NET庫通過C#實現(xiàn)插入或刪除Word分節(jié)符,需要的朋友可以參考下
    2024-08-08
  • C#獲取圖片文件擴展名的方法

    C#獲取圖片文件擴展名的方法

    這篇文章主要介紹了C#獲取圖片文件擴展名的方法,實例總結(jié)了較為常見的獲取圖片文件擴展名的技巧,非常具有實用價值,需要的朋友可以參考下
    2014-10-10

最新評論

运城市| 天柱县| 白玉县| 潍坊市| 仁怀市| 华池县| 临邑县| 长阳| 宁蒗| 高邮市| 滕州市| 嘉定区| 昌江| 贵州省| 昭平县| 绥芬河市| 尚义县| 遂宁市| 宽城| 特克斯县| 阿巴嘎旗| 黄龙县| 铁力市| 景东| 无极县| 白银市| 绿春县| 修文县| 天津市| 青神县| 岗巴县| 阳朔县| 荣成市| 县级市| 腾冲县| 盱眙县| 黎川县| 浠水县| 徐汇区| 泰州市| 方山县|