使用VirtualFileDataObject拖放IStream的大型虚拟文件
本文关键字:大型 虚拟 文件 IStream VirtualFileDataObject 拖放 使用 | 更新日期: 2023-09-27 17:54:44
我成功地使用了来自延迟博客的VirtualFileDataObject代码,但我想避免将整个文件流式传输到内存中。
我发现这个以前回答过的关于堆栈溢出的问题拖放大型虚拟文件从c#到Windows资源管理器这个问题是由matthieu回答的,通过改变SetData方法的签名。
这是我的问题,在改变SetData方法的签名后,其他调用它的地方仍然在寻找旧的签名。
这是原始的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建议改成;
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);
...
}
在我做了这些改变之后,下面的调用将不工作;(这就是我需要帮助的地方)
如何修复这个调用? foreach (var fileDescriptor in fileDescriptors)
{
**SetData(FILECONTENTS, index, fileDescriptor.StreamContents);**
index++;
}
基本上改变"Action streamData"到"Stream Stream"是导致我的问题。我不确定如何调用它后的变化。
所有这些代码都来自延迟VirtualFileDataObject。我不知道我是否应该把它贴在这里。但是如果你点击上面的链接,它会把你带到博客,这样你就可以看到它了。
我已经很接近了,只是不能想出最后一步,谢谢你的关注
我也遇到过同样的问题。以下是我为解决这个问题所做的(正如你所说的,在另一个答案中没有完全解决)
1)修改FileDescriptor
的StreamContents
属性:
public Action<Stream> StreamContents { get; set; }
:
public Func<Stream> StreamContents { get; set; }
(而不是传递客户端可以写入的Stream
,我们将期望可以从中读取的Stream
,这正是Explorer的工作方式和期望)
2)修改SetData
方法重载:
public void SetData(short dataFormat, int index, Action<Stream> streamData)
:
public void SetData(short dataFormat, int index, Func<Stream> streamData)
3)更改SetData
代码的GetData
lambda为:
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
类)
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):
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;
}
};