使用IStream拖放虚拟文件

Sim*_*mon 5 c# drag-and-drop

我想启用从基于Windows窗体的应用程序拖放到Windows资源管理器.最大的问题:文件存储在数据库中,因此我需要使用延迟数据渲染.有一篇关于codeproject.com文章,但作者正在使用一个H_GLOBAL对象,这会导致文件大于aprox的内存问题.20 MB.我没有找到使用IStream对象的工作解决方案.我认为必须有可能实施,因为这不是一个不寻常的情况.(例如,FTP程序也需要这样的功能)

编辑:当用户删除文件时是否可以获取事件?所以我可以将它复制到temp并且资源管理器从那里获取它?也许有一个替代方法来解决我的问题......

arb*_*ter 5

AFAIK,没有关于.net的工作文章.所以你应该自己编写,这有点复杂,因为.net DataObject类是有限的.我有相反任务的例子(接受来自资源管理器的延迟渲染文件),但它更容易,因为我不需要自己的IDataObject实现.

所以你的任务将是:

  1. 在.net中查找工作IDataObject实现.我建议你看看这里(在.NET中的Shell风格拖放(WPF和WinForms))
  2. 您还需要一个用于托管流的IStream包装器(它相对容易实现)
  3. 使用来自MSDN(Shell剪贴板格式)的信息实现延迟呈现

这是起点,并且通常有足够的信息来实现这样的功能.有点耐心和几次不成功的尝试你会做:)

更新:以下代码缺少许多必要的方法和功能,但主要逻辑在这里.

// ...

private static IEnumerable<IVirtualItem> GetDataObjectContent(System.Windows.Forms.IDataObject dataObject)
{
  if (dataObject == null)
    return null;

  List<IVirtualItem> Result = new List<IVirtualItem>();

  bool WideDescriptor = dataObject.GetDataPresent(ShlObj.CFSTR_FILEDESCRIPTORW);
  bool AnsiDescriptor = dataObject.GetDataPresent(ShlObj.CFSTR_FILEDESCRIPTORA);

  if (WideDescriptor || AnsiDescriptor)
  {
    IDataObject NativeDataObject = dataObject as IDataObject;
    if (NativeDataObject != null)
    {
      object Data = null;
      if (WideDescriptor)
        Data = dataObject.GetData(ShlObj.CFSTR_FILEDESCRIPTORW);
      else
        if (AnsiDescriptor)
          Data = dataObject.GetData(ShlObj.CFSTR_FILEDESCRIPTORA);

      Stream DataStream = Data as Stream;
      if (DataStream != null)
      {
        Dictionary<string, VirtualClipboardFolder> FolderMap =
          new Dictionary<string, VirtualClipboardFolder>(StringComparer.OrdinalIgnoreCase);

        BinaryReader Reader = new BinaryReader(DataStream);
        int Count = Reader.ReadInt32();
        for (int I = 0; I < Count; I++)
        {
          VirtualClipboardItem ClipboardItem;

          if (WideDescriptor)
          {
            FILEDESCRIPTORW Descriptor = ByteArrayHelper.ReadStructureFromStream<FILEDESCRIPTORW>(DataStream);
            if (((Descriptor.dwFlags & FD.FD_ATTRIBUTES) > 0) && ((Descriptor.dwFileAttributes & FileAttributes.Directory) > 0))
              ClipboardItem = new VirtualClipboardFolder(Descriptor);
            else
              ClipboardItem = new VirtualClipboardFile(Descriptor, NativeDataObject, I);
          }
          else
          {
            FILEDESCRIPTORA Descriptor = ByteArrayHelper.ReadStructureFromStream<FILEDESCRIPTORA>(DataStream);
            if (((Descriptor.dwFlags & FD.FD_ATTRIBUTES) > 0) && ((Descriptor.dwFileAttributes & FileAttributes.Directory) > 0))
              ClipboardItem = new VirtualClipboardFolder(Descriptor);
            else
              ClipboardItem = new VirtualClipboardFile(Descriptor, NativeDataObject, I);
          }

          string ParentFolder = Path.GetDirectoryName(ClipboardItem.FullName);
          if (string.IsNullOrEmpty(ParentFolder))
            Result.Add(ClipboardItem);
          else
          {
            VirtualClipboardFolder Parent = FolderMap[ParentFolder];
            ClipboardItem.Parent = Parent;
            Parent.Content.Add(ClipboardItem);
          }

          VirtualClipboardFolder ClipboardFolder = ClipboardItem as VirtualClipboardFolder;
          if (ClipboardFolder != null)
            FolderMap.Add(PathHelper.ExcludeTrailingDirectorySeparator(ClipboardItem.FullName), ClipboardFolder);
        }
      }
    }
  }

  return Result.Count > 0 ? Result : null;
}

// ...

public VirtualClipboardFile : VirtualClipboardItem, IVirtualFile
{
  // ...

public Stream Open(FileMode mode, FileAccess access, FileShare share, FileOptions options, long startOffset)
{
  if ((mode != FileMode.Open) || (access != FileAccess.Read))
    throw new ArgumentException("Only open file mode and read file access supported.");

  System.Windows.Forms.DataFormats.Format Format = System.Windows.Forms.DataFormats.GetFormat(ShlObj.CFSTR_FILECONTENTS);
  if (Format == null)
    return null;

  FORMATETC FormatEtc = new FORMATETC();
  FormatEtc.cfFormat = (short)Format.Id;
  FormatEtc.dwAspect = DVASPECT.DVASPECT_CONTENT;
  FormatEtc.lindex = FIndex;
  FormatEtc.tymed = TYMED.TYMED_ISTREAM | TYMED.TYMED_HGLOBAL;

  STGMEDIUM Medium;
  FDataObject.GetData(ref FormatEtc, out Medium);

  try
  {
    switch (Medium.tymed)
    {
      case TYMED.TYMED_ISTREAM:
        IStream MediumStream = (IStream)Marshal.GetTypedObjectForIUnknown(Medium.unionmember, typeof(IStream));
        ComStreamWrapper StreamWrapper = new ComStreamWrapper(MediumStream, FileAccess.Read, ComRelease.None);

        // Seek from beginning
        if (startOffset > 0)
          if (StreamWrapper.CanSeek)
            StreamWrapper.Seek(startOffset, SeekOrigin.Begin);
          else
          {
            byte[] Null = new byte[256];
            int Readed = 1;
            while ((startOffset > 0) && (Readed > 0))
            {
              Readed = StreamWrapper.Read(Null, 0, (int)Math.Min(Null.Length, startOffset));
              startOffset -= Readed;
            }
          }

        StreamWrapper.Closed += delegate(object sender, EventArgs e)
        {
          ActiveX.ReleaseStgMedium(ref Medium);
          Marshal.FinalReleaseComObject(MediumStream);
        };

        return StreamWrapper;
      case TYMED.TYMED_HGLOBAL:
        byte[] FileContent;

        IntPtr MediumLock = Windows.GlobalLock(Medium.unionmember);
        try
        {
          long Size = FSize.HasValue ? FSize.Value : Windows.GlobalSize(MediumLock).ToInt64();
          FileContent = new byte[Size];
          Marshal.Copy(MediumLock, FileContent, 0, (int)Size);
        }
        finally
        {
          Windows.GlobalUnlock(Medium.unionmember);
        }
        ActiveX.ReleaseStgMedium(ref Medium);

        Stream ContentStream = new MemoryStream(FileContent, false);
        ContentStream.Seek(startOffset, SeekOrigin.Begin);

        return ContentStream;
      default:
        throw new ApplicationException(string.Format("Unsupported STGMEDIUM.tymed ({0})", Medium.tymed));
    }
  }
  catch
  {
    ActiveX.ReleaseStgMedium(ref Medium);
    throw;
  }
}

// ...
Run Code Online (Sandbox Code Playgroud)