使用IStream拖放虚拟文件

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

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

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

所以你的任务将是:

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

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

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

// ... private static IEnumerable GetDataObjectContent(System.Windows.Forms.IDataObject dataObject) { if (dataObject == null) return null; List Result = new List(); 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 FolderMap = new Dictionary(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(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(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; } } // ... 

Google员工可能会觉得这很有用: 使用Windows IStream下载文件