I have a C# WPF application with a section that serves as an FTP client, listing files on a remote server and allowing the user to download them. I want the user to be able to drag and drop files from the file listing onto their own machine (i.e. into a Windows Explorer shell).
To accomplish this, I used the VirtualFileDataObject code from Delay's blog, using the Action<Stream>
overload of SetData
. This works great on smaller files.
My problem is: some of the files I'm dealing with are very large (2+ GB), and the way the VirtualFileDataObject
class handles the stream involves reading the entire thing into memory, which can end up throwing a "not enough storage" error for those very large files.
The relevant section of the VirtualFileDataObject
code is below. How can I rewrite this code to not require the entire stream to be in memory?
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);
},
});
}
In particular, this section of GetData
is the culprit:
// Wrap in a .NET-friendly Stream and call provided code to fill it
using (var stream = new IStreamWrapper(iStream)) {
streamData(stream);
}
streamData
is the Action<stream>
I provide which writes the actual file data to the stream. My delegate is just opening a file and reading the bytes into the provided stream.
Is there a way to avoid this last step, perhaps somehow passing the file stream directly to be read from by the Explorer shell? I'm thinking something like replacing iStream
with a pointer to the .NET filestream I've got...but I don't know enough about COM interop to even know the syntax for doing that. Any tips/direction would be appreciated!