c#+handle.exe实现升级程序在运行时自动解除文件被占用的问题
我公司最近升級(jí)程序經(jīng)常報(bào)出更新失敗問題,究其原因,原來是更新時(shí),他們可能又打開了正在被更新的文件,導(dǎo)致更新文件時(shí),文件被其它進(jìn)程占用,無法正常更新而報(bào)錯(cuò),為了解決這個(gè)問題,我花了一周時(shí)間查詢多方資料及研究,終于找到了一個(gè)查詢進(jìn)程的利器:handle.exe,下載地址:https://technet.microsoft.com/en-us/sysinternals/bb896655.aspx,我是通過它來找到被占用的進(jìn)程,然后KILL掉占用進(jìn)程,最后再來更新,這樣就完美的解決了更新時(shí)文件被占用報(bào)錯(cuò)的問題了,實(shí)現(xiàn)方法很簡單,我下面都有列出主要的方法,一些注意事項(xiàng)我也都有說明,大家一看就明白了,當(dāng)然如果大家有更好的方案,歡迎交流,謝謝!
IsFileUsing:判斷文件是否被占用
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | [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:獲取指定文件或目錄中存在的(關(guān)聯(lián)的)運(yùn)行進(jìn)程信息,以便后面可以解除占用
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | /// <summary> /// 獲取指定文件或目錄中存在的(關(guān)聯(lián)的)運(yùn)行進(jì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);?//查詢指定路徑下的運(yùn)行的進(jìn)程 ????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?//如果亂碼,則進(jìn)行特殊的比對(duì) ????????????????{ ????????????????????if?(procFilePath.Contains("?") || procFileName.Contains("?"))?//?亂碼比對(duì)邏輯 ????????????????????{ ????????????????????????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)?//其它亂碼比對(duì)邏輯,僅比對(duì)長度,如果相同交由用戶判斷 ????????????????????{ ????????????????????????if?(MessageBox.Show(string.Format("發(fā)現(xiàn)文件:{0}可能被一個(gè)進(jìn)程({1})占用,\n您是否需要強(qiáng)制終止該進(jìn)程?", filePath, procFileName),?"發(fā)現(xiàn)疑似被占用進(jìn)程", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) ????????????????????????{ ????????????????????????????runProcInfos[procId] = procFileName; ????????????????????????} ????????????????????} ????????????????} ????????????} ????????} ????}; ????startProcess.Start(); ????startProcess.BeginOutputReadLine(); ????startProcess.WaitForExit(); ????return?runProcInfos; } |
上述代碼邏輯簡要說明:創(chuàng)建一個(gè)建程來啟動(dòng)handle.exe(以資源形式內(nèi)嵌到項(xiàng)目中),然后異步接收返回?cái)?shù)據(jù),并通過正則表達(dá)式來匹配獲取進(jìn)程數(shù)據(jù),由于handle.exe對(duì)于中文路徑或文件名兼容不好,返回的數(shù)據(jù)存在?或其它亂碼字符,故我作了一些特殊的模糊匹配邏輯;
RelaseAndGetHandleExePath:從項(xiàng)目中釋放handle.exe并保存到系統(tǒng)的APPData目錄下,以便后續(xù)直接可以使用(注意:由于handle.exe需要授權(quán)同意后才能正常的使用該工具,故我在第一次生成handle.exe時(shí),會(huì)直接運(yùn)行進(jìn)程,讓用戶選擇Agree后再去進(jìn)行后面的邏輯處理,這樣雖能解決問題,但有點(diǎn)不太友好,目前一個(gè)是中文亂碼、一個(gè)是必需同意才能使用handle.exe我認(rèn)為如果微軟解決了可能會(huì)更好)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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);//若第一次,則彈出提示框,需要點(diǎn)擊agree同意才行 ????????handleProc.WaitForExit(); ????} ????return?handleInfo.FullName; } |
PathJoin:拼接路徑(不過濾特殊字符),由于handle.exe對(duì)于中文路徑或文件名兼容不好,返回的數(shù)據(jù)存在?或其它亂碼字符,如查采用:Path.Combine方法則會(huì)報(bào)錯(cuò),故這里自定義一個(gè)方法,只是簡單的拼接
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | /// <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:核心方法,關(guān)閉指定文件被占用的進(jìn)程,上述所有的方法均是為了實(shí)現(xiàn)該方法的功能
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | private?void?CloseProcessWithFile(string?filePath) { ????if?(!IsFileUsing(filePath))?return; ????ShowDownInfo(string.Format("正在嘗試解除占用文件 {0}", _FilePaths[_FileIndex])); ????var?runProcInfos = GetRunProcessInfos(filePath);?//獲取被占用的進(jìn)程 ????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ā)布時(shí)可以去掉 ????var?localProcesses = Process.GetProcesses(); ????bool?hasKilled =?false; ????foreach?(var?item?in?runProcInfos) ????{ ????????if?(item.Key != currentProcessId)?//排除當(dāng)前進(jìn)程 ????????{ ????????????var?runProcess = localProcesses.SingleOrDefault(p => p.Id == item.Key); ????????????//var runProcess = Process.GetProcessById(item.Key); ????????????if?(runProcess !=?null) ????????????{ ????????????????try ????????????????{ ????????????????????runProcess.Kill();?//強(qiáng)制關(guān)閉被占用的進(jìn)程 ????????????????????hasKilled =?true; ????????????????} ????????????????catch ????????????????{ } ????????????} ????????} ????} ????if?(hasKilled) ????{ ????????Thread.Sleep(500); ????} } |
上述代碼邏輯簡要說明:先判斷是否被占用,若被占用,則獲取該文件被占用的進(jìn)程列表,然后獲取一下當(dāng)前操作系統(tǒng)的所有進(jìn)程列表,最后通過進(jìn)程ID查詢得到排除當(dāng)前程序自己的進(jìn)程ID(currentProcessId = Process.GetCurrentProcess().Id)列表,若能獲取得到,表明進(jìn)程仍在運(yùn)行,則強(qiáng)制終止該進(jìn)程,實(shí)現(xiàn)解除文件占用
注意:KILL掉占用進(jìn)程后,可能由于緩存原因,若直接進(jìn)行文件的覆蓋與替換或轉(zhuǎn)移操作,可能仍會(huì)報(bào)錯(cuò),故這里作了一個(gè)判斷,若有成功KILL掉進(jìn)程,則需等待500MS再去做更新文件之類的操作;
本文轉(zhuǎn)自 夢(mèng)在旅途 博客園博客,原文鏈接:http://www.cnblogs.com/zuowj/p/5840567.html??,如需轉(zhuǎn)載請(qǐng)自行聯(lián)系原作者
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的c#+handle.exe实现升级程序在运行时自动解除文件被占用的问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 老人梦到结婚是什么预兆
- 下一篇: 梦到白无常是什么意思