使用IStream拖放虚拟文件

时间:2009-07-27 10:19:51

标签: c# drag-and-drop

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

编辑:当用户删除文件时是否可以获取活动?所以我可以将它复制到temp并且资源管理器从那里获取它?也许对我的问题有另一种方法......

2 个答案:

答案 0 :(得分:5)

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

所以你的任务将是:

  1. 在.net中查找工作IDataObject实现。我建议你看here (Shell Style Drag and Drop in .NET (WPF and WinForms))
  2. 您还需要一个用于托管流的IStream包装器(它相对容易实现)
  3. 使用MSDN (Shell Clipboard Formats)
  4. 中的信息实施延迟呈现

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

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

    // ...
    
    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;
      }
    }
    
    // ...
    

答案 1 :(得分:0)

Google员工可能会觉得这很有用:download a file using windows IStream