2009-07-27 53 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