2009-07-27 72 views
5

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

编辑:当用户丢弃文件时是否可以获取事件?那么我可以将它复制到临时文件夹中,然后探险者从那里获取它?也许有我的问题的替代方法...

回答

5

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

所以,你的任务是:

  1. 查找.NET的IDataObject实现。我建议你看看here (Shell Style Drag and Drop in .NET (WPF and WinForms))
  2. 您还需要管理数据流的IStream的包装(它是相对容易实现)
  3. 使用信息,从MSDN (Shell Clipboard Formats)

实施延迟渲染这是出发点,和一般足够的信息来实现这种功能。有点耐心和几次不成功的尝试,你会这样做:)

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

// ... 

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

// ... 
+0

谢谢您的回答。我已经试图自己实现它,但它非常棘手。不幸的是,Explorer.exe总是关闭。但我会再试一次,并用一些源代码更新我的帖子。 – Simon 2009-07-27 13:13:35