Ullan
Ullan

Reputation: 1341

Shell Extension DLL - how to capture the folder path, if user clicks inside the folder empty area?

Using shell extension dll, how to capture the folder path, if user clicks inside the folder empty area?

Upvotes: 1

Views: 1670

Answers (2)

le canh minh
le canh minh

Reputation: 11

With VC++ language please reference Winmerge souce code http://sourceforge.net/p/winmerge/code/HEAD/tree/trunk/ShellExtension/

With C# please reference this article http://www.codeproject.com/Articles/174369/How-to-Write-Windows-Shell-Extension-with-NET-Lang and update some place bellow: At FileContextMenuExt.cs file:

...............

    #region Shell Extension Registration

    [ComRegisterFunction()]
    public static void Register(Type t)
    {
        try
        {
            ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, "Directory", 
                "CSShellExtContextMenuHandler.FileContextMenuExt Class");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message); // Log the error
            throw;  // Re-throw the exception
        }
    }

    [ComUnregisterFunction()]
    public static void Unregister(Type t)
    {
        try
        {
            ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, "Directory");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message); // Log the error
            throw;  // Re-throw the exception
        }
    }

    #endregion
   ............... 

   public void Initialize(IntPtr pidlFolder, IntPtr pDataObj, IntPtr hKeyProgID)
    {
        if (pDataObj == IntPtr.Zero && pidlFolder == IntPtr.Zero)
        {
            throw new ArgumentException();
        }

        FORMATETC fe = new FORMATETC();
        fe.cfFormat = (short)CLIPFORMAT.CF_HDROP;
        fe.ptd = IntPtr.Zero;
        fe.dwAspect = DVASPECT.DVASPECT_CONTENT;
        fe.lindex = -1;
        fe.tymed = TYMED.TYMED_HGLOBAL;
        STGMEDIUM stm = new STGMEDIUM();          

        try
        {
            if (pDataObj != IntPtr.Zero)
            {
                // The pDataObj pointer contains the objects being acted upon. In this 
                // example, we get an HDROP handle for enumerating the selected files 
                // and folders.
                IDataObject dataObject = (IDataObject)Marshal.GetObjectForIUnknown(pDataObj);
                dataObject.GetData(ref fe, out stm);

                // Get an HDROP handle.
                IntPtr hDrop = stm.unionmember;
                if (hDrop == IntPtr.Zero)
                {
                    throw new ArgumentException();
                }

                // Determine how many files are involved in this operation.
                uint nFiles = NativeMethods.DragQueryFile(hDrop, UInt32.MaxValue, null, 0);

                // This code sample displays the custom context menu item when only 
                // one file is selected. 
                if (nFiles == 1)
                {
                    // Get the path of the file.
                    StringBuilder fileName = new StringBuilder(260);
                    if (0 == NativeMethods.DragQueryFile(hDrop, 0, fileName,
                        fileName.Capacity))
                    {
                        Marshal.ThrowExceptionForHR(WinError.E_FAIL);
                    }
                    this.selectedFile = fileName.ToString();
                }
                else
                {
                    Marshal.ThrowExceptionForHR(WinError.E_FAIL);
                }
            }

            if (pidlFolder != IntPtr.Zero) {
                StringBuilder folderName = new StringBuilder(260);
                if (0 == NativeMethods.SHGetPathFromIDList(pidlFolder, folderName))
                {
                    Marshal.ThrowExceptionForHR(WinError.E_FAIL);
                }
                this.selectedFile = folderName.ToString();
            }
        }
        finally
        {
            NativeMethods.ReleaseStgMedium(ref stm);
        }
    }

At ShellExtLib.cs file Add folowing source:

    [DllImport("shell32.dll")]
    public static extern Int32 SHGetPathFromIDList(
        IntPtr pidl,                // Address of an item identifier list that
        // specifies a file or directory location
        // relative to the root of the namespace (the
        // desktop). 
        StringBuilder pszPath);        // Address of a buffer to receive the file system

And update RegisterShellExtContextMenuHandler and UnregisterShellExtContextMenuHandler function at ShellExtLib.cs file

    public static void RegisterShellExtContextMenuHandler(Guid clsid, 
        string fileType, string friendlyName)
    {
        if (clsid == Guid.Empty)
        {
            throw new ArgumentException("clsid must not be empty");
        }
        if (string.IsNullOrEmpty(fileType))
        {
            throw new ArgumentException("fileType must not be null or empty");
        }

        // If fileType starts with '.', try to read the default value of the 
        // HKCR\<File Type> key which contains the ProgID to which the file type 
        // is linked.
        if (fileType.StartsWith("."))
        {
            using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(fileType))
            {
                if (key != null)
                {
                    // If the key exists and its default value is not empty, use 
                    // the ProgID as the file type.
                    string defaultVal = key.GetValue(null) as string;
                    if (!string.IsNullOrEmpty(defaultVal))
                    {
                        fileType = defaultVal;
                    }
                }
            }
        }
        else {

            // Create the key HKCR\<File Type>\shellex\ContextMenuHandlers\{<CLSID>}.
            string keyName1 = string.Format(@"{0}\Background\shellex\ContextMenuHandlers\{1}",
                fileType, clsid.ToString("B"));
            using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(keyName1))
            {
                // Set the default value of the key.
                if (key != null && !string.IsNullOrEmpty(friendlyName))
                {
                    key.SetValue(null, friendlyName);
                }
            }
        }

        // Create the key HKCR\<File Type>\shellex\ContextMenuHandlers\{<CLSID>}.
        string keyName = string.Format(@"{0}\shellex\ContextMenuHandlers\{1}",
            fileType, clsid.ToString("B"));
        using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(keyName))
        {
            // Set the default value of the key.
            if (key != null && !string.IsNullOrEmpty(friendlyName))
            {
                key.SetValue(null, friendlyName);
            }
        }
    }


    public static void UnregisterShellExtContextMenuHandler(Guid clsid, 
        string fileType)
    {
        if (clsid == null)
        {
            throw new ArgumentException("clsid must not be null");
        }
        if (string.IsNullOrEmpty(fileType))
        {
            throw new ArgumentException("fileType must not be null or empty");
        }

        // If fileType starts with '.', try to read the default value of the 
        // HKCR\<File Type> key which contains the ProgID to which the file type 
        // is linked.
        if (fileType.StartsWith("."))
        {
            using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(fileType))
            {
                if (key != null)
                {
                    // If the key exists and its default value is not empty, use 
                    // the ProgID as the file type.
                    string defaultVal = key.GetValue(null) as string;
                    if (!string.IsNullOrEmpty(defaultVal))
                    {
                        fileType = defaultVal;
                    }
                }
            }
        }
        else {
            // Remove the key HKCR\<File Type>\shellex\ContextMenuHandlers\{<CLSID>}.
            string keyName1 = string.Format(@"{0}\Background\shellex\ContextMenuHandlers\{1}",
                fileType, clsid.ToString("B"));
            Registry.ClassesRoot.DeleteSubKeyTree(keyName1, false);
        }

        // Remove the key HKCR\<File Type>\shellex\ContextMenuHandlers\{<CLSID>}.
        string keyName = string.Format(@"{0}\shellex\ContextMenuHandlers\{1}",
            fileType, clsid.ToString("B"));
        Registry.ClassesRoot.DeleteSubKeyTree(keyName, false);
    }

Upvotes: 0

Stefan
Stefan

Reputation: 43575

If you're implementing a shell extension dll, then you get the path in your IShellExtInit::Initialize() method as the pidlFolder parameter.

To make sure your extension is also registered for folder backgrounds, you have to create the appropriate entries also under HKCR\Directory\Background\shellex\ContextMenuHandlers

Upvotes: 5

Related Questions