2009-07-27 14 views
5

Voglio abilitare il trascinamento dalla nostra applicazione basata su Windows Form a Esplora risorse. Il grosso problema: i file sono archiviati in un database, quindi ho bisogno di utilizzare il rendering dei dati ritardato. C'è un article on codeproject.com, ma l'autore sta usando un oggetto H_GLOBAL che porta a problemi di memoria con file più grandi di aprox. 20 MB. Non ho trovato una soluzione funzionante per l'utilizzo di un oggetto IStream invece. Penso che questo dovrebbe essere possibile implementare, perché questo non è un caso insolito. (Un programma FTP necessita anche di questa funzione, ad esempio)Trascinare e rilasciare file virtuali utilizzando IStream

Modifica: È possibile ottenere un evento quando l'utente rilascia il file? Quindi potrei ad esempio copiarlo in temp e l'explorer lo otterrà da lì? Forse c'è un approccio alternativo per il mio problema ...

risposta

5

AFAIK, non c'è un articolo funzionante a riguardo per .net. Quindi dovresti scriverlo da solo, questo è un po 'complicato, perché la classe .net DataObject è limitata. Ho un esempio funzionante del compito opposto (accettare i file di rendering ritardati da explorer), ma è più semplice, perché non ho bisogno della propria implementazione IDataObject.

Così il vostro compito sarà:

  1. Trova lavoro implementazione IDataObject in .NET. Vi consiglio di guardare here (Shell Style Drag and Drop in .NET (WPF and WinForms))
  2. È inoltre necessario un wrapper IStream per il flusso gestito (è relativamente facile da implementare)
  3. Implementare il rendering ritardato utilizzando le informazioni da MSDN (Shell Clipboard Formats)

Questo è il punto di partenza, e, in generale, abbastanza informazioni per implementare tale funzionalità. Con un po 'di pazienza e diversi tentativi infruttuosi lo farai :)

Aggiornamento: Il seguente codice manca di molti metodi e funzioni necessari, ma la logica principale è qui.

// ... 

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

Grazie per la risposta. Ho già provato a implementarlo da solo, ma è piuttosto complicato. Sfortunatamente, Explorer.exe si spegne sempre. Ma ci riproverò e aggiornerò il mio post con qualche codice sorgente. – Simon

+0

Potresti darmi la tua implementazione? (Alltough fa il compito opposto, potrebbe essere utile) Grazie. – Simon

+0

puoi pubblicare il tuo "Ho un esempio funzionante del compito opposto (accettando i file di rendering ritardati da explorer)"? –

Problemi correlati