Top / JUNK / VirtualDataObject

仮想ファイルをエクスプローラにドロップするための DataObject です。

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;
using ComTypes = System.Runtime.InteropServices.ComTypes;
using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;

class VirtualDataObject : IComDataObject
{
    protected readonly DataObject dataObject = new DataObject();

    protected IComDataObject comDataObject {
        get {
            return dataObject;
        }
    }

    protected readonly List<DataEntry> dataEntries = new List<DataEntry>();

    protected struct DataEntry
    {
        public string VirtualPath;
        public Stream Stream;
    }

    public VirtualDataObject() { }

    public VirtualDataObject(StringCollection filePaths) {
        dataObject.SetFileDropList(filePaths);
    }

    public void AddVirtualPath(string virtualPath, string fileName) {
        AddVirtualPath(virtualPath, (new FileInfo(fileName).OpenRead()));
    }

    public void AddVirtualPath(string virtualPath, Stream stream) {
        if (!dataObject.GetDataPresent(NativeMethods.CFSTR_FILEDESCRIPTORW)) {
            dataObject.SetData(NativeMethods.CFSTR_FILEDESCRIPTORW, new MemoryStream());
        }
        var entry = new DataEntry();
        entry.VirtualPath = virtualPath;
        entry.Stream = stream;
        dataEntries.Add(entry);
        dataObject.SetData(NativeMethods.CFSTR_FILECONTENTS, stream);
    }

    private void SetFileDescriptor() {
        var mem = (MemoryStream)dataObject.GetData(NativeMethods.CFSTR_FILEDESCRIPTORW);
        mem.Position = 0;
        // FILEGROUPDESCRIPTOR.cItems をセット
        var bytes = BitConverter.GetBytes(dataEntries.Count);
        mem.Write(bytes, 0, bytes.Length);
        // FILEGROUPDESCRIPTOR.fgd[i] をセット
        var fd = new NativeMethods.FILEDESCRIPTORW();
        foreach (var entry in dataEntries) {
            fd.cFileName = entry.VirtualPath;
            fd.dwFlags =
                    NativeMethods.FileDescriptorFlags.FD_PROGRESSUI |
                    NativeMethods.FileDescriptorFlags.FD_UNICODE |
                    NativeMethods.FileDescriptorFlags.FD_FILESIZE;
            fd.nFileSizeHigh = (uint)(entry.Stream.Length >> 32);
            fd.nFileSizeLow = (uint)(entry.Stream.Length & 0xFFFFFFFF);
            bytes = NativeMethods.StructureToBytes(ref fd);
            mem.Write(bytes, 0, bytes.Length);
        }
    }

    public bool IsDropRecycleBin { get; set; }

    private void SetDataTargetClsId(ref STGMEDIUM medium) {
        if (medium.tymed == TYMED.TYMED_HGLOBAL) {
            var hGlobal = medium.unionmember;
            var lp = NativeMethods.GlobalLock(hGlobal);
            try {
                var guid = Marshal.PtrToStructure<Guid>(lp);
                IsDropRecycleBin = (guid == NativeMethods.CLSID_RecycleBin);
            } finally {
                NativeMethods.GlobalUnlock(hGlobal);
            }
        }
    }

    [DebuggerStepThrough]
    void IComDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium) {
        if (format.cfFormat == NativeMethods.CF_FILEDESCRIPTORW) {
            SetFileDescriptor();
        }
        comDataObject.GetData(ref format, out medium);
    }

    [DebuggerStepThrough]
    void IComDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium) {
        comDataObject.GetDataHere(ref format, ref medium);
    }

    [DebuggerStepThrough]
    int IComDataObject.QueryGetData(ref FORMATETC format) {
        return comDataObject.QueryGetData(ref format);
    }

    [DebuggerStepThrough]
    int IComDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut) {
        return comDataObject.GetCanonicalFormatEtc(ref formatIn, out formatOut);
    }

    [DebuggerStepThrough]
    void IComDataObject.SetData(ref FORMATETC format, ref STGMEDIUM medium, bool release) {
        if (format.cfFormat == NativeMethods.CF_TARGETCLSID) {
            SetDataTargetClsId(ref medium);
        }
        comDataObject.SetData(ref format, ref medium, release);
    }

    [DebuggerStepThrough]
    IEnumFORMATETC IComDataObject.EnumFormatEtc(DATADIR direction) {
        return comDataObject.EnumFormatEtc(direction);
    }

    [DebuggerStepThrough]
    int IComDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection) {
        return comDataObject.DAdvise(ref pFormatetc, advf, adviseSink, out connection);
    }

    [DebuggerStepThrough]
    void IComDataObject.DUnadvise(int connection) {
        comDataObject.DUnadvise(connection);
    }

    [DebuggerStepThrough]
    int IComDataObject.EnumDAdvise(out IEnumSTATDATA enumAdvise) {
        return comDataObject.EnumDAdvise(out enumAdvise);
    }

    private static class NativeMethods
    {
        public const int MAX_PATH = 260;

        public const string CFSTR_FILEDESCRIPTORW = "FileGroupDescriptorW";
        public const string CFSTR_FILECONTENTS = "FileContents";
        public const string CFSTR_PERFORMEDDROPEFFECT = "Performed DropEffect";
        public const string CFSTR_LOGICALPERFORMEDDROPEFFECT = "Logical Performed DropEffect";
        public const string CFSTR_DROPDESCRIPTION = "DropDescription";
        public const string CFSTR_TARGETCLSID = "TargetCLSID";

        public static short CF_FILEDESCRIPTORW = GetDataFormat(CFSTR_FILEDESCRIPTORW);
        public static short CF_FILECONTENTS = GetDataFormat(CFSTR_FILECONTENTS);
        public static short CF_PERFORMEDDROPEFFECT = GetDataFormat(CFSTR_PERFORMEDDROPEFFECT);
        public static short CF_LOGICALPERFORMEDDROPEFFECT = GetDataFormat(CFSTR_LOGICALPERFORMEDDROPEFFECT);
        public static short CF_DROPDESCRIPTION = GetDataFormat(CFSTR_DROPDESCRIPTION);
        public static short CF_TARGETCLSID = GetDataFormat(CFSTR_TARGETCLSID);

        public const string CSTR_RecycleBin = "645FF040-5081-101B-9F08-00AA002F954E";
        public const string CSTR_ShellFolder = "F3364BA0-65B9-11CE-A9BA-00AA004AE837";

        public static Guid CLSID_RecycleBin = new Guid(CSTR_RecycleBin);
        public static Guid CLSID_ShellFolder = new Guid(CSTR_ShellFolder);

        public static short GetDataFormat(string format) {
            var fmt = DataFormats.GetFormat(format);
            return unchecked((short)fmt.Id);
        }

        public static string GetDataFormatName(short format) {
            var fmt = DataFormats.GetFormat(unchecked((ushort)format));
            return fmt.Name;
        }

        [Flags]
        public enum FileDescriptorFlags : uint
        {
            FD_CLSID = 0x00000001,
            FD_SIZEPOINT = 0x00000002,
            FD_ATTRIBUTES = 0x00000004,
            FD_CREATETIME = 0x00000008,
            FD_ACCESSTIME = 0x00000010,
            FD_WRITESTIME = 0x00000020,
            FD_FILESIZE = 0x00000040,
            FD_PROGRESSUI = 0x00004000,
            FD_LINKUI = 0x00008000,
            FD_UNICODE = 0x80000000
        }

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct FILEDESCRIPTORW
        {
            public FileDescriptorFlags dwFlags;
            public Guid clsid;
            public Size sizel;
            public Point pointl;
            public uint dwFileAttributes;
            public ComTypes.FILETIME ftCreationTime;
            public ComTypes.FILETIME ftLastAccessTime;
            public ComTypes.FILETIME ftLastWriteTime;
            public uint nFileSizeHigh;
            public uint nFileSizeLow;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH)]
            public string cFileName;
        }

        public static byte[] StructureToBytes<T>(ref T value) where T : struct {
            var nSize = Marshal.SizeOf(typeof(T));
            var lpAddr = Marshal.AllocCoTaskMem(nSize);
            try {
                Marshal.StructureToPtr(value, lpAddr, true);
                var bytes = new byte[nSize];
                Marshal.Copy(lpAddr, bytes, 0, bytes.Length);
                return bytes;

            } finally {
                Marshal.FreeCoTaskMem(lpAddr);
            }
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr GlobalLock(IntPtr hMem);

        [DllImport("kernel32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool GlobalUnlock(IntPtr hMem);
    }
}

使い方

    using System;
    using System.Drawing;
    using System.Collections.Specialized;
    using System.Windows.Forms;

    static class Program
    {
        [STAThread]
        static void Main() {
            var f = new Form();

            f.SuspendLayout();
            f.ClientSize = new Size(300, 150);

            var lbl1 = CreateLabel("RealPath");
            lbl1.Dock = DockStyle.Fill;
            lbl1.MouseDown += Lbl1_MouseDown;
            f.Controls.Add(lbl1);

            var lbl2 = CreateLabel("VirtualPath");
            lbl2.Dock = DockStyle.Right;
            lbl2.MouseDown += Lbl2_MouseDown;
            f.Controls.Add(lbl2);

            f.ResumeLayout();

            Application.Run(f);
        }

        private static Label CreateLabel(string text) {
            var lbl = new Label();
            lbl.Text = text;
            lbl.AutoSize = false;
            lbl.BorderStyle = BorderStyle.FixedSingle;
            lbl.Size = new Size(150, 150);
            lbl.TextAlign = ContentAlignment.MiddleCenter;
            return lbl;
        }

        private static void Lbl1_MouseDown(object sender, MouseEventArgs e) {
            // パスを渡す
            var filePaths = new StringCollection();
            filePaths.Add(@"E:\work\bigdata.txt");
            var data = new VirtualDataObject(filePaths);
            ((Control)sender).DoDragDrop(data, DragDropEffects.Copy | DragDropEffects.Move);
        }

        private static void Lbl2_MouseDown(object sender, MouseEventArgs e) {
            // 仮想ファイルを渡す
            var data = new VirtualDataObject();
            data.AddVirtualPath("test1.dat", @"E:\work\test1.dat");
            var mem = new MemoryStream();
            var bytes = new byte[] { 0x31, 0x32, 0x33 };
            mem.Write(bytes, 0, bytes.Length);
            data.AddVirtualPath("test2.dat", mem);
            ((Control)sender).DoDragDrop(data, DragDropEffects.Copy);
        }
    }



トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   最終更新のRSS
Last-modified: 2020-03-30 (月) 15:15:14 (57d)