使用VirtualFileDataObject使用IStream拖放大型虚拟文件 [英] Drag and Drop large virtual files with IStream using VirtualFileDataObject
问题描述
我已成功使用来自延迟博客的VirtualFileDataObject代码,但是我想避免将整个文件流入内存。
I am successfully using VirtualFileDataObject code from Delay's blog, but i want to avoid streaming the entire file into memory.
我发现这个以前在Stack Overflow中回答的问题将大型虚拟文件从c#拖放到Windows资源管理器通过更改SetData方法的签名,matthieu回答了问题。
I found this previously answered question on Stack Overflow Drag and Drop large virtual files from c# to Windows Explorer The question was answered by matthieu, by changing the signature of the SetData method.
这是我的问题,在更改SetData方法的签名后,调用它的其他地方仍在寻找旧签名。
Here is my problem, after changing the signature of the SetData method, other places that call it are still looking for the old signature.
这是原始的SetData;
Here is the original SetData;
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);
},
});
}
matthieu建议将其更改为;
matthieu suggested to change it to;
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);
...
}
在进行这些更改后,以下调用将不行; (这是我需要帮助的地方)
我如何修复此调用;
After I make these changes the following call will not work; ( and this is where i need help) How do i fix this call;
foreach (var fileDescriptor in fileDescriptors)
{
**SetData(FILECONTENTS, index, fileDescriptor.StreamContents);**
index++;
}
基本上改变Action streamData到Stream stream导致我的问题。我不确定在更改后如何调用它。
Basically changing "Action streamData" To "Stream stream" is causing my problems. I am not sure on how to call it after the changes are made.
所有这些代码都来自Delays VirtualFileDataObject。我不知道我是否应该在这里发布。但是,如果你按照上面的链接,它将带你到博客,所以你可以查看。
All this code comes from Delays VirtualFileDataObject. I don't know if i should post it on here or not. But if you follow the link above it will take you to the blog so you can view it.
我很亲近,只是不能想象这最后一步,感谢您查看
I am so close, just can't figure this last step out, thanks for taking a look
推荐答案
我有完全相同的问题。这是我做了什么来解决这个问题(正如你所说没有在另一个答案中完全解决)
I've had exactly the same problem. Here is what I did to fix this issue (which as you say has not been fully addressed in the other answer)
1)修改 FileDescriptor
的 StreamContents
属性从此:
1) Modify FileDescriptor
's StreamContents
property from this:
public Action<Stream> StreamContents { get; set; }
到:
public Func<Stream> StreamContents { get; set; }
(而不是传递一个 Stream
客户端可以写,我们会期待一个 Stream
,我们可以从中读取,这正是Explorer的工作原理以及预期的内容。)
(instead of passing a Stream
the client can write, we'll expect a Stream
we can read from, which is exactly how Explorer works and what it expects)
2)从此修改 SetData
方法重载:
2) Modify the SetData
method overload from this:
public void SetData(short dataFormat, int index, Action<Stream> streamData)
/ p>
to this:
public void SetData(short dataFormat, int index, Func<Stream> streamData)
3)更改 SetData
代码 GetData
lambda到:
3) change SetData
code's GetData
lambda to this:
GetData = () =>
{
ManagedIStream istream = null;
if (streamData != null)
{
Stream stream = streamData();
if (stream != null)
{
istream = new ManagedIStream(stream);
}
}
IntPtr ptr = istream != null ? Marshal.GetComInterfaceForObject(istream, typeof(IStream)) : IntPtr.Zero;
return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
},
4)添加这个 ManagedIStream
类代码(您也可以完全删除 IStreamWrapper
类)
4) add this ManagedIStream
class to the code (you can also delete the IStreamWrapper
class completely)
private class ManagedIStream : IStream
{
private Stream _stream;
public ManagedIStream(Stream stream)
{
_stream = stream;
}
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 Read(byte[] pv, int cb, IntPtr pcbRead)
{
int read = _stream.Read(pv, 0, cb);
if (pcbRead != IntPtr.Zero)
{
Marshal.WriteInt32(pcbRead, read);
}
}
public void Revert()
{
throw new NotImplementedException();
}
public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
{
long newPos = _stream.Seek(dlibMove, (SeekOrigin)dwOrigin);
if (plibNewPosition != IntPtr.Zero)
{
Marshal.WriteInt64(plibNewPosition, newPos);
}
}
public void SetSize(long libNewSize)
{
_stream.SetLength(libNewSize);
}
public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
{
const int STGTY_STREAM = 2;
pstatstg = new System.Runtime.InteropServices.ComTypes.STATSTG();
pstatstg.type = STGTY_STREAM;
pstatstg.cbSize = _stream.Length;
pstatstg.grfMode = 0;
if (_stream.CanRead && _stream.CanWrite)
{
const int STGM_READWRITE = 0x00000002;
pstatstg.grfMode |= STGM_READWRITE;
return;
}
if (_stream.CanRead)
{
const int STGM_READ = 0x00000000;
pstatstg.grfMode |= STGM_READ;
return;
}
if (_stream.CanWrite)
{
const int STGM_WRITE = 0x00000001;
pstatstg.grfMode |= STGM_WRITE;
return;
}
throw new IOException();
}
public void UnlockRegion(long libOffset, long cb, int dwLockType)
{
throw new NotImplementedException();
}
public void Write(byte[] pv, int cb, IntPtr pcbWritten)
{
_stream.Write(pv, 0, cb);
if (pcbWritten != IntPtr.Zero)
{
Marshal.WriteInt32(pcbWritten, cb);
}
}
}
就是这样。现在,您可以使用这样的代码(使用与原始文章相同的示例: http:// dlaa.me/blog/post/9913083 ):
That's it. Now you can use the code like this (using the same sample as in the original article available here: http://dlaa.me/blog/post/9913083):
new VirtualFileDataObject.FileDescriptor
{
Name = "Alphabet.txt",
Length = 26,
ChangeTimeUtc = DateTime.Now.AddDays(-1),
StreamContents = () =>
{
var contents = Enumerable.Range('a', 26).Select(i => (byte)i).ToArray();
MemoryStream ms = new MemoryStream(contents); // don't dispose/using here, it would be too early
return ms;
}
};
这篇关于使用VirtualFileDataObject使用IStream拖放大型虚拟文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!