c# - 将大型虚拟文件从 C# 拖放到 Windows 资源管理器

标签 c# com drag-and-drop

我有一个 C# WPF 应用程序,其中有一个部分用作 FTP 客户端,列出远程服务器上的文件并允许用户下载它们。我希望用户能够将文件列表中的文件拖放到他们自己的计算机上(即拖放到 Windows 资源管理器外壳中)。

为此,我使用了 VirtualFileDataObject code from Delay's blog , 使用 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);
                },
            });
    }

特别是这一段GetData是罪魁祸首:

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

streamDataAction<stream>我提供将实际文件数据写入流的方法。我的委托(delegate)只是打开一个文件并将字节读入提供的流中。

有没有办法避免这最后一步,也许以某种方式直接传递文件流以供 Explorer shell 从中读取?我在想类似替换 iStream 的东西带有指向 .NET 文件流的指针我已经...但是我对 COM 互操作的了解还不够,甚至不知道这样做的语法。任何提示/方向将不胜感激!

最佳答案

经过更多的谷歌搜索和跌跌撞撞,不断尝试,我找到了一些可行的方法,但我仍然愿意接受更好的解决方案。现在,当删除操作发生时,我将文件检索到一个临时位置,然后使用 SHCreateStreamOnFileEx打开指向该位置的 IStream。修改后的部分 GetData lambda 如下:

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);
}

正如我所说,我不知道这是否是最好的方法,或者我是否可以更干净地管理它,但这似乎有效。

关于c# - 将大型虚拟文件从 C# 拖放到 Windows 资源管理器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12410114/

相关文章:

c# - 如何在c#中动态设置数组长度

c# - 如何在 .NET 2003 中编写十六进制和二进制常量

windows - 完全从头开始编写 COM 对象?

drag-and-drop - 是否可以关闭vscode中的拖放功能?

javascript - Angular 拖放列出单独容器中的项目

c# - Swashbuckle.AspNetCore v1.0.0,带 OAuth2,流程 : application -> IdentityServer4

c# - NHibernate Validator 一个值或另一个

c++ - 从非提升的进程连接到提升的 COM 服务器

c++ - 使用 ATL 正确关闭在单独线程上创建的窗口

c# - 为什么鼠标右键单击时拖放不起作用?