Перетащите файл из WPF в Проводник - с обработкой файла перед удалением

В моем приложении я хочу, чтобы пользователи могли перетаскивать файлы (визуальное представление) из моего окна в проводник. Но мне нужно пропустить файлы через "очистку" (то есть это не просто копирование и вставка, я на самом деле отправляю файл на сервер CDR и возвращаю его обратно).

Вопрос - как мне все это инициировать? Поскольку кажется, что вы не можете получить назначение перетаскивания... Я хочу отложить фактическое событие перетаскивания и ввести свою логику между ними.

Я нашел один обходной путь, основанный на этом посте в блоге, для создания виртуального файла - это позволяет мне в основном отправить функцию очистки, дождаться создания очищенного файла в некотором временном местоположении, а затем выполнить магию перетаскивания.,

Это прекрасно работает, но есть один недостаток - он загружает весь файл в память... Так как файлы могут быть довольно большими, меня это беспокоит. Я почти уверен, что должен быть способ сделать это без этого (возможно даже без виртуального беспорядка файла), я просто не понимаю достаточно, как сделать это. Кто-нибудь может предложить какую-либо помощь?

КОД:

С моей точки зрения:

private void ItemMouseDown(object sender, MouseButtonEventArgs e)
{
    var t = MainWindow.CurrentMainWindow.GetSelected();
    DragDropFile(SanitizeFiles, t, LocalTempPath);
}

Метод dragdrop (полный код можно найти в блоге Дэвида Ансона - ссылка выше):

public void DragDropFile(Func<string[], string, bool> sanitizeFiles, string path, string tempSanitizedLocation)
{
    string sanitizedFile = Path.Combine(tempSanitizedLocation, Path.GetFileName(path));

    var x = new[]
    {
        new VirtualFileDataObject.FileDescriptor
        {
            Name = Path.GetFileName(path),
            ChangeTimeUtc = DateTime.Now,
            StreamContents = stream =>
            {
                sanitizeFiles(path, tempSanitizedLocation);

                int i = 0;
                while (!File.Exists(sanitizedFile))
                {
                    Thread.Sleep(500);
                    i++;
                    if (i > 1000) // timeout = 500 seconds
                        break;
                }

                // fast - spike in memory, immediate drop (GC)    
                var content = File.ReadAllBytes(sanitizedFile);
                stream.Write(content, 0, content.Length);

                // slow - gradual rise in memory to same level as the fast, and immediate drop for GC
                //using (var fs = new FileStream(sanitizedFile, FileMode.Open, FileAccess.Read))
                //{
                //    fs.CopyTo(stream, 131072);
                //}                        

                // delete temp file
                try
                {
                    File.Delete(sanitizedFile);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
            }
        }
    };
    var virtualFileDataObject = new VirtualFileDataObject();
    virtualFileDataObject.SetData(x);
    try
    {
        VirtualFileDataObject.DoDragDrop(virtualFileDataObject, DragDropEffects.Copy);

    }
    catch (COMException)
    {
     // Failure; no way to recover
    }
}

    [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "dragSource", Justification = "Parameter is present so the signature matches that of System.Windows.DragDrop.DoDragDrop.")]
    public static DragDropEffects DoDragDrop(System.Runtime.InteropServices.ComTypes.IDataObject dataObject, DragDropEffects allowedEffects)
    {
        int[] finalEffect = new int[1];
        try
        {
            // (the ole method)
            NativeMethods.DoDragDrop(dataObject, new DropSource(), (int)allowedEffects, finalEffect);
        }
        finally
        {
            var virtualFileDataObject = dataObject as VirtualFileDataObject;
            if ((null != virtualFileDataObject) && !virtualFileDataObject.IsAsynchronous && virtualFileDataObject._inOperation)
            {
                // Call the end action and exit the operation
                if (null != virtualFileDataObject._endAction)
                {
                    virtualFileDataObject._endAction(virtualFileDataObject);
                }
                virtualFileDataObject._inOperation = false;
            }
        }
        return (DragDropEffects)(finalEffect[0]);
    }

(Пока игнорируйте цикл while- это можно улучшить, немного изменив метод sanitizaion.)

Может быть, можно как-то изменить метод SetData / класс DataObject - чтобы вместо ожидания потока и воссоздания файла (что на самом деле не то, что мне нужно), он возьмет строку и сделает немедленное копирование?

Еще немного кода:

[SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "Deliberate to provide obvious coupling.")]
public class FileDescriptor
{
    /// <summary>
    /// Gets or sets the name of the file.
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Gets or sets the (optional) length of the file.
    /// </summary>
    public Int64? Length { get; set; }

    /// <summary>
    /// Gets or sets the (optional) change time of the file.
    /// </summary>
    public DateTime? ChangeTimeUtc { get; set; }

    /// <summary>
    /// Gets or sets an Action that returns the contents of the file.
    /// </summary>
    public Action<Stream> StreamContents { get; set; }
}
public void SetData(IEnumerable<FileDescriptor> fileDescriptors)
{
    // Prepare buffer
    var bytes = new List<byte>();
    // Add FILEGROUPDESCRIPTOR header
    bytes.AddRange(StructureBytes(new NativeMethods.FILEGROUPDESCRIPTOR { cItems = (uint)(fileDescriptors.Count()) }));
    // Add n FILEDESCRIPTORs
    foreach (var fileDescriptor in fileDescriptors)
    {
        // Set required fields
        var FILEDESCRIPTOR = new NativeMethods.FILEDESCRIPTOR
        {
            cFileName = fileDescriptor.Name,
        };
        // Set optional timestamp
        if (fileDescriptor.ChangeTimeUtc.HasValue)
        {
            FILEDESCRIPTOR.dwFlags |= NativeMethods.FD_CREATETIME | NativeMethods.FD_WRITESTIME;
            var changeTime = fileDescriptor.ChangeTimeUtc.Value.ToLocalTime().ToFileTime();
            var changeTimeFileTime = new System.Runtime.InteropServices.ComTypes.FILETIME
            {
                dwLowDateTime = (int)(changeTime & 0xffffffff),
                dwHighDateTime = (int)(changeTime >> 32),
            };
            FILEDESCRIPTOR.ftLastWriteTime = changeTimeFileTime;
            FILEDESCRIPTOR.ftCreationTime = changeTimeFileTime;
        }
        // Set optional length
        if (fileDescriptor.Length.HasValue)
        {
            FILEDESCRIPTOR.dwFlags |= NativeMethods.FD_FILESIZE;
            FILEDESCRIPTOR.nFileSizeLow = (uint)(fileDescriptor.Length & 0xffffffff);
            FILEDESCRIPTOR.nFileSizeHigh = (uint)(fileDescriptor.Length >> 32);
        }
        // Add structure to buffer
        bytes.AddRange(StructureBytes(FILEDESCRIPTOR));
    }

    // Set CFSTR_FILEDESCRIPTORW
    SetData(FILEDESCRIPTORW, bytes);
    // Set n CFSTR_FILECONTENTS
    var index = 0;
    foreach (var fileDescriptor in fileDescriptors)
    {
        SetData(FILECONTENTS, index, fileDescriptor.StreamContents);
        index++;
    }
}

ОБНОВЛЕНИЕ: если я изменю дескриптор файла, чтобы содержать Func<string> вместо Action<Stream> Я могу вернуть путь, а затем в SetData(FILECONTENTS, index, fileDescriptor.StreamContents) - Я могу получить IntPtr этого пути к файлу и вернуть этот кортеж. Похоже, это "работает" - файл копируется в папку размещения и не загружается в память. Единственная "маленькая" проблема заключается в том, что проводник получает исключение (при перетаскивании на рабочий стол он перезагружается с черным экраном, при открытии проводника в открытой папке он закрывает это окно); Postmortem с WinDbg показывает, что я получаю "Нарушение прав доступа"... Но это насколько я могу получить... Я вижу, вы также можете изменить некоторые свойства, называемые tymed, чтобы быть TYMED.TYMED_FILE вместо стрима, но это для меня пока не получается полностью.

0 ответов

Другие вопросы по тегам