C# 如何用DragDrop拖放特殊文件到程序窗口 (如:我的電腦 回收站 網上鄰居)

    最常見的情況是把一個文件拖放到Form窗體中,窗體顯示文件的路徑和文件名。這個功能容易實現,具體思路是藉助Form的事件響應函數DragEnter和DragDrop來完成。
    DragEnter函數主要作用是在拖拽文件進入窗體時修改鼠標圖標樣式,指定拖拽的可能效果(是拷貝文件數據,還是顯示文件路徑)。

    DragDrop函數主要作用是在鼠標拖放結束時進行某些操作。

    可以使用下面代碼實現常規文件的拖放:

<pre name="code" class="csharp">        private void Form1_DragEnter(object sender, DragEventArgs e)
        {
        <span style="white-space:pre">	</span>e.Effect = DragDropEffects.Link; //重要代碼:把拖拽的文件當成一個文件鏈接
        }
<span style="white-space:pre">	</span>private void Form1_DragDrop(object sender, DragEventArgs e)
        {
                //string[] formatList = e.Data.GetFormats();
                string  filename = ((Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString ();
                //或者 string  filename = ((Array)e.Data.GetData(“FileDrop”)).GetValue(0).ToString ();
        }


    函數e.Data.GetData(string)裏可填的數據類型可以是DataFormats.FileDrop,也可以是字符串形式如"FileDrop","FileName"等,根據拖放文件的類型而不同。我們可以通過e.Data.GetFormats()來獲得具體的格式類型列表。

    常規文件Formats類型信息如下:

    例如:FileDrop包含了文件的全路徑字符串,FileName則包含文件名。

    特殊文件的拖放(如:我的電腦,網上鄰居,回收站等)也可以用e.Data.GetFormats()來獲得具體的格式類型列表。

    具體信息如下:

顯然裏面並沒有FileDrop和FileName。因此要想獲取特殊文件的文件名用FileDrop和FileName不行。那麼這個辦法就不奏效了。

然而我們卻注意到一個細節,不論什麼類型的文件都有"Shell IDList Array","DragImageBits",“DragContext”等其它信息。

經過查找相關資料瞭解到"Shell IDList Array"實際上它指向一個CIDA結構。通過它可以得到對應的PIDL。而PIDL裏有文件的相關信息。

特殊文件拖放的實現代碼:

        private void Form1_DragEnter(object sender, DragEventArgs e)
        {
        	e.Effect = DragDropEffects.Link; //重要代碼:把拖拽的文件當成一個文件鏈接
        }
	private void Form1_DragDrop(object sender, DragEventArgs e)
        {
            string[] str = e.Data.GetFormats();
            MemoryStream data = (MemoryStream)e.Data.GetData("Shell IDList Array");
            byte[] b = data.ToArray();
            IntPtr p = Marshal.AllocHGlobal(b.Length);
            Marshal.Copy(b, 0, p, b.Length);

            UInt32 cidl = (UInt32)Marshal.ReadInt32(p);

            int offset = sizeof(UInt32);
            IntPtr parentpidl = (IntPtr)((int)p + (UInt32)Marshal.ReadInt32(p, offset));

            for (int i = 1; i <= cidl; ++i)
            {
                offset += sizeof(UInt32);
                IntPtr relpidl = (IntPtr)((int)p + (UInt32)Marshal.ReadInt32(p, offset));
                IntPtr abspidl = ILCombine(parentpidl, relpidl);
                SHFILEINFO sf = new SHFILEINFO();
                if (SHGetFileInfo(abspidl, 0, ref sf, Marshal.SizeOf(sf), SHGFI.PIDL | SHGFI.DISPLAYNAME | SHGFI.TYPENAME).ToInt32() > 0)
                {
                    MessageBox.Show(sf.szDisplayName);
                }

                ILFree(abspidl);
            }
        }
"Shell IDList Array"的構建與解析用到了下面的代碼:(參考帖子:http://www.cnblogs.com/feiyun0112/archive/2009/02/09/1386802.html)
        [DllImport("shell32.dll")]
        public static extern IntPtr ILCombine(IntPtr pidl1, IntPtr pidl2);
        [DllImport("shell32.dll")]
        public static extern void ILFree(IntPtr pidl);

        public enum SFGAO : uint
        {
            BROWSABLE = 0x8000000,
            CANCOPY = 1,
            CANDELETE = 0x20,
            CANLINK = 4,
            CANMONIKER = 0x400000,
            CANMOVE = 2,
            CANRENAME = 0x10,
            CAPABILITYMASK = 0x177,
            COMPRESSED = 0x4000000,
            CONTENTSMASK = 0x80000000,
            DISPLAYATTRMASK = 0xfc000,
            DROPTARGET = 0x100,
            ENCRYPTED = 0x2000,
            FILESYSANCESTOR = 0x10000000,
            FILESYSTEM = 0x40000000,
            FOLDER = 0x20000000,
            GHOSTED = 0x8000,
            HASPROPSHEET = 0x40,
            HASSTORAGE = 0x400000,
            HASSUBFOLDER = 0x80000000,
            HIDDEN = 0x80000,
            ISSLOW = 0x4000,
            LINK = 0x10000,
            NEWCONTENT = 0x200000,
            NONENUMERATED = 0x100000,
            READONLY = 0x40000,
            REMOVABLE = 0x2000000,
            SHARE = 0x20000,
            STORAGE = 8,
            STORAGEANCESTOR = 0x800000,
            STORAGECAPMASK = 0x70c50008,
            STREAM = 0x400000,
            VALIDATE = 0x1000000
        }
        public const int MAX_PATH = 260;

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct SHFILEINFO
        {
            public IntPtr hIcon;
            public int iIcon;
            public SFGAO dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
            public string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            public string szTypeName;
        }

        public enum SHGFI : uint
        {
            ADDOVERLAYS = 0x20,
            ATTR_SPECIFIED = 0x20000,
            ATTRIBUTES = 0x800,
            DISPLAYNAME = 0x200,
            EXETYPE = 0x2000,
            ICON = 0x100,
            ICONLOCATION = 0x1000,
            LARGEICON = 0,
            LINKOVERLAY = 0x8000,
            OPENICON = 2,
            OVERLAYINDEX = 0x40,
            PIDL = 8,
            SELECTED = 0x10000,
            SHELLICONSIZE = 4,
            SMALLICON = 1,
            SYSICONINDEX = 0x4000,
            TYPENAME = 0x400,
            USEFILEATTRIBUTES = 0x10
        }

        public enum FILE_ATTRIBUTE
        {
            READONLY = 0x00000001,
            HIDDEN = 0x00000002,
            SYSTEM = 0x00000004,
            DIRECTORY = 0x00000010,
            ARCHIVE = 0x00000020,
            DEVICE = 0x00000040,
            NORMAL = 0x00000080,
            TEMPORARY = 0x00000100,
            SPARSE_FILE = 0x00000200,
            REPARSE_POINT = 0x00000400,
            COMPRESSED = 0x00000800,
            OFFLINE = 0x00001000,
            NOT_CONTENT_INDEXED = 0x00002000,
            ENCRYPTED = 0x00004000
        }

        [DllImport("shell32", EntryPoint = "SHGetFileInfo", ExactSpelling = false, CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr SHGetFileInfo(IntPtr ppidl, FILE_ATTRIBUTE dwFileAttributes, ref SHFILEINFO sfi, int cbFileInfo, SHGFI uFlags);

效果截圖:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章