保存操作失败时如何捕获网络上的哪个用户打开了文件?

Posted

技术标签:

【中文标题】保存操作失败时如何捕获网络上的哪个用户打开了文件?【英文标题】:How to catch which user at network has the file open when the save operation fails? 【发布时间】:2021-07-30 05:53:16 【问题描述】:

我正在编写一个控制台应用程序,它将查询结果写入 .xlsx 文档。对于这个海豚,我使用Microsoft.Office.Interop.Excel 库和Workbook.SaveAs() 方法将文件保存在网络上的共享文件夹中。 多么期望,当文件已经打开时,代码会抛出异常来处理这种情况,但我需要多一步:在保存操作失败之前,至少在网络上捕获一个打开文件的用户。 有什么想法吗?

【问题讨论】:

【参考方案1】:

要找出谁在使用已打开的文件,您应该检查文件地址以查看是否有进程在该地址运行。如果是,谁是这个过程的所有者?哪个是锁定文件的用户。我把它放在完整的代码下。

在静态FileUtil类中,一个名为WhoIsLocking(string path)的静态方法返回使用该文件的进程,然后在GetProcessOwner(process.Id)方法中,它返回进程所有者的名称。

提示:通过 nuget 安装 System.Management

public string GetProcessOwner(int processId)

   string query = "Select * From Win32_Process Where ProcessID = " + processId;
   ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
   ManagementObjectCollection processList = searcher.Get();

   foreach (ManagementObject obj in processList)
   
       string[] argList = new string[]  string.Empty, string.Empty ;
       int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
       if (returnVal == 0)
       
           // return DOMAIN\user
           return argList[1] + "\\" + argList[0];
       
    
    return "NO OWNER";

protected virtual bool IsFileLocked(FileInfo file)

     try
     
        using (FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
        
            stream.Close();
        
     
     catch (IOException)
     
        return true;
     

     //file is not locked
     return false;

public void OpenFile(string path)

   FileInfo fInfo = new FileInfo(path);
   if (IsFileLocked(fInfo))
   
      //get user name
      var process = FileUtil.WhoIsLocking(path).FirstOrDefault();
      string user = GetProcessOwner(process.Id);
   
   else
   
      //Open/read/write file
    

FileUtil

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;
      
 

FileUtil 类参考:how-do-i-find-out-which-process-is-locking-a-file-using-net

【讨论】:

以上是关于保存操作失败时如何捕获网络上的哪个用户打开了文件?的主要内容,如果未能解决你的问题,请参考以下文章

xml文件用啥编辑

使用 OpenCV 捕获和保存视频

如何使用 jquery/javascript 捕获页面上的所有回发?

能直接用浏览器网页打开网站文件夹目录,怎么禁止?

仅在 Sublime 中保存到映射驱动器时出错:无法保存打开用户映射部分的文件

如何在内部存储中创建文件夹并保存捕获的图像