2008-08-04 28 views
50

Tôi đang tìm cách xóa tệp bị khóa bởi một quy trình khác bằng C#. Tôi nghi ngờ phương pháp này phải có khả năng tìm thấy quy trình nào đang khóa tệp (có thể bằng cách theo dõi các xử lý, mặc dù tôi không chắc chắn cách thực hiện điều này trong C#) rồi đóng quá trình đó trước khi có thể hoàn thành việc xóa tệp bằng cách sử dụng File.Delete().Làm cách nào để xóa tệp bị khóa bởi một quy trình khác trong C#?

+0

liên quan - http://stackoverflow.com/questions/1760481/closing-open-files-using -c-sharp/41902677 # 41902677 – vapcguy

Trả lời

34

Killing quá trình khác không phải là một dấu hiệu tốt để làm. Nếu kịch bản của bạn liên quan đến việc gỡ cài đặt, bạn có thể sử dụng MoveFileEx API function để đánh dấu tệp để xóa khi khởi động lại lần tiếp theo.

Nếu có vẻ như bạn thực sự cần xóa tệp đang được sử dụng bởi một quy trình khác, tôi khuyên bạn nên xem xét lại vấn đề thực tế trước khi xem xét bất kỳ giải pháp nào.

+6

Xin lưu ý nhận xét MSDN này: 'MOVEFILE_DELAY_UNTIL_REBOOT' -" ... Giá trị này chỉ có thể được sử dụng nếu quá trình nằm trong ngữ cảnh của người dùng thuộc nhóm quản trị viên hoặc Tài khoản LocalSystem ... " –

4

Bạn có thể sử dụng chương trình này, Handle để tìm quy trình nào có khóa trên tệp của bạn. Đó là một công cụ dòng lệnh, vì vậy tôi đoán bạn sử dụng đầu ra từ đó ... Tôi không chắc chắn về việc tìm kiếm nó theo chương trình.

Nếu xóa các tập tin có thể chờ đợi, bạn có thể xác định nó để xóa khi máy tính của bạn bên cạnh khởi động:

  1. Bắt đầu REGEDT32 (W2K) hoặc REGEDIT (WXP) và điều hướng tới:

    HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager 
    
  2. W2K và WXP

    • W2K:
      Sửa
      Thêm giá trị gia tăng ...
      Data Type: REG_MULTI_SZ
      Value Name: PendingFileRenameOperations
      OK

    • WXP:
      Sửa
      New
      Multi-String Value
      nhập
      PendingFileRenameOperations

  3. Trong khu vực Dữ liệu, nhập "\??\" + filename để xóa. Các số LFN có thể được nhập mà không được nhúng trong dấu ngoặc kép. Để xóa C:\Long Directory Name\Long File Name.exe, nhập dữ liệu sau:

    \??\C:\Long Directory Name\Long File Name.exe 
    

    Sau đó nhấn OK .

  4. "Tên tệp đích" là chuỗi rỗng (không).Nó được nhập như sau:

    • W2K:
      Sửa
      Binary
      chọn Format dữ liệu: Hex
      nhấp chuột ở phần cuối của chuỗi hex
      nhập 0000 (bốn số không)
      OK

    • WXP:
      Nhấp chuột phải vào giá trị
      chọn "Sửa đổi dữ liệu nhị phân"
      nhấp vào cuối hex vòng
      nhập 0000 (bốn số không)
      OK

  5. Đóng REGEDT32/REGEDIT và khởi động lại để xóa các tập tin.

(shamelessly bị đánh cắp từ some random forum, vì hậu thế của.)

7

Nếu bạn muốn thực hiện theo chương trình. Tôi không chắc ... và tôi thực sự khuyên bạn nên chống lại nó. Nếu bạn chỉ xử lý sự cố trên máy của mình, SysInternals Process Explorer có thể giúp bạn

Chạy nó, sử dụng lệnh Tìm xử lý (tôi nghĩ rằng nó nằm trong menu tìm hoặc xử lý) và tìm kiếm tên tệp của bạn . Sau khi xử lý (s) được tìm thấy, bạn có thể buộc phải đóng chúng.

Sau đó, bạn có thể xóa tệp và v.v.

Hãy coi chừng, thực hiện điều này có thể khiến chương trình sở hữu tay cầm hoạt động lạ, vì bạn vừa kéo tấm thảm từ dưới nó ra, nhưng nó hoạt động tốt khi bạn gỡ lỗi mã riêng của mình hoặc khi visual studio/windows explorer đang được crap và không phát hành tập tin xử lý ngay cả khi bạn nói với họ để đóng các tập tin lứa tuổi trước đây ... sigh :-)

3

Ồ, một hack lớn tôi làm việc nhiều năm trước đây, là Windows giành được Không cho phép bạn xóa tệp nhưng nó cho phép bạn di chuyển chúng.

Pseudo-loại-of-code:

mv %WINDIR%\System32\mfc42.dll %WINDIR\System32\mfc42.dll.old 
Install new mfc42.dll 
Tell user to save work and restart applications 

Khi các ứng dụng khởi động lại (lưu ý chúng tôi không cần phải khởi động lại máy), họ nạp mới mfc42.dll, và tất cả là tốt. Điều đó, cùng với PendingFileOperations để xóa cái cũ trong lần tiếp theo toàn bộ hệ thống khởi động lại, hoạt động khá tốt.

14

Phương pháp điển hình như sau. Bạn đã nói rằng bạn muốn làm điều này trong C# vì vậy ở đây đi ...

  1. Nếu bạn không biết quy trình nào đã khóa tệp, bạn sẽ cần kiểm tra danh sách xử lý của từng quy trình và truy vấn từng xử lý để xác định xem nó có nhận dạng tệp bị khóa hay không. Làm điều này trong C# có thể sẽ yêu cầu P/Invoke hoặc một trung gian C++/CLI để gọi các API gốc mà bạn sẽ cần.
  2. Khi bạn đã tìm ra quy trình nào bị khóa, bạn sẽ cần phải tiêm một DLL gốc nhỏ vào quy trình một cách an toàn (bạn cũng có thể tiêm một DLL được quản lý, nhưng điều này sẽ phức tạp hơn) phải bắt đầu hoặc đính kèm với thời gian chạy .NET).
  3. Đó bootstrap DLL sau đó đóng tay cầm sử dụng CloseHandle vv

Về cơ bản: Cách để mở khóa một "khóa" tập tin là phải tiêm một tập tin DLL vào không gian địa chỉ vi phạm quy trình và đóng nó lại chính mình. Bạn có thể thực hiện việc này bằng mã gốc hoặc mã được quản lý. Không có vấn đề gì, bạn sẽ cần một số lượng nhỏ mã gốc hoặc ít nhất là P/Gọi vào cùng một.

liên kết hữu ích:

Chúc may mắn!

4

Sử dụng những lời khuyên Orion Edwards Tôi đã tải về Sysinternals Process Explorer do đó cho phép tôi khám phá ra rằng các tập tin tôi đã gặp khó khăn xóa được trong thực tế được tổ chức không bởi đối tượng Excel.Applications tôi nghĩ, nhưng đúng hơn là mã C# của tôi gửi mã thư đã tạo ra một đối tượng Đính kèm để mở một tay cầm cho tệp này đang mở.

Khi tôi thấy điều này, tôi khá đơn giản được gọi là phương pháp vứt bỏ đối tượng Đính kèm và tay cầm đã được giải phóng.

Trình khám phá Sysinternals cho phép tôi khám phá điều này được sử dụng cùng với trình gỡ lỗi trực quan   Studio   2005.

Tôi rất khuyên bạn nên sử dụng công cụ này!

+0

Đây cũng chính là vấn đề của tôi. Cảm ơn bạn +1 – Fandango68

2

Bạn có thể sử dụng mã mà bạn cung cấp các đường dẫn tập tin đầy đủ, và nó sẽ trả về một List<Processes> bất cứ điều gì khóa tập tin đó:

using System.Runtime.InteropServices; 
using System.Diagnostics; 

static public class FileUtil 
{ 
    [StructLayout(LayoutKind.Sequential)] 
    struct RM_UNIQUE_PROCESS 
    { 
     public int dwProcessId; 
     public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime; 
    } 

    const int RmRebootReasonNone = 0; 
    const int CCH_RM_MAX_APP_NAME = 255; 
    const int CCH_RM_MAX_SVC_NAME = 63; 

    enum RM_APP_TYPE 
    { 
     RmUnknownApp = 0, 
     RmMainWindow = 1, 
     RmOtherWindow = 2, 
     RmService = 3, 
     RmExplorer = 4, 
     RmConsole = 5, 
     RmCritical = 1000 
    } 

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
    struct RM_PROCESS_INFO 
    { 
     public RM_UNIQUE_PROCESS Process; 

     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] 
     public string strAppName; 

     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] 
     public string strServiceShortName; 

     public RM_APP_TYPE ApplicationType; 
     public uint AppStatus; 
     public uint TSSessionId; 
     [MarshalAs(UnmanagedType.Bool)] 
     public bool bRestartable; 
    } 

    [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)] 
    static extern int RmRegisterResources(uint pSessionHandle, 
              UInt32 nFiles, 
              string[] rgsFilenames, 
              UInt32 nApplications, 
              [In] RM_UNIQUE_PROCESS[] rgApplications, 
              UInt32 nServices, 
              string[] rgsServiceNames); 

    [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)] 
    static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey); 

    [DllImport("rstrtmgr.dll")] 
    static extern int RmEndSession(uint pSessionHandle); 

    [DllImport("rstrtmgr.dll")] 
    static extern int RmGetList(uint dwSessionHandle, 
           out uint pnProcInfoNeeded, 
           ref uint pnProcInfo, 
           [In, Out] RM_PROCESS_INFO[] rgAffectedApps, 
           ref uint lpdwRebootReasons); 

    /// <summary> 
    /// Find out what process(es) have a lock on the specified file. 
    /// </summary> 
    /// <param name="path">Path of the file.</param> 
    /// <returns>Processes locking the file</returns> 
    /// <remarks>See also: 
    /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx 
    /// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing) 
    /// 
    /// </remarks> 
    static public List<Process> WhoIsLocking(string path) 
    { 
     uint handle; 
     string key = Guid.NewGuid().ToString(); 
     List<Process> processes = new List<Process>(); 

     int res = RmStartSession(out handle, 0, key); 
     if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker."); 

     try 
     { 
      const int ERROR_MORE_DATA = 234; 
      uint pnProcInfoNeeded = 0, 
       pnProcInfo = 0, 
       lpdwRebootReasons = RmRebootReasonNone; 

      string[] resources = new string[] { path }; // Just checking on one resource. 

      res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null); 

      if (res != 0) throw new Exception("Could not register resource.");          

      //Note: there's a race condition here -- the first call to RmGetList() returns 
      //  the total number of process. However, when we call RmGetList() again to get 
      //  the actual processes this number may have increased. 
      res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons); 

      if (res == ERROR_MORE_DATA) 
      { 
       // Create an array to store the process results 
       RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded]; 
       pnProcInfo = pnProcInfoNeeded; 

       // Get the list 
       res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons); 
       if (res == 0) 
       { 
        processes = new List<Process>((int)pnProcInfo); 

        // Enumerate all of the results and add them to the 
        // list to be returned 
        for (int i = 0; i < pnProcInfo; i++) 
        { 
         try 
         { 
          processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId)); 
         } 
         // catch the error -- in case the process is no longer running 
         catch (ArgumentException) { } 
        } 
       } 
       else throw new Exception("Could not list processes locking resource.");      
      } 
      else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");      
     } 
     finally 
     { 
      RmEndSession(handle); 
     } 

     return processes; 
    } 
} 

Sau đó, lặp danh sách chương trình và đóng chúng lại và xóa các tập tin :

string[] files = Directory.GetFiles(target_dir); 
    List<Process> lstProcs = new List<Process>(); 

    foreach (string file in files) 
    { 
     lstProcs = ProcessHandler.WhoIsLocking(file); 
     if (lstProcs.Count > 0) // deal with the file lock 
     { 
      foreach (Process p in lstProcs) 
      { 
       if (p.MachineName == ".") 
        ProcessHandler.localProcessKill(p.ProcessName); 
       else 
        ProcessHandler.remoteProcessKill(p.MachineName, txtUserName.Text, txtPassword.Password, p.ProcessName); 
      } 
      File.Delete(file); 
     } 
     else 
      File.Delete(file); 
    } 

Và tùy thuộc vào nếu tập tin trên máy tính cục bộ:

public static void localProcessKill(string processName) 
{ 
    foreach (Process p in Process.GetProcessesByName(processName)) 
    { 
     p.Kill(); 
    } 
} 

hoặc máy tính mạng:

public static void remoteProcessKill(string computerName, string fullUserName, string pword, string processName) 
{ 
    var connectoptions = new ConnectionOptions(); 
    connectoptions.Username = fullUserName; // @"YourDomainName\UserName"; 
    connectoptions.Password = pword; 

    ManagementScope scope = new ManagementScope(@"\\" + computerName + @"\root\cimv2", connectoptions); 

    // WMI query 
    var query = new SelectQuery("select * from Win32_process where name = '" + processName + "'"); 

    using (var searcher = new ManagementObjectSearcher(scope, query)) 
    { 
     foreach (ManagementObject process in searcher.Get()) 
     { 
      process.InvokeMethod("Terminate", null); 
      process.Dispose(); 
     } 
    } 
} 

Tài liệu tham khảo:
How do I find out which process is locking a file using .NET?

Delete a directory where someone has opened a file

Các vấn đề liên quan