尝试从Chrome实现拖放Gmail附件 [英] Trying to implement Drag and Drop gmail attachment from chrome

查看:97
本文介绍了尝试从Chrome实现拖放Gmail附件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have been trying to implement drag and drop a gmail attachment from chrome into my application.

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

Its possible to drag the file from the email to the desktop and it create the attachment there so I know this must be possible.

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

I have been able to get it to read the file name, but when I read FileContents from the data object I get an internet shortcut with a link to the file.

有人在此之前工作过吗?该代码目前已硬编码为.txt文件

Has anyone got this working before? the code at the moment is hard coded for a .txt file

示例项目可从以下位置下载:

The sample project can be downloaded from:

https://www.dropbox.com/s/jz4zde0mvgxzn1g/DragDropTest .zip?dl = 0

我的主要DataObjectWrapper类如下:

My main DataObjectWrapper class is as follows:

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

there are too many characters to post all of it but the main method is:

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

我可以从中获取很多数据,但不能就像放在桌面上一样:

I can get quite a bit of data out of it, but just not in the same was as if it was dropped on the desktop:

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

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

I have read and tried the below links already:

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

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

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

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

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

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

使用IStream拖放虚拟文件

> https://blogs.msdn.microsoft.com/adamroot/2008/02/19/ shell样式-拖放到wpf和winforms /

在我的.NET Windows窗体上实施从Chrome拖放

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-part-2 /

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

https://www.codeproject。 com / reference / 1091137 / windows-cl ipboard格式

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

使用IStream拖放虚拟文件

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

https://www.codeproject.com/Articles/28209/Outlook-拖放到C

推荐答案

您应该可以将&用代码删除嵌入式图像。但是,附件(例如zip或pdf文件)是从Chrome以 FileDrop格式传输的。不幸的是,它使用了异步拖放功能。 DotNet本身不支持的drop接口。这就是为什么GetData( FileDrop)始终返回null的原因。

You should be able to drag & drop embedded images with your code. But attachments like zip or pdf files are transfered as "FileDrop" format from Chrome. Unfortunately it uses the asynchronous drag & drop interface which is not natively supported by DotNet. That's why GetData("FileDrop") will always return null.

您需要使用IDataObjectAsyncCapability接口触发下载。但是首先有必要将DataObject分解为底层的COM-DataObject:

You need to use the interface IDataObjectAsyncCapability to trigger the download. But first it is necessary to pick apart the DataObject to get to the underlying 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;
        }
    }
}

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

Call it from your drop handler and files should contain the path to your attachment:

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

这篇关于尝试从Chrome实现拖放Gmail附件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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