using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
#pragma warning disable CS1591 // 缺少对公共可见类型或成员的 XML 注释
namespace WinShell {

    /// <summary>
    /// 
    /// </summary>
    public class CShellFolder: IDisposable {
        public CShellFolder() {
            desktop = API.GetDesktopFolder(/*out desktopPtr*/);
        }

        public void Dispose() {
            Marshal.ReleaseComObject(desktop);
        }

        /// <summary>
        /// 
        /// </summary>
        public class FetchFolderEventArgs : EventArgs {
            public FetchFolderEventArgs(String folder)
                : base() {
                foldername = folder;
            }

            public String Folder {
                get {
                    return foldername;
                }
            }

            private String foldername;
        }

        /// <summary>
        /// 
        /// </summary>
        public class FetchFileEventArgs : EventArgs {
            public FetchFileEventArgs(String fileName)
                : base() {
                filename = fileName;
            }

            public String Filename {
                get {
                    return filename;
                }
            }

            private String filename;
        }

        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<FetchFolderEventArgs> FetchFolderEventHandler;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="folderName"></param>
        public void OnFetchFolder(Object sender, String folderName) {
            if (FetchFolderEventHandler != null) {
                FetchFolderEventHandler(sender, new FetchFolderEventArgs(folderName));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<FetchFileEventArgs> FetchFileEventHandler;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="fileName"></param>
        public void OnFatchFile(object sender, String fileName) {
            if (FetchFileEventHandler != null) {
                FetchFileEventHandler(sender, new FetchFileEventArgs(fileName));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="FolderPath"></param>
        public void DoFatch(string FolderPath) {
            //獲取 C 盤的 PIDL
            //string FolderPath = @"C:\";
            IntPtr Pidl = IntPtr.Zero;
            IShellFolder Root;
            uint i, j = 0;
            // 獲得對象的PIDL，即便對像在目錄樹中處於當前目錄下一層或更多層。例如，對於文件對象來說，它的解析名就是它的路徑，我們用文件系統對象的完全路徑名來調用桌面的IshellFolder接口的 ParseDisplayName 方法，它會返回這個對象的完全PIDL
            desktop.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, FolderPath, out i, out Pidl, ref j);
            desktop.BindToObject(Pidl, IntPtr.Zero, ref Guids.IID_IShellFolder, out Root);
           

            //循環尋找 C 盤下面的文件/文件夾的 PIDL
            IEnumIDList fileEnum = null;
            IEnumIDList folderEnum = null;
            IntPtr fileEnumPtr = IntPtr.Zero;
            IntPtr folderEnumPtr = IntPtr.Zero;
            IntPtr pidlSub;
            int celtFetched;

            //獲取子文件夾
            if (Root.EnumObjects(IntPtr.Zero, SHCONTF.FOLDERS | SHCONTF.INCLUDEHIDDEN, out fileEnumPtr) == API.S_OK) {
                fileEnum = (IEnumIDList)Marshal.GetObjectForIUnknown(fileEnumPtr);
                while (fileEnum.Next(1, out pidlSub, out celtFetched) == 0 && celtFetched == API.S_FALSE) {
                    //獲取顯示名稱
                    string name = API.GetNameByPIDL(pidlSub);
                    OnFetchFolder(this, name);
                }
            }

            //獲取子文件
            if (Root.EnumObjects(IntPtr.Zero, SHCONTF.NONFOLDERS | SHCONTF.INCLUDEHIDDEN, out folderEnumPtr) == API.S_OK) {
                folderEnum = (IEnumIDList)Marshal.GetObjectForIUnknown(folderEnumPtr);
                while (folderEnum.Next(1, out pidlSub, out celtFetched) == 0 && celtFetched == API.S_FALSE) {
                    string name = API.GetNameByPIDL(pidlSub);
                    OnFatchFile(this, name);
                    //lvFile.Items.Add(name, 0);
                }
            }

            Marshal.ReleaseComObject(Root);
        }
        /// <summary>
        /// 弹出系统右键菜单
        /// </summary>
        /// <param name="FolderPath"></param>
        /// <param name="MenuLocation"></param>
        /// <param name="handle"></param>
        public void ShowMenu(string FolderPath,Point MenuLocation,IntPtr handle)
        {
            //獲取 C 盤的 PIDL
            //string FolderPath = @"C:\";
            IntPtr Pidl = IntPtr.Zero;
            IShellFolder Root;
            uint i, j = 0;
            var parent_path=System.IO.Path.GetDirectoryName(FolderPath);
            var folder_name= System.IO.Path.GetFileName(FolderPath);
            // 獲得對象的PIDL，即便對像在目錄樹中處於當前目錄下一層或更多層。例如，對於文件對象來說，它的解析名就是它的路徑，我們用文件系統對象的完全路徑名來調用桌面的IshellFolder接口的 ParseDisplayName 方法，它會返回這個對象的完全PIDL
            desktop.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, parent_path, out i, out Pidl, ref j);
            desktop.BindToObject(Pidl, IntPtr.Zero, ref Guids.IID_IShellFolder, out Root);


            //循環尋找 C 盤下面的文件/文件夾的 PIDL
            IEnumIDList fileEnum = null;
            IntPtr fileEnumPtr = IntPtr.Zero;
            IntPtr folderEnumPtr = IntPtr.Zero;
            IntPtr pidlSub;
            int celtFetched;

            //獲取子文件夾
            if (Root.EnumObjects(IntPtr.Zero, SHCONTF.FOLDERS | SHCONTF.INCLUDEHIDDEN, out fileEnumPtr) == API.S_OK)
            {
                fileEnum = (IEnumIDList)Marshal.GetObjectForIUnknown(fileEnumPtr);
                while (fileEnum.Next(1, out pidlSub, out celtFetched) == 0 && celtFetched == API.S_FALSE)
                {
                    //獲取顯示名稱
                    string name = API.GetNameByPIDL(pidlSub);
                    if (name == folder_name)
                    {
                        OnFetchFolder(this, name);
                        IShellFolder IParent = Root;
                        IntPtr PIDL = pidlSub;
                        //存放 PIDL 的数组
                        IntPtr[] pidls = new IntPtr[1];
                        pidls[0] = PIDL;

                        //得到 IContextMenu 接口
                        IntPtr iContextMenuPtr = IntPtr.Zero;
                        iContextMenuPtr = IParent.GetUIObjectOf(IntPtr.Zero, (uint)pidls.Length,
                            pidls, ref Guids.IID_IContextMenu, out iContextMenuPtr);
                        IContextMenu iContextMenu = (IContextMenu)Marshal.GetObjectForIUnknown(iContextMenuPtr);

                        //提供一个弹出式菜单的句柄
                        IntPtr contextMenu = API.CreatePopupMenu();
                        iContextMenu.QueryContextMenu(contextMenu, 0,
                            API.CMD_FIRST, API.CMD_LAST, CMF.NORMAL | CMF.EXPLORE);// | CMF.CANRENAME

                        //弹出菜单
                        uint cmd = API.TrackPopupMenuEx(contextMenu, TPM.RETURNCMD,
                            MenuLocation.X, MenuLocation.Y, handle, IntPtr.Zero);
                        //获取命令序号，执行菜单命令
                        if (cmd >= API.CMD_FIRST)
                        {
                            CMINVOKECOMMANDINFOEX invoke = new CMINVOKECOMMANDINFOEX();
                            invoke.cbSize = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX));
                            invoke.lpVerb = (IntPtr)(cmd - 1);
                            invoke.lpDirectory = FolderPath;
                            invoke.fMask = 0;
                            invoke.ptInvoke = new POINT(MenuLocation.X, MenuLocation.Y);
                            invoke.nShow = 1;
                            iContextMenu.InvokeCommand(ref invoke);
                        }
                        //lvFile.Items.Add(name, 1);
                        break;
                    }
                }
            }
            Marshal.ReleaseComObject(Root);
        }
        private IShellFolder desktop;
    }
}
