Mat*_*ler 5 c# com drag-and-drop
我有一个C#WPF应用程序,其中一个部分用作FTP客户端,列出远程服务器上的文件并允许用户下载它们.我希望用户能够将文件列表中的文件拖放到他们自己的机器上(即进入Windows资源管理器外壳).
为了实现这一点,我使用了Delay的博客中的VirtualFileDataObject代码,使用了Action<Stream>重载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);
},
});
}
Run Code Online (Sandbox Code Playgroud)
特别是,这部分GetData是罪魁祸首:
// Wrap in a .NET-friendly Stream and call provided code to fill it
using (var stream = new IStreamWrapper(iStream)) {
streamData(stream);
}
Run Code Online (Sandbox Code Playgroud)
streamData是Action<stream>我提供的将实际文件数据写入流.我的代表只是打开一个文件并将字节读入提供的流中.
有没有办法避免这最后一步,也许以某种方式直接传递文件流以便从Explorer shell读取?我正在考虑iStream用一个指向我已经获得的.NET文件流的指针替换...但我对COM互操作知之甚少,甚至不知道这样做的语法.任何提示/方向将不胜感激!
小智 2
我遇到了同样的问题,但很容易解决;)
问题是我们正在创建一个新的内存流,但实际上不需要它,因为我们已经有了自己的内存流。您可以在 c# 中创建一个实现 IStream 的 Stream 包装器:
/// <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();
}
}
Run Code Online (Sandbox Code Playgroud)
然后在 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);
...
}
Run Code Online (Sandbox Code Playgroud)
现在,不会创建新的内存流。
| 归档时间: |
|
| 查看次数: |
2941 次 |
| 最近记录: |