尝试从 Chrome 实现拖放 Gmail 附件

2024-01-31

我一直在尝试将 Gmail 附件从 Chrome 拖放到我的应用程序中。

可以将文件从电子邮件拖到桌面并在那里创建附件,所以我知道这一定是可能的。

我已经能够让它读取文件名,但是当我从数据对象读取 FileContents 时,我得到一个带有该文件链接的互联网快捷方式。

有人以前做过这个工作吗?目前的代码是针对 .txt 文件进行硬编码的

我的主要 DataObjectWrapper 类如下:

字符太多,无法全部发布,但主要方法是:

public object GetDataNative(string format, bool autoConvert)
{
    switch (format)
    {
        case CFSTR_FILEDESCRIPTOR_A:

            IntPtr fileGroupDescriptorAPointer = IntPtr.Zero;
            try
            {
                //use the underlying IDataObject to get the FileGroupDescriptor as a MemoryStream
                MemoryStream fileGroupDescriptorStream = (MemoryStream)this.underlyingDataObject.GetData(CFSTR_FILEDESCRIPTOR_A, autoConvert);
                byte[] fileGroupDescriptorBytes = new byte[fileGroupDescriptorStream.Length];
                fileGroupDescriptorStream.Read(fileGroupDescriptorBytes, 0, fileGroupDescriptorBytes.Length);
                fileGroupDescriptorStream.Close();

                //copy the file group descriptor into unmanaged memory 
                fileGroupDescriptorAPointer = Marshal.AllocHGlobal(fileGroupDescriptorBytes.Length);
                Marshal.Copy(fileGroupDescriptorBytes, 0, fileGroupDescriptorAPointer, fileGroupDescriptorBytes.Length);

                //marshal the unmanaged memory to to FILEGROUPDESCRIPTORA struct
                object fileGroupDescriptorObject = Marshal.PtrToStructure(fileGroupDescriptorAPointer, typeof(NativeMethods.FILEGROUPDESCRIPTORA));
                NativeMethods.FILEGROUPDESCRIPTORA fileGroupDescriptor = (NativeMethods.FILEGROUPDESCRIPTORA)fileGroupDescriptorObject;

                //get the pointer to the first file descriptor
                IntPtr fileDescriptorPointer = (IntPtr)((int)fileGroupDescriptorAPointer + Marshal.SizeOf(fileGroupDescriptor.cItems));

                NativeMethods.FILEDESCRIPTORA[] fileDescriptors = new NativeMethods.FILEDESCRIPTORA[fileGroupDescriptor.cItems];

                //loop for the number of files acording to the file group descriptor
                for (int fileDescriptorIndex = 0; fileDescriptorIndex < fileGroupDescriptor.cItems; fileDescriptorIndex++)
                {

                    //marshal the pointer top the file descriptor as a FILEDESCRIPTORA struct and get the file name
                    NativeMethods.FILEDESCRIPTORA fileDescriptor = (NativeMethods.FILEDESCRIPTORA)Marshal.PtrToStructure(fileDescriptorPointer, typeof(NativeMethods.FILEDESCRIPTORA));
                    fileDescriptors[fileDescriptorIndex] = fileDescriptor;

                    //move the file descriptor pointer to the next file descriptor
                    fileDescriptorPointer = (IntPtr)((int)fileDescriptorPointer + Marshal.SizeOf(fileDescriptor));
                }

                fileGroupDescriptor.fgd = fileDescriptors;

                //return the array of filenames
                return fileGroupDescriptor;
            }
            finally
            {
                //free unmanaged memory pointer
                Marshal.FreeHGlobal(fileGroupDescriptorAPointer);
            }

        case CFSTR_FILEDESCRIPTOR_W:

            IntPtr fileGroupDescriptorWPointer = IntPtr.Zero;
            try
            {
                //use the underlying IDataObject to get the FileGroupDescriptorW as a MemoryStream
                MemoryStream fileGroupDescriptorStream = (MemoryStream)this.underlyingDataObject.GetData(CFSTR_FILEDESCRIPTOR_W);
                byte[] fileGroupDescriptorBytes = new byte[fileGroupDescriptorStream.Length];
                fileGroupDescriptorStream.Read(fileGroupDescriptorBytes, 0, fileGroupDescriptorBytes.Length);
                fileGroupDescriptorStream.Close();

                //copy the file group descriptor into unmanaged memory
                fileGroupDescriptorWPointer = Marshal.AllocHGlobal(fileGroupDescriptorBytes.Length);
                Marshal.Copy(fileGroupDescriptorBytes, 0, fileGroupDescriptorWPointer, fileGroupDescriptorBytes.Length);

                //marshal the unmanaged memory to to FILEGROUPDESCRIPTORW struct
                object fileGroupDescriptorObject = Marshal.PtrToStructure(fileGroupDescriptorWPointer, typeof(NativeMethods.FILEGROUPDESCRIPTORW));
                NativeMethods.FILEGROUPDESCRIPTORW fileGroupDescriptor = (NativeMethods.FILEGROUPDESCRIPTORW)fileGroupDescriptorObject;

                //get the pointer to the first file descriptor
                IntPtr fileDescriptorPointer = (IntPtr)((int)fileGroupDescriptorWPointer + Marshal.SizeOf(fileGroupDescriptor.cItems));

                NativeMethods.FILEDESCRIPTORW[] fileDescriptiors = new NativeMethods.FILEDESCRIPTORW[fileGroupDescriptor.cItems];

                //loop for the number of files acording to the file group descriptor
                for (int fileDescriptorIndex = 0; fileDescriptorIndex < fileGroupDescriptor.cItems; fileDescriptorIndex++)
                {
                    //marshal the pointer top the file descriptor as a FILEDESCRIPTORW struct and get the file name
                    NativeMethods.FILEDESCRIPTORW fileDescriptor = (NativeMethods.FILEDESCRIPTORW)Marshal.PtrToStructure(fileDescriptorPointer, typeof(NativeMethods.FILEDESCRIPTORW));
                    fileDescriptiors[fileDescriptorIndex] = fileDescriptor;

                    //move the file descriptor pointer to the next file descriptor
                    fileDescriptorPointer = (IntPtr)((int)fileDescriptorPointer + Marshal.SizeOf(fileDescriptor));
                }

                fileGroupDescriptor.fgd = fileDescriptiors;

                //return the array of filenames
                return fileGroupDescriptor;
            }
            finally
            {
                //free unmanaged memory pointer
                Marshal.FreeHGlobal(fileGroupDescriptorWPointer);
            }

        case CFSTR_FILECONTENTS:

            //override the default handling of FileContents which returns the
            //contents of the first file as a memory stream and instead return
            //a array of MemoryStreams containing the data to each file dropped

            //get the array of filenames which lets us know how many file contents exist
            string[] fileContentNames = (string[])this.GetData(CFSTR_FILEDESCRIPTOR_W);

            //create a MemoryStream array to store the file contents
            MemoryStream[] fileContents = new MemoryStream[fileContentNames.Length];

            //loop for the number of files acording to the file names
            for (int fileIndex = 0; fileIndex < fileContentNames.Length; fileIndex++)
            {
                //get the data at the file index and store in array
                fileContents[fileIndex] = this.GetData(format, fileIndex);
            }

            //return array of MemoryStreams containing file contents
            return fileContents;

        case CFSTR_INETURL_A:

            //use the underlying IDataObject to get the FileGroupDescriptorW as a MemoryStream
            MemoryStream UniformResourceLocatorStream = (MemoryStream)this.underlyingDataObject.GetData(CFSTR_INETURL_A);
            byte[] UniformResourceLocatorBytes = new byte[UniformResourceLocatorStream.Length];
            UniformResourceLocatorStream.Read(UniformResourceLocatorBytes, 0, UniformResourceLocatorBytes.Length);
            UniformResourceLocatorStream.Close();

            string url = null;

            if (UniformResourceLocatorBytes[1] == 0)
                url = Encoding.Unicode.GetString(UniformResourceLocatorBytes);
            else
                url = Encoding.ASCII.GetString(UniformResourceLocatorBytes);

            return url;

        case CFSTR_INETURL_W:

            //use the underlying IDataObject to get the FileGroupDescriptorW as a MemoryStream
            MemoryStream UniformResourceLocatorWStream = (MemoryStream)this.underlyingDataObject.GetData(CFSTR_INETURL_W);
            byte[] UniformResourceLocatorWBytes = new byte[UniformResourceLocatorWStream.Length];
            UniformResourceLocatorWStream.Read(UniformResourceLocatorWBytes, 0, UniformResourceLocatorWBytes.Length);
            UniformResourceLocatorWStream.Close();

            string urlW = null;

            if (UniformResourceLocatorWBytes[1] == 0)
                urlW = Encoding.Unicode.GetString(UniformResourceLocatorWBytes);
            else
                urlW = Encoding.ASCII.GetString(UniformResourceLocatorWBytes);

            return urlW;

        case TEXT_X_MOZ_URL:

            //use the underlying IDataObject to get the FileGroupDescriptorW as a MemoryStream
            MemoryStream textMozStream = (MemoryStream)this.underlyingDataObject.GetData(TEXT_X_MOZ_URL);
            byte[] textMozBytes = new byte[textMozStream.Length];
            textMozStream.Read(textMozBytes, 0, textMozBytes.Length);
            textMozStream.Close();

            string urlText = null;

            if (textMozBytes[1] == 0)
                urlText = Encoding.Unicode.GetString(textMozBytes);
            else
                urlText = Encoding.ASCII.GetString(textMozBytes);

            return urlText;

        case "text/html":

            //use the underlying IDataObject to get the FileGroupDescriptorW as a MemoryStream
            MemoryStream dataFormatStream = (MemoryStream)this.underlyingDataObject.GetData("text/html");
            byte[] dataFormatBytes = new byte[dataFormatStream.Length];
            dataFormatStream.Read(dataFormatBytes, 0, dataFormatBytes.Length);
            dataFormatStream.Close();

            string formatText = null;

            if (dataFormatBytes[1] == 0)
                formatText = Encoding.Unicode.GetString(dataFormatBytes);
            else
                formatText = Encoding.ASCII.GetString(dataFormatBytes);

            return formatText;
    }

    //use underlying IDataObject to handle getting of data
    return this.underlyingDataObject.GetData(format, autoConvert);
}

我可以从中获取相当多的数据,但与将其放在桌面上不同:

private void Form1_DragDrop(object sender, DragEventArgs e)
{
    string[] dataFormats = e.Data.GetFormats();

    Dictionary<string, object> dataDictionary = new Dictionary<string, object>();

    foreach (string dataFormat in dataFormats)
    {
        dataDictionary.Add(dataFormat, e.Data.GetData(dataFormat));

        Debug.WriteLine(
            String.Format("Data Format: {0}     Has data: {1}       Data: {2}",
                dataFormat,
                dataDictionary[dataFormat] != null ? "Yes" : "No",
                dataDictionary[dataFormat] != null ? dataDictionary[dataFormat] : string.Empty));
    }

    DataObjectWrapper dataWrapper = new DragDropTest.DataObjectWrapper(e.Data);

    DataObjectWrapper.NativeMethods.FILEGROUPDESCRIPTORW fileGroupDescriptorW = dataWrapper.GetFileGroupDescriptorW();

    string url = dataWrapper.GetUniformResourceLocatorA();
    string urlw = dataWrapper.GetUniformResourceLocatorW();
    string mozUrl = dataWrapper.GetTextMozURL();
    string textHTML = dataWrapper.GetTExtHtml();

    Stream[] streams = dataWrapper.GetFileContents();

    byte[] buffer = new byte[1024];

    int received = 0;

    FileStream fileStream = File.OpenWrite(@"c:\temp\hello.txt");
    using (Stream input = streams[0])
    {
        int size = input.Read(buffer, 0, buffer.Length);
        while (size > 0)
        {
            fileStream.Write(buffer, 0, size);
            received += size;

            size = input.Read(buffer, 0, buffer.Length);
        }
    }

    fileStream.Flush();
    fileStream.Close();

    return;
}

我已经阅读并尝试了以下链接:

http://dlaa.me/blog/post/9913083 http://dlaa.me/blog/post/9913083

使用 VirtualFileDataObject 通过 IStream 拖放大型虚拟文件 https://stackoverflow.com/questions/22700195/drag-and-drop-large-virtual-files-with-istream-using-virtualfiledataobject

将大型虚拟文件从 C# 拖放到 Windows 资源管理器 https://stackoverflow.com/questions/12410114/drag-and-drop-large-virtual-files-from-c-sharp-to-windows-explorer

https://msdn.microsoft.com/en-us/library/windows/desktop/bb776902(v=vs.85).aspx#CFSTR_FILECONTENTS https://msdn.microsoft.com/en-us/library/windows/desktop/bb776902(v=vs.85).aspx#CFSTR_FILECONTENTS

https://msdn.microsoft.com/en-us/library/windows/desktop/bb776904(v=vs.85).aspx https://msdn.microsoft.com/en-us/library/windows/desktop/bb776904(v=vs.85).aspx

https://dlaa.me/blog/post/9923072 https://dlaa.me/blog/post/9923072

使用 IStream 拖放虚拟文件 https://stackoverflow.com/questions/1187444/drag-and-drop-virtual-files-using-istream

https://blogs.msdn.microsoft.com/adamroot/2008/02/19/shell-style-drag-and-drop-in-net-wpf-and-winforms/ https://blogs.msdn.microsoft.com/adamroot/2008/02/19/shell-style-drag-and-drop-in-net-wpf-and-winforms/

在我的 .NET Windows 窗体上实现 Chrome 的拖放 https://stackoverflow.com/questions/6125921/implementing-drag-drop-from-chrome-on-my-net-windows-form

https://blogs.msdn.microsoft.com/adamroot/2008/02/19/shell-style-drag-and-drop-in-net-part-3/ https://blogs.msdn.microsoft.com/adamroot/2008/02/19/shell-style-drag-and-drop-in-net-part-3/

https://blogs.msdn.microsoft.com/adamroot/2008/02/19/shell-style-drag-and-drop-in-net-wpf-and-winforms/ https://blogs.msdn.microsoft.com/adamroot/2008/02/19/shell-style-drag-and-drop-in-net-wpf-and-winforms/

https://blogs.msdn.microsoft.com/adamroot/2008/02/19/shell-style-drag-and-drop-in-net-part-2/ https://blogs.msdn.microsoft.com/adamroot/2008/02/19/shell-style-drag-and-drop-in-net-part-2/

https://blogs.msdn.microsoft.com/adamroot/2008/02/02/dragdroplib-cs/ https://blogs.msdn.microsoft.com/adamroot/2008/02/02/dragdroplib-cs/

https://www.codeproject.com/reference/1091137/windows-clipboard-formats https://www.codeproject.com/reference/1091137/windows-clipboard-formats

http://dlaa.me/blog/post/9923072 http://dlaa.me/blog/post/9923072

使用 IStream 拖放虚拟文件 https://stackoverflow.com/questions/1187444/drag-and-drop-virtual-files-using-istream

http://www.ookii.org/Blog/opening_files_via_idroptarget_in_net http://www.ookii.org/Blog/opening_files_via_idroptarget_in_net

https://www.codeproject.com/Articles/28209/Outlook-Drag-and-Drop-in-C https://www.codeproject.com/Articles/28209/Outlook-Drag-and-Drop-in-C


您应该能够使用代码拖放嵌入的图像。但 zip 或 pdf 文件等附件会以“FileDrop”格式从 Chrome 传输。不幸的是,它使用了 DotNet 本身不支持的异步拖放界面。这就是为什么 GetData("FileDrop") 将始终返回 null。

您需要使用IDataObjectAsyncCapability接口来触发下载。但首先有必要拆开 DataObject 以获取底层 COM-DataObject:

using System;
using System.Collections.Specialized;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Threading.Tasks;

namespace Dummy
{
    public class FileDrop
    {
        [ComImport]
        [Guid("3D8B0590-F691-11d2-8EA9-006097DF5BD4")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IDataObjectAsyncCapability
        {
            void SetAsyncMode([In] Int32 fDoOpAsync);
            void GetAsyncMode([Out] out Int32 pfIsOpAsync);
            void StartOperation([In] IBindCtx pbcReserved);
            void InOperation([Out] out Int32 pfInAsyncOp);
            void EndOperation([In] Int32 hResult, [In] IBindCtx pbcReserved, [In] UInt32 dwEffects);
        }

        public static async Task<StringCollection> GetFileDrop(System.Windows.Forms.DataObject dataobject)
        {
            if (dataobject.ContainsFileDropList())
            {
                var files = dataobject.GetFileDropList();
                if (files.Count == 0)
                {
                    // try async version
                    if (await ProcessFileDropAsync(dataobject))
                    {
                        files = dataobject.GetFileDropList();
                    }
                }

                return files;
            }

            // return empty collection    
            return new StringCollection();
        }

        private static async Task<bool> ProcessFileDropAsync(System.Windows.Forms.DataObject dataobject)
        {
            // get the internal ole dataobject
            FieldInfo innerDataField = dataobject.GetType().GetField("innerData", BindingFlags.NonPublic | BindingFlags.Instance);
            var oledataobject = (System.Windows.Forms.IDataObject)innerDataField.GetValue(dataobject);

            var type = oledataobject.GetType();
            if (type.Name == "OleConverter")
            {
                // get the COM-object from the OleConverter
                FieldInfo innerDataField2 = type.GetField("innerData", BindingFlags.NonPublic | BindingFlags.Instance);
                var item = innerDataField2.GetValue(oledataobject);

                var asyncitem = item as IDataObjectAsyncCapability;

                if (asyncitem != null)
                {
                    var isasync = 0;
                    asyncitem.GetAsyncMode(out isasync);
                    if (isasync != 0)
                    {
                        var task = Task.Run(() =>
                        {
                            asyncitem.StartOperation(null);

                            // calling GetData after StartOperation will trigger the download in Chrome
                            // subsequent calls to GetData return cached data
                            // files are downloaded to something like c:\temp\chrome_drag1234_123456789\yourfilename.here
                            var result = dataobject.GetData("FileDrop"); 

                            asyncitem.EndOperation(0, null, 1); // DROPEFFECT_COPY = 1                    
                        });

                        await task;

                        return true;
                    }                    
                }
            }

            return false;
        }
    }
}

从您的放置处理程序中调用它,文件应包含附件的路径:

    private async void panel_DragDrop(object sender, DragEventArgs e)
    {
        var files = await FileDrop.GetFileDrop(e.Data as System.Windows.Forms.DataObject);

        foreach (var file in files)
        {
            Console.WriteLine(file);
        }
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

尝试从 Chrome 实现拖放 Gmail 附件 的相关文章

  • 使用 C# 登录《我的世界》

    我正在尝试为自己和一些朋友创建一个简单的自定义 Minecraft 启动器 我不需要启动 Minecraft 的代码 只需要登录的实际代码行 例如 据我所知 您过去可以使用 string netResponse httpGET https
  • IdentityServer 4 对它的工作原理感到困惑

    我阅读和观看了很多有关 Identity Server 4 的内容 但我仍然对它有点困惑 因为似乎有很多移动部件 我现在明白这是一个单独的项目 它处理用户身份验证 我仍然不明白的是用户如何注册它 谁存储用户名 密码 我打算进行此设置 Rea
  • JNI 将 Char* 2D 数组传递给 JAVA 代码

    我想从 C 代码通过 JNI 层传递以下指针数组 char result MAXTEST MAXRESPONSE 12 12 8 3 29 70 5 2 42 42 在java代码中我写了以下声明 public static native
  • 如何填充 ToolStripComboBox?

    我发现它很难将数据绑定到ToolStripComboBox 好像没有这个ValueMember and DisplayMember特性 怎么绑定呢 访问toolstripcombobox中包装的组合框并访问其ValueMember Disp
  • 查看 NuGet 包依赖关系层次结构

    有没有一种方法 文本或图形 来查看 NuGet 包之间的依赖关系层次结构 如果您使用的是新的 csproj 您可以在此处获取所有依赖项 在项目构建后 项目目录 obj project assets json
  • 使用可变参数包类型扩展的 C++ 函数调用者包装器

    我绑定了一些 API 并且绑定了一些函数签名 如下所示 static bool WrapperFunction JSContext cx unsigned argc JS Value vp 我尝试将对象和函数包装在 SpiderMonkey
  • 使用 GCP 的数据存储区时如何区分代码是在模拟器中运行还是在 GKE 中运行

    按照中给出的说明进行操作后 我不确定是否遗漏了任何内容https cloud google com datastore docs tools datastore emulator https cloud google com datasto
  • 使用 LINQ to SQL 时避免连接超时的最佳实践

    我需要知道在 net 应用程序中使用 LINQ to SQL 时避免连接超时的最佳实践 特别是在返回时IQueryable
  • 如何在 Qt 应用程序中通过终端命令运行分离的应用程序?

    我想使用命令 cd opencv opencv 3 0 0 alpha samples cpp cpp example facedetect lena jpg 在 Qt 应用程序中按钮的 clicked 方法上运行 OpenCV 示例代码
  • 如何在 C 中安全地声明 16 位字符串文字?

    我知道已经有一个标准方法 前缀为L wchar t test literal L Test 问题是wchar t不保证是16位 但是对于我的项目 我需要16位wchar t 我还想避免通过的要求 fshort wchar 那么 C 不是 C
  • 如何查看正在运行的 tcsh 版本?

    如何查看我的 UNIX 终端中运行的 tcsh 的当前版本 看着那 这version多变的 echo version tcsh 6 14 00 Astron 2005 03 25 i386 intel linux options wide
  • 在mysql连接字符串中添加应用程序名称/程序名称[关闭]

    Closed 这个问题需要细节或清晰度 help closed questions 目前不接受答案 我正在寻找一种解决方案 在连接字符串中添加应用程序名称或程序名称 以便它在 MySQL Workbench 中的 客户端连接 下可见 SQL
  • C++ int 前面加 0 会改变整个值

    我有一个非常奇怪的问题 如果我像这样声明一个 int int time 0110 然后将其显示到控制台返回的值为72 但是当我删除前面的 0 时int time 110 然后控制台显示110正如预期的那样 我想知道两件事 首先 为什么它在
  • C++ 中的双精度型数字

    尽管内部表示有 17 位 但 IEE754 64 位 浮点应该正确表示 15 位有效数字 有没有办法强制第 16 位和第 17 位为零 Ref http msdn microsoft com en us library system dou
  • C++ new * char 不为空

    我有一个问题 我在 ASIO 中开发服务器 数据包采用尖头字符 当我创建新字符时 例如char buffer new char 128 我必须手动将其清理为空 By for int i 0 i lt 128 i buffer i 0x00
  • String.Empty 与 "" [重复]

    这个问题在这里已经有答案了 可能的重复 String Empty 和 有什么区别 https stackoverflow com questions 151472 what is the difference between string
  • OpenGL:仅获取模板缓冲区而没有深度缓冲区?

    我想获取一个模板缓冲区 但如果可能的话 不要承受附加深度缓冲区的开销 因为我不会使用它 我发现的大多数资源表明 虽然模板缓冲区是可选的 例如 排除它以利于获得更高的深度缓冲区精度 但我还没有看到任何请求并成功获取仅 8 位模板缓冲区的代码
  • 在 Windows Phone silverlight 8.1 上接收 WNS 推送通知

    我有 Windows Phone 8 1 silverlight 应用程序 我想使用新框架 WNS 接收通知 我在 package appxmanifest 中有
  • 如何在richtextbox中使用多颜色[重复]

    这个问题在这里已经有答案了 我使用 C windows 窗体 并且有 richtextbox 我想将一些文本设置为红色 一些设置为绿色 一些设置为黑色 怎么办呢 附图片 System Windows Forms RichTextBox有一个
  • Objective-C / C 给出枚举默认值

    我在某处读到过关于给枚举默认值的内容 如下所示 typedef enum MarketNavigationTypeNone 0 MarketNavigationTypeHeirachy 1 MarketNavigationTypeMarke

随机推荐