【问题标题】:Drag and Drop large virtual files with IStream using VirtualFileDataObject使用 VirtualFileDataObject 使用 IStream 拖放大型虚拟文件
【发布时间】:2014-03-27 22:04:45
【问题描述】:

我已成功使用VirtualFileDataObject code from Delay's blog,但我想避免将整个文件流式传输到内存中。

我在 Stack Overflow Drag and Drop large virtual files from c# to Windows Explorer 上找到了这个先前回答的问题,这个问题由 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”导致了我的问题。我不确定在更改后如何调用它。

所有这些代码都来自Delays VirtualFileDataObject。不知道该不该发在这里。但是,如果您点击上面的链接,它会将您带到博客,以便您查看它。

我已经很接近了,只是想不出最后一步,谢谢你看看

【问题讨论】:

    标签: c# wpf drag-and-drop


    【解决方案1】:

    我也遇到过同样的问题。这是我为解决此问题所做的工作(正如您所说,其他答案中尚未完全解决)

    1) 从此修改FileDescriptorStreamContents 属性:

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

    【讨论】:

    • @DavidRefaeli - 当另一端的调用者(无论是谁)关闭流时,应该调用 Dispose。
    • 嗯是的,但是怎么做?你能举个例子吗?您在哪里调用 ms.Close() 以及如何将其传递到外部?就我而言,我使用的是在临时位置创建的实际文件( return new FileStream(sanitizedFile, FileMode.Open, FileAccess.Read); )并且它使临时位置中的文件句柄保持打开状态,因此我无法删除它...
    • @DavidRefaeli - 你不要关闭它。进行复制的人将关闭流(卧底,它是一个非托管流)。只要剪贴板数据保持不变,流就会保持打开状态。这就是我使用 MemoryStream 的原因。如果您确实有物理文件,请不要使用该方法,使用标准文件剪贴板格式。
    • 你能检查我的问题吗:stackoverflow.com/questions/48766727/…
    • 我尝试了您的解决方案,它在资源管理器中运行良好,但拖入 Outlook 电子邮件不起作用。首先,我需要为 IStream CopyTo 函数添加一个实现,但即使在那之后,Outlook 也会给出一条无帮助的消息“操作失败”。知道如何让它在这种情况下也能正常工作吗?
    猜你喜欢
    • 2010-11-14
    • 1970-01-01
    • 2012-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多