0

I'm coding an console-application that write a query result in a .xlsx document. For this porpouse, I'm using the Microsoft.Office.Interop.Excel library with the Workbook.SaveAs() method to save the file in a shared folder on network. How expected, when the file is already open, the code throws an exception to deal with this situation, but I need one more step: catch at least one user at network that has the file open, before the save operation fails.
Any idea?

1 Answers1

0

To find out who works with a file if it has been opened, you should check through the file address to see if a process is running at that address. If yes, who is the owner of this process? Which is the user who locked the file. I put this under the full code.

In the static FileUtil class, a static method called WhoIsLocking(string path) returns the process that uses the file, and then in the GetProcessOwner(process.Id) method, it returns the name of the process owner.

Just a tip: Install the System.Management library via nuget

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 class

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 class references : how-do-i-find-out-which-process-is-locking-a-file-using-net

Meysam Asadi
  • 6,438
  • 3
  • 7
  • 17