使用 VirtualFileDataObject 使用 IStream 拖放大型虚拟文件

时间:2023-04-27
本文介绍了使用 VirtualFileDataObject 使用 IStream 拖放大型虚拟文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

我已成功使用 Delay 博客中的 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 资源管理器 matthieu 通过更改 SetData 方法的签名回答了这个问题.

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) 修改 FileDescriptorStreamContents 属性:

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)

到这里:

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 拖放大型虚拟文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一篇:WPF/C#: 禁用拖动 &amp;删除文本框? 下一篇:在 Windows 资源管理器上获取拖放文件的文件路径

相关文章