Click here to Skip to main content
15,891,136 members
Articles / Desktop Programming / Win32

Notifying Windows Explorer about files in use

Rate me:
Please Sign up or sign in to vote.
4.84/5 (47 votes)
26 Mar 2013LGPL33 min read 84.8K   635   132  
How to notify Windows Explorer about which files are used and locked by your application.
using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Security;
using Microsoft.Win32;

namespace LukeSw.IO
{
    /// <summary>
    /// Provides a way of notifying Windows Explorer, which files are currently used by the application.
    /// </summary>
    public sealed class FileLocker : IDisposable, NativeMethods.IOleObject
    {
        #region " Registering and Unregistering "

        /// <summary>
        /// Registers the FileLocker. Invoke it from the application during the installation process.
        /// </summary>
        /// <exception cref="T:System.InvalidOperationException">The attempt to register the FileLocker is invalid because the attempt to modify the registry is denied or the process executable has an invalid location.</exception>
        public static void Register()
        {
            try
            {
                string guid = GetAssemblyGuid();
                string progid = GetAssemblyProgId();
                string location = GetAssemblyLocation();

                Registry.ClassesRoot.CreateSubKey(@"CLSID\" + guid + @"\ProgID").SetValue(null, progid);
                Registry.ClassesRoot.CreateSubKey(progid + @"\CLSID").SetValue(null, guid);
                Registry.ClassesRoot.CreateSubKey(progid + @"\shell\Open\command").SetValue(null, "\"" + location + "\"");
            }
            catch (Exception e)
            {
                throw new InvalidOperationException("Cannot register FileLocker.", e);
            }
        }

        /// <summary>
        /// Unregisters the FileLocker. Invoke it from the application during the uninstallation process.
        /// </summary>
        /// <exception cref="T:System.InvalidOperationException">The attempt to unregister the FileLocker is invalid because the attempt to modify the registry is denied or the process executable has an invalid location.</exception>
        public static void Unregister()
        {
            try
            {
                string guid = GetAssemblyGuid();
                string progid = GetAssemblyProgId();
                string location = GetAssemblyLocation();

                Registry.ClassesRoot.OpenSubKey(@"CLSID", true).DeleteSubKeyTree(guid);
                Registry.ClassesRoot.DeleteSubKeyTree(progid);
                Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\ShellNoRoam\MUICache", true).DeleteValue(location, false);
            }
            catch (Exception e)
            {
                throw new InvalidOperationException("Cannot unregister FileLocker.", e);
            }
        }

        private static string GetAssemblyLocation()
        {
            string location = Assembly.GetEntryAssembly().Location;
            if (!File.Exists(location))
            {
                throw new FileNotFoundException();
            }
            return location;
        }

        private static string GetAssemblyGuid()
        {
            return "{" + _GetAssemblyGuid() + "}";
        }

        private static string GetAssemblyProgId()
        {
            return "LukeSw.IO.FileLocker." + _GetAssemblyGuid();
        }

        private static Guid _GetAssemblyGuid()
        {
            object[] attrs = Assembly.GetEntryAssembly().GetCustomAttributes(typeof(GuidAttribute), false);
            Guid? g = null;
            foreach (object attr in attrs)
            {
                if (attr is GuidAttribute)
                {
                    g = new Guid((attr as GuidAttribute).Value);
                }
            }
            if (g == null)
            {
                throw new InvalidOperationException("Assembly has no GuidAttribute attribute applied to it.");
            }
            return g.Value;
        }

        #endregion

        #region " Locking & Unlocking "

        /// <summary>
        /// Initializes a new instance of the <see cref="FileLocker"/> class and notifies the Windows Explorer that the specified file is currently used by the application.
        /// </summary>
        /// <param name="fileInfo">The used file's FileInfo.</param>
        /// <exception cref="T:System.IO.FileNotFoundException"/>
        /// <exception cref="T:System.InvalidOperationException">Attempt to notify of file usage was unsuccessful.</exception>
        public FileLocker(FileInfo fileInfo) : this(fileInfo.FullName) { }

        /// <summary>
        /// Initializes a new instance of the <see cref="FileLocker"/> class and notifies the Windows Explorer that the specified file is currently used by the application.
        /// </summary>
        /// <param name="path">The path of the used file.</param>
        /// <exception cref="T:System.IO.FileNotFoundException"/>
        /// <exception cref="T:System.InvalidOperationException">Attempt to notify of file usage was unsuccessful.</exception>
        public FileLocker(string path)
        {
            try
            {
                path = Path.GetFullPath(path);
            }
            catch (Exception e)
            {
                throw new ArgumentException("The specified path is invalid.", "path", e);
            }
            if (!File.Exists(path))
            {
                throw new FileNotFoundException("The specified file does not exist.", path);
            }
            IBindCtx ctx;
            int ret = NativeMethods.CreateBindCtx(0, out ctx);
            if (ret != 0 || ctx == null)
            {
                throw new InvalidOperationException();
            }

            IRunningObjectTable rot;
            try
            {
                ctx.GetRunningObjectTable(out rot);
                if (rot == null)
                {
                    throw new InvalidOperationException();
                }
                try
                {
                    IMoniker mon;
                    ret = NativeMethods.CreateFileMoniker(path, out mon);
                    if (ret != 0 || mon == null)
                    {
                        throw new InvalidOperationException();
                    }

                    try
                    {
                        try
                        {
                            cookie = rot.Register(NativeMethods.ROTFLAGS_ALLOWANYCLIENT | NativeMethods.ROTFLAGS_REGISTRATIONKEEPSALIVE, this, mon);
                        }
                        catch (COMException)
                        {
                            cookie = rot.Register(NativeMethods.ROTFLAGS_REGISTRATIONKEEPSALIVE, this, mon);
                        }
                    }
                    finally
                    {
                        Marshal.ReleaseComObject(mon);
                    }
                }
                finally
                {
                    Marshal.ReleaseComObject(rot);
                }
            }
            finally
            {
                Marshal.ReleaseComObject(ctx);
            }
        }

        ~FileLocker()
        {
            Dispose(false);
        }

        private int cookie;

        /// <summary>
        /// Cancels the notification and disposes the object.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing)
        {
            try
            {
                Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\ShellNoRoam\MUICache", true).DeleteValue(GetAssemblyLocation(), false);
            }
            catch { }
            IBindCtx ctx;
            int ret = NativeMethods.CreateBindCtx(0, out ctx);
            if (ret != 0 || ctx == null)
            {
                return;
            }

            IRunningObjectTable rot;
            try
            {
                ctx.GetRunningObjectTable(out rot);
                if (rot == null)
                {
                    return;
                }
                try
                {
                    rot.Revoke(cookie);
                }
                catch { }
                finally
                {
                    try
                    {
                        Marshal.ReleaseComObject(rot);
                    }
                    catch { }
                }
            }
            catch { }
            finally
            {
                try
                {
                    Marshal.ReleaseComObject(ctx);
                }
                catch { }
            }
        }

        #endregion

        #region " IOleObject Members "

        int NativeMethods.IOleObject.SetClientSite(object pClientSite)
        {
            throw new NotImplementedException();
        }

        object NativeMethods.IOleObject.GetClientSite()
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.SetHostNames(string szContainerApp, string szContainerObj)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.Close(int dwSaveOption)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.SetMoniker(int dwWhichMoniker, object pmk)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.GetMoniker(int dwAssign, int dwWhichMoniker, out object moniker)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.InitFromData(IDataObject pDataObject, int fCreation, int dwReserved)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.GetClipboardData(int dwReserved, out IDataObject data)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.DoVerb(int iVerb, IntPtr lpmsg, object pActiveSite, int lindex, IntPtr hwndParent, NativeMethods.COMRECT lprcPosRect)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.EnumVerbs(out object e)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.OleUpdate()
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.IsUpToDate()
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.GetUserClassID(ref Guid pClsid)
        {
            try
            {
                pClsid = _GetAssemblyGuid();
            }
            catch { }
            return 0;
        }

        int NativeMethods.IOleObject.GetUserType(int dwFormOfType, out string userType)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.SetExtent(int dwDrawAspect, NativeMethods.SIZE pSizel)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.GetExtent(int dwDrawAspect, NativeMethods.SIZE pSizel)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.Advise(IAdviseSink pAdvSink, out int cookie)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.Unadvise(int dwConnection)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.EnumAdvise(out IEnumSTATDATA e)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.GetMiscStatus(int dwAspect, out int misc)
        {
            throw new NotImplementedException();
        }

        int NativeMethods.IOleObject.SetColorScheme(NativeMethods.tagLOGPALETTE pLogpal)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

    #region " NativeMethods class "

    internal static class NativeMethods
    {
        public const int ROTFLAGS_REGISTRATIONKEEPSALIVE = 0x1;
        public const int ROTFLAGS_ALLOWANYCLIENT = 0x2;
        [DllImport("ole32.dll")]
        public static extern int CreateFileMoniker([MarshalAs(UnmanagedType.LPWStr)] string lpszPathName, out IMoniker ppmk);
        [DllImport("ole32.dll")]
        public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);

        [StructLayout(LayoutKind.Sequential)]
        public class COMRECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
        }

        [StructLayout(LayoutKind.Sequential)]
        public class SIZE
        {
            public int cx;
            public int cy;
        }

        [StructLayout(LayoutKind.Sequential)]
        public sealed class tagLOGPALETTE
        {
            [MarshalAs(UnmanagedType.U2)]
            public ushort palVersion;
            [MarshalAs(UnmanagedType.U2)]
            public ushort palNumEntries;
        }

        [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), SecurityCritical(SecurityCriticalScope.Everything), Guid("00000112-0000-0000-C000-000000000046"), SuppressUnmanagedCodeSecurity]
        public interface IOleObject
        {
            [PreserveSig]
            int SetClientSite([In, MarshalAs(UnmanagedType.Interface)] object pClientSite);
            object GetClientSite();
            [PreserveSig]
            int SetHostNames([In, MarshalAs(UnmanagedType.LPWStr)] string szContainerApp, [In, MarshalAs(UnmanagedType.LPWStr)] string szContainerObj);
            [PreserveSig]
            int Close(int dwSaveOption);
            [PreserveSig]
            int SetMoniker([In, MarshalAs(UnmanagedType.U4)] int dwWhichMoniker, [In, MarshalAs(UnmanagedType.Interface)] object pmk);
            [PreserveSig]
            int GetMoniker([In, MarshalAs(UnmanagedType.U4)] int dwAssign, [In, MarshalAs(UnmanagedType.U4)] int dwWhichMoniker, [MarshalAs(UnmanagedType.Interface)] out object moniker);
            [PreserveSig]
            int InitFromData([In, MarshalAs(UnmanagedType.Interface)] IDataObject pDataObject, int fCreation, [In, MarshalAs(UnmanagedType.U4)] int dwReserved);
            [PreserveSig]
            int GetClipboardData([In, MarshalAs(UnmanagedType.U4)] int dwReserved, out IDataObject data);
            [PreserveSig]
            int DoVerb(int iVerb, [In] IntPtr lpmsg, [In, MarshalAs(UnmanagedType.Interface)] object pActiveSite, int lindex, IntPtr hwndParent, [In] NativeMethods.COMRECT lprcPosRect);
            [PreserveSig]
            int EnumVerbs(out object e);
            [PreserveSig]
            int OleUpdate();
            [PreserveSig]
            int IsUpToDate();
            [PreserveSig]
            int GetUserClassID([In, Out] ref Guid pClsid);
            [PreserveSig]
            int GetUserType([In, MarshalAs(UnmanagedType.U4)] int dwFormOfType, [MarshalAs(UnmanagedType.LPWStr)] out string userType);
            [PreserveSig]
            int SetExtent([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect, [In] NativeMethods.SIZE pSizel);
            [PreserveSig]
            int GetExtent([In, MarshalAs(UnmanagedType.U4)] int dwDrawAspect, [Out] NativeMethods.SIZE pSizel);
            [PreserveSig]
            int Advise(IAdviseSink pAdvSink, out int cookie);
            [PreserveSig]
            int Unadvise([In, MarshalAs(UnmanagedType.U4)] int dwConnection);
            [PreserveSig]
            int EnumAdvise(out IEnumSTATDATA e);
            [PreserveSig]
            int GetMiscStatus([In, MarshalAs(UnmanagedType.U4)] int dwAspect, out int misc);
            [PreserveSig]
            int SetColorScheme([In] NativeMethods.tagLOGPALETTE pLogpal);
        }

    }

    #endregion
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer
Poland Poland
I am a graduate of Wroclaw University of Science and Technology, Poland.

My interests: gardening, reading, programming, drawing, Japan, Spain.

Comments and Discussions