2012-09-13 49 views
4

我有一個C#WPF應用程序,其中有一節用作FTP客戶端,列出遠程服務器上的文件並允許用戶下載它們。我希望用戶能夠將文件從文件列表中拖放到他們自己的機器上(即進入Windows資源管理器的shell)。將C#中的大型虛擬文件拖放到Windows資源管理器中

爲了實現這個目標,我使用了VirtualFileDataObject code from Delay's blog,使用的SetData過載。這對小文件很有效。

我的問題是:我正在處理的一些文件非常大(2+ GB),VirtualFileDataObject類處理流的方式涉及將整個事件讀入內存,最終可能會拋出「沒有足夠的存儲「錯誤,這些非常大的文件。

VirtualFileDataObject代碼的相關部分如下。 我該如何重寫這段代碼,不要求整個流在內存中?

public void SetData(short dataFormat, int index, Action<Stream> streamData) { 
     _dataObjects.Add(
      new DataObject { 
       FORMATETC = new FORMATETC { 
        cfFormat = dataFormat, 
        ptd = IntPtr.Zero, 
        dwAspect = DVASPECT.DVASPECT_CONTENT, 
        lindex = index, 
        tymed = TYMED.TYMED_ISTREAM 
       }, 
       GetData =() => { 
        // Create IStream for data 
        var ptr = IntPtr.Zero; 
        var iStream = NativeMethods.CreateStreamOnHGlobal(IntPtr.Zero, true); 
        if (streamData != null) { 
         // Wrap in a .NET-friendly Stream and call provided code to fill it 
         using (var stream = new IStreamWrapper(iStream)) { 
          streamData(stream); 
         } 
        } 
        // Return an IntPtr for the IStream 
        ptr = Marshal.GetComInterfaceForObject(iStream, typeof(IStream)); 
        Marshal.ReleaseComObject(iStream); 
        return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK); 
       }, 
      }); 
    } 

特別地,GetData此部分是罪魁禍首:

// Wrap in a .NET-friendly Stream and call provided code to fill it 
using (var stream = new IStreamWrapper(iStream)) { 
    streamData(stream); 
} 

streamDataAction<stream>我提供一種實際的文件數據寫入到流。我的委託只是打開一個文件並將字節讀入提供的流中。

有沒有辦法避免這最後一步,或許以某種方式直接傳遞文件流以通過資源管理器shell讀取?我想用一個指向.NET文件流的指針代替iStream ...但我對COM互操作知之甚少,甚至不知道這樣做的語法。任何提示/方向將不勝感激!

回答

2

我得到了同樣的問題,很容易解決,雖然;)

的問題是,我們正在創造一個新的內存流,而不是需要它,因爲我們已經有我們的。您可以在C#中創建一個實現的IStream的流包裝:

/// <summary> 
/// Simple class that exposes a read-only Stream as a IStream. 
/// </summary> 
private class StreamWrapper : IStream 
{ 

    private Stream _stream; 

    public StreamWrapper(Stream stream) 

    { 
     _stream = stream; 
    } 

    public void Read(byte[] pv, int cb, System.IntPtr pcbRead) 

    { 
     Marshal.WriteInt32(pcbRead, _stream.Read(pv, 0, cb)); 
    } 

    public void Seek(long dlibMove, int dwOrigin, System.IntPtr plibNewPosition) 

    { 
     Marshal.WriteInt32(plibNewPosition, (int)_stream.Seek(dlibMove, (SeekOrigin)dwOrigin)); 
    } 

    public void Clone(out IStream ppstm) 

    { 
     throw new NotImplementedException(); 
    } 

    public void Commit(int grfCommitFlags) 

    { 
     throw new NotImplementedException(); 
    } 

    public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten) 

    { 
     throw new NotImplementedException(); 
    } 

    public void LockRegion(long libOffset, long cb, int dwLockType) 

    { 
     throw new NotImplementedException(); 
    } 

    public void Revert() 

    { 
     throw new NotImplementedException(); 
    } 

    public void SetSize(long libNewSize) 

    { 
     throw new NotImplementedException(); 
    } 

    public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag) 

    { 
     throw new NotImplementedException(); 
    } 

    public void UnlockRegion(long libOffset, long cb, int dwLockType) 

    { 
     throw new NotImplementedException(); 
    } 

    public void Write(byte[] pv, int cb, IntPtr pcbWritten) 

    { 
     throw new NotImplementedException(); 
    } 
} 

然後在VirtualFileDataObject類,改變SetData方法的簽名,讓你現在通過一個Stream:

public void SetData(short dataFormat, int index, Stream stream) 
{ 
    ... 
    var iStream = new StreamWrapper(stream); 
    ... 
    // Ensure the following line is commented out: 
    //Marshal.ReleaseComObject(iStream); 
    return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK); 
... 
} 

現在,沒有新的內存流將被創建。

如需進一步信息,請訪問http://blogs.msdn.com/b/delay/archive/2009/11/04/creating-something-from-nothing-asynchronously-developer-friendly-virtual-file-implementation-for-net-improved.aspx#10496772和閱讀我的意見

1

經過更多的谷歌搜索和絆腳石,嘗試一件又一件,我已經有了一些工作,但我仍然開放更好的解決方案。現在,當發生拖放操作時,我正在將文件檢索到臨時位置,然後使用SHCreateStreamOnFileEx打開到該位置的IStream。修訂後的一部分,GetData拉姆達如下:

GetData =() => { 
    var filename = getFilename(); 

    IStream stream = null; 
    NativeMethods.SHCreateStreamOnFileEx(filename, NativeMethods.STGM_FAILIFTHERE, NativeMethods.FILE_ATTRIBUTE_NORMAL, false, null, ref stream); 
    var ptr = Marshal.GetComInterfaceForObject(stream, typeof(IStream)); 
    Marshal.ReleaseComObject(stream); 
    return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK); 
} 

正如我說的,我不知道這是否是做的最好的辦法,或者如果我可以更清晰地管理它,但這似乎工作。

+0

僅供參考我在這裏回答了同樣的問題:HTTP://計算器。COM /問題/ 22700195 /拖和下拉式大虛擬文件與 - istream的-使用,virtualfiledataobject –

相關問題