我如何像Windows资源管理器一样从chrome检索拖放数据 [英] How can I retrieve the drag-drop data from chrome, like Windows Explorer does

查看:79
本文介绍了我如何像Windows资源管理器一样从chrome检索拖放数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我将图像从Chrome(或Firefox)拖到桌面上,则Windows资源管理器(不是IE)会保存文件.

If I drag an image from Chrome (or Firefox) onto the Desktop, Windows Explorer (not IE) is able to save the file.

但是,如果我尝试从 System.Windows.Forms.IDataObject 获取位图,则没有可用于Chrome的位图数据(只有Firefox的DIB).

However, if I attempt to get the Bitmap from the System.Windows.Forms.IDataObject, there is no Bitmap data available for Chrome (and only DIB from Firefox).

这让我感到困惑,当 IDataObject 中没有可用的资源时,Explorer如何从Chrome/Firefox获取实际图像?Microsoft是否具有唯一的功能,只有Windows才能使用它来提取图像数据?我知道如何通过URI或FileDrop临时文件(参见下文)获取图像.问题是如何获取提供的文件",就像资源管理器一样.

So riddle me this, how is Explorer able to get the actual image from Chrome/Firefox, when it's not available in IDataObject? Does Microsoft have a undocumented feature the only Windows can use to extract the image data? I know how to get the image via the URI, or the FileDrop tempfile (see below). The question is "how to get the served file", like Explorer does.

一些注意事项:

  1. 放置在桌面上的JPEG与提供的JPEG相同(md5sum),并且保留了EXIF数据,因此无法将DIB(如果存在)转换为JPEG.
  2. 图片位于经过身份验证的会话之后,因此资源管理器无法通过URL提取文件.
  3. 两个浏览器都提供 DragImageBits ,但这不是原始图像(太小),而且仅应将其用作拖动时的预览(Explorer会这样做).
  4. Firefox提供了一个 FileDrop 数组,其中包含一个临时文件,但这是BMP,而不是JPEG.
  1. The JPEG dropped onto the Desktop is identical (md5sum) to the one served, plus EXIF data is preserved, so it can't be converting the DIB (if present) to a JPEG.
  2. The Image is behind an authenticated session, so Explorer can't be fetching the file by the URL.
  3. Both browsers provide DragImageBits, but this isn't the original image (it's too small), plus it's only supposed to be used as an preview when dragging (Explorer does this).
  4. Firefox provides a FileDrop array which contains a tempfile, but this is a BMP, not a JPEG.

我实际上写了一个小应用程序来显示Paste/DragDrop事件中的 IDataObject 数据,以解决此问题,但无济于事.

I actually wrote a small app to display IDataObject data from Paste/DragDrop events in order to solve this, but to no avail.

来源&在 GitHub

推荐答案

在阅读了有关 System.Windows.Forms.IDataObject 类的一些注释后,这些注释未公开 IStream (我一无所知),我遇到了CodeProject文章 Outlook在C#中的拖放.

After reading some comments about the System.Windows.Forms.IDataObject class not exposing IStream (which I had no idea about), I came across a CodeProject article Outlook Drag and Drop in C#.

邮件消息是一个问题,因为OS调用返回一个IStorage,它是一种复合文件类型,并且 IDataObject的C#实现再次使我们失望,因为它不处理这种类型的返回,因此您得到的是null.

因此,从该示例中大胆地提出(我没有实现 IStorage ,因为我主要感兴趣的是 HGlobal ),我创建了一组扩展方法,使我可以检索FileContent.

So, lifting liberally from that example (I haven't implement IStorage, as what I was mainly interested about turned out to be HGlobal) I created a set of extension methods that allow me to retrieve the FileContent.

用法:

var fileNames = e.Data.GetFileContentNames();
for (int i = 0; i < files.Length; i++) {
  using (var ms = e.Data.GetFileContent(i)) {
    // Do something with your unadulterated content!
  }
}

代码:

using System;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;

namespace DragDropViewer.ExtensionMethods {
  /// <summary>Helper methods for getting FileContents from DragDrop data.</summary>
  /// <see cref="!:https://www.codeproject.com/Articles/28209/Outlook-Drag-and-Drop-in-C"/>
  public static class IDataObjectExtensionMethods {
    /// <summary>Gets the array of FileNames from the FileGroupDescriptors format.</summary>
    public static string[] GetFileContentNames(this System.Windows.Forms.IDataObject data) {
      var names = new string[data.GetFileContentCount()];

      if (names.Length != 0) {
        var bytes = data.GetFileGroupDescriptor().ToArray();
        IntPtr fgdPtr = IntPtr.Zero;
        try {
          fgdPtr = Marshal.AllocHGlobal(bytes.Length);

          int offset = Marshal.SizeOf(typeof(UInt32));
          int size = Marshal.SizeOf(typeof(FILEDESCRIPTORW));

          for (int i = 0; i < names.Length; i++) {
            var fd = (FILEDESCRIPTORW)Marshal.PtrToStructure(fgdPtr + offset + (i * size), typeof(FILEDESCRIPTORW));
            names[i] = fd.cFileName;
          }

        } finally {
          if (fgdPtr != IntPtr.Zero) Marshal.FreeHGlobal(fgdPtr);

        }
      }

      return names;
    }

    /// <summary>Gets the number of files available in the FileGroupDescriptor format.</summary>
    public static int GetFileContentCount(this System.Windows.Forms.IDataObject data) {
      // File count is stored as an UInt32 in the FileGroupDescriptor format
      MemoryStream ms = data.GetFileGroupDescriptor();
      if (ms == null) return 0;

      using (var reader = new BinaryReader(ms)) {
        return (int)reader.ReadUInt32(); // Assume this won't overflow!
      }
    }

    /// <summary>Gets the file content for the specified FileDescriptor index.</summary>
    /// <param name="index">The index of the file content to retrieve.</param>
    public static MemoryStream GetFileContent(this System.Windows.Forms.IDataObject data, int index) {
      // As this is indexed, "FileContent" is most likely null, so the COM IDataObject needs to be used
      var comData = (System.Runtime.InteropServices.ComTypes.IDataObject)data;

      var formatetc = new FORMATETC() {
        cfFormat = (short)DataFormats.GetFormat("FileContents").Id,
        dwAspect = DVASPECT.DVASPECT_CONTENT,
        lindex = index,
        ptd = IntPtr.Zero,
        tymed = TYMED.TYMED_ISTREAM | TYMED.TYMED_HGLOBAL
      };

      var medium = new STGMEDIUM();
      comData.GetData(ref formatetc, out medium);

      switch (medium.tymed) {
        case TYMED.TYMED_HGLOBAL:
          return data.GetFileContentFromHGlobal(medium);

        case TYMED.TYMED_ISTREAM:
          return data.GetFileContentFromIStream(medium);

        default:
          throw new InvalidOperationException($"Cannot get FileContent for {medium.tymed} TYMED.");

      }
    }

    private static MemoryStream GetFileContentFromHGlobal(this System.Windows.Forms.IDataObject data, STGMEDIUM medium) {
      var innerDataField = data.GetType().GetField("innerData", BindingFlags.NonPublic | BindingFlags.Instance);
      var oldData = (System.Windows.Forms.IDataObject)innerDataField.GetValue(data);

      var getDataFromHGLOBLALMethod = oldData.GetType().GetMethod("GetDataFromHGLOBLAL", BindingFlags.NonPublic | BindingFlags.Instance);

      return (MemoryStream)getDataFromHGLOBLALMethod.Invoke(oldData, new object[] { "FileContents", medium.unionmember });
    }

    private static MemoryStream GetFileContentFromIStream(this System.Windows.Forms.IDataObject data, STGMEDIUM medium) {
      var iStream = (IStream)Marshal.GetObjectForIUnknown(medium.unionmember);
      Marshal.Release(medium.unionmember);

      var iStreamStat = new System.Runtime.InteropServices.ComTypes.STATSTG();
      iStream.Stat(out iStreamStat, 0);

      var content = new byte[(int)iStreamStat.cbSize];
      iStream.Read(content, content.Length, IntPtr.Zero);

      return new MemoryStream(content);
    }

    private static MemoryStream GetFileGroupDescriptor(this System.Windows.Forms.IDataObject data) {
      MemoryStream ms = null;
      if (data.GetDataPresent("FileGroupDescriptorW")) {
        ms = (MemoryStream)data.GetData("FileGroupDescriptorW", true);
      }

      return ms;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct FILEDESCRIPTORW {
      public UInt32 dwFlags;
      public Guid clsid;
      public System.Drawing.Size sizel;
      public System.Drawing.Point pointl;
      public UInt32 dwFileAttributes;
      public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
      public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
      public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
      public UInt32 nFileSizeHigh;
      public UInt32 nFileSizeLow;
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
      public String cFileName;
    }
  }
}

这篇关于我如何像Windows资源管理器一样从chrome检索拖放数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆