2

I am trying to drag from another windows application (EM Client, thunderbird or outlook) onto my form. When an email is dragged from the other application to windows explore it will drop as a file. If the user drags onto my application I would like to get the file contents as a file stream.

I was able to get this working with a UWP app, but I need to get it working in a Windows Form app so it will work in Windows 7.

I have found lots of examples of going the other way (dragging from App to windows).

The thing that makes this so annoying is it is easy in the UWP app. Here is how I did it in the UWP app, the results of which is I get a new file saved in the roaming folder at name "email.eml":

XAML

 <Grid AllowDrop="True" DragOver="Grid_DragOver" Drop="Grid_Drop"
      Background="LightBlue" Margin="10,10,10,353">
        <TextBlock>Drop anywhere in the blue area</TextBlock>
 </Grid>

XAML.CS

namespace App1
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }
        private void Grid_DragOver(object sender, DragEventArgs e)
        {
            e.AcceptedOperation = DataPackageOperation.Copy;
        }
        private async void Grid_Drop(object sender, DragEventArgs e)
        {
            if (e.DataView.Contains(StandardDataFormats.StorageItems))
            {
                var items = await e.DataView.GetStorageItemsAsync();
                if (items.Count > 0)
                {
                    var storageFile = items[0] as StorageFile;
                    var reader = (await storageFile.OpenAsync(FileAccessMode.Read));
                    IBuffer result = new byte[reader.Size].AsBuffer();
                    var test = await reader.ReadAsync(result, result.Length,Windows.Storage.Streams.InputStreamOptions.None);
                    Windows.Storage.StorageFolder storageFolder =
                    Windows.Storage.ApplicationData.Current.LocalFolder;
                    Windows.Storage.StorageFile sampleFile = await storageFolder.CreateFileAsync("email.eml",Windows.Storage.CreationCollisionOption.ReplaceExisting);

                    await Windows.Storage.FileIO.WriteBufferAsync(sampleFile, test);


                }
            }
        }
    }

}

I have read every article that that is listed on this answer, plus more: Trying to implement Drag and Drop gmail attachment from chrome

Basically no matter how I attack it I end up with one of 3 results:

  1. a exception for "Invalid FORMATETC structure (Exception from HRESULT: 0x80040064(DV_E_FORMATETC))"
  2. my MemoryStream is null
  3. I get a security violation

This is the Code that gets a security violation:

 MemoryStream ClipboardMemoryStream = new MemoryStream();

 BinaryFormatter bft = new BinaryFormatter();

 bft.Serialize(ClipboardMemoryStream, e.Data.GetData("FileGroupDescriptorW", false));

 byte[] byteArray = ClipboardMemoryStream.ToArray();

My guess is that I need to implement the e.Data.GetData("FileGroupDesciptorW") is returning a IStorage Class, and I need to implement that class, but I am loss on how to do it, plus I am not sure that is the case

e.Data.GetType shows its a marshalbyrefobject, I have attempted to do the Remoting manually, but I got stuck on not having an open channel.

https://learn.microsoft.com/en-us/windows/desktop/api/objidl/nn-objidl-istorage https://learn.microsoft.com/en-us/windows/desktop/shell/datascenarios#dragging-and-dropping-shell-objects-asynchronously

  • That exception suggests that the application that is being dragged from has created an invalid FORMATETC stream on the clipboard. Why not try something simple like a text string to prove the D&D bit works, then work up to something more complicated. – Neil Aug 03 '18 at 20:23
  • Thanks Neil! I was able to drop files from windows explorer, and even cells from excel and handle those. It may be that I don't have enough understanding of the FORMATETC structure to correctly request the data. Whats interesting is in the UWP app, e.DataView.GetStorageItemsAsync() must be making a getData call. The UWP app works. I just have no clue what FORMATEC structure it uses. – BigShedBuilder Aug 03 '18 at 21:26
  • There is too much irrelevant info in this question that obfuscates what you are actually trying to drag. Dropping file(s) onto a Winforms app is [simple as well](https://stackoverflow.com/a/89470/17034). – Hans Passant Aug 03 '18 at 21:37
  • Did you try example you posted? Because if you did you would know that only works if the item dropped was a file that exist on the system disk, not a stream coming from another application. – BigShedBuilder Aug 04 '18 at 00:48

1 Answers1

0

So After reaching out to a professional for help I have a working example. The trick was to get "FileDescriptorW" working in the Custom ComObject class. You will find a version of this class in the Drag from Outlook example but it does not work when dragging from EM Client, this does.

Here is the Code: Code is too Big to post

Then You can use it like this:

        MyDataObject obj = new MyDataObject(e.Data);
        string[] fileNames = { };
        //ThunderBird Does a FileDrop
        if (obj.GetDataPresent(DataFormats.FileDrop, true))
        {
            string[] tempFileNames = (string[])obj.GetData(DataFormats.FileDrop);
            List<string> tempFileNameList = new List<string>();
            foreach(string f in tempFileNames)
            {
                tempFileNameList.Add(Path.GetFileName(f));
            }
            fileNames = tempFileNameList.ToArray();

        } else if (fileNames.Length == 0)
        {
            //EM Client uses "FileGroupDescriptorW"
            fileNames = (string[])obj.GetData("FileGroupDescriptorW");
        }else if (fileNames.Length == 0)
        {  
                //Outlook Uses "FileGroupDescriptor"
                fileNames = (string[])obj.GetData("FileGroupDescriptor");
        }


        int index = 0;
        foreach (string f in fileNames)
        {
            File.WriteAllBytes("C:\\FilePath\\"+f, obj.GetData("FileContents", index).ToArray());

            index++;
        }