title: Monitor a folder and find if a file is open in windows app
body: <p><strong>Edit:</strong> So apparently there is <strong>no simple or convenient way of knowing if a file is open / being held by a process</strong>. I think this is a problem or design decision of Windows OS itself because even programs like Process Explorer can't tell when I have "mytext.txt" file is open in notepad right besides it's window :) further research also showed only way to get this information reliably is to dvelve into system drivers. My question still stands and if anyone can show me a path to this driver solution that is also appreciated.</p>
  
  <p>p.s: I sincerely think there <em>should</em> be a simple solution to this answer. I mean, it feels like it's a feature missing in a whole OS - can't tell if a file is open? Really? Not even an API method? This doesn't feel right to me.</p>
  
  <p>-- Original Question --</p>
  
  <p>I've been searching all around the web to find an answer to this. Apparently after Windows Vista there is a feature called Restart Manager was added and we can invoke that dll's methods to check if a file locked or not.</p>
  
  <p>However, when I try this in a simple console app (inspired by this article: <a href="https://blogs.msdn.microsoft.com/oldnewthing/20120217-00/?p=8283" rel="nofollow noreferrer">https://blogs.msdn.microsoft.com/oldnewthing/20120217-00/?p=8283</a> and this SO answer <a href="https://stackoverflow.com/a/20623311/1858013">https://stackoverflow.com/a/20623311/1858013</a>) it can tell if an office file (xls, doc etc...) is locked / open but not when I open a .txt file in Notepad, or a .sln file when I have a project open in Visual Studio.</p>
  
  <p>My program is monitoring a preset folder in user's system (say, Documents folder) and I want to trigger an event when <strong>any file</strong> is opened in that folder. The program itself won't modify the file or do work with that file so locking or unlocking is not a problem, I simply want to be notified when a file is opened in any program in the monitored folder.</p>
  
  <p>Right now, I'm using this code to find which processes are locking a file but I feel there is an easier way to know when a file is opened than using Win32 apis.</p>
  
  <pre><code>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);
  
      /// &lt;summary&gt;
      /// Find out what process(es) have a lock on the specified file.
      /// &lt;/summary&gt;
      /// &lt;param name="path"&gt;Path of the file.&lt;/param&gt;
      /// &lt;returns&gt;Processes locking the file&lt;/returns&gt;
      /// &lt;remarks&gt;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)
      /// 
      /// &lt;/remarks&gt;
      static public List&lt;Process&gt; WhoIsLocking(string path)
      {
          uint handle;
          string key = Guid.NewGuid().ToString();
          List&lt;Process&gt; processes = new List&lt;Process&gt;();
  
          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&lt;Process&gt;((int)pnProcInfo);
  
                      // Enumerate all of the results and add them to the 
                      // list to be returned
                      for (int i = 0; i &lt; 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;
      }
  }
  </code></pre>
  
  <p>Any help would be appreciated.</p>