C# WPF - 将远程文件拖放到 Windows 资源管理器

Riz*_*Riz 6 c# wpf drag-and-drop

我正在开发一个 WPF 应用程序,它显示存储在远程服务器上的文件列表(就像保管箱一样)。我希望用户将它们拖放到桌面或任何文件夹上。有很多与此相关的问题,但没有一个真正给出完整的解决方案。

这是我正在使用的完整代码https://github.com/dotriz/VirtualDragDrop/

截屏

如果文件存储在本地系统上,这是一个非常简单的任务,但这里的文件位于远程服务器上,需要先下载。

唯一与此相关的文章是 13 年前发布的https://dlaa.me/blog/post/9923072。它也有一些问题,比如

  • 当我们将它拖到 Windows 资源管理器上时,它在调试模式下会出错,但当我们直接运行 exe 时工作正常。可能是什么问题?

无效的 FORMATETC 结构(HRESULT 异常:0x80040064 (DV_E_FORMATETC)

  • 如果我们将文件拖到 Slack 等应用程序中,它会在文件下载时挂起。但当文件拖放到 Windows 资源管理器时,工作正常(如果我们直接运行 exe)。

以下是标签的 MouseDown 事件中使用的代码。它使用上面给出的链接中的VirtualFileDataObject类

private void VirtualFile2_MouseButtonDown(object sender, MouseButtonEventArgs e)
{
    var virtualFileDataObject = new VirtualFileDataObject();

    virtualFileDataObject.SetData(new VirtualFileDataObject.FileDescriptor[]
    {
        new VirtualFileDataObject.FileDescriptor
        {
            Name = "test.zip",
            ChangeTimeUtc = DateTime.Now.AddDays(-1),
            StreamContents = stream =>
                {
                    using(var webClient = new WebClient())
                    {
                        var data = webClient.DownloadData("https://somesite.com/test.zip");
                        stream.Write(data, 0, data.Length);
                    }

                }
        },
    });

    DoDragDropOrClipboardSetDataObject(e.ChangedButton, VirtualFile2, virtualFileDataObject, DragDropEffects.Copy);
}

private static void DoDragDropOrClipboardSetDataObject(MouseButton button, DependencyObject dragSource, VirtualFileDataObject virtualFileDataObject, DragDropEffects allowedEffects)
{
    try
    {
        VirtualFileDataObject.DoDragDrop(dragSource, virtualFileDataObject, allowedEffects);
    }
    catch (COMException)
    {
        // Failure; no way to recover
    }
}
Run Code Online (Sandbox Code Playgroud)

Sim*_*ier 1

我可以成功使用提供的复制程序。当在不调试的情况下运行时,它至少可以在 Windows 10、11、x86 x64 的发布和调试配置中运行。

DV_E_FORMATETC (0x80040064 ) 只是一个标准错误代码,这意味着数据对象不支持所请求的剪贴板格式。此错误在复制/粘贴、拖放和剪贴板操作中很常见。

根本不起作用的是:

  • 在拖放处理代码中放置断点,例如在void System.Runtime.InteropServices.ComTypes.IDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium)调试模式下运行时。
  • 在调试模式下运行时,拖放处理代码中引发异常。

我认为您无法调试 .NET 程序(使用当今的工具),并在DoDragDrop 函数调用中抛出断点或异常。

DoDragDrop是一个大的模态循环,它吃掉发送到窗口的大部分消息。不幸的是,由于某种原因,.NET 调试器工具或异常处理程序(在 clr.dll 中)似乎也在等待此循环。taskkill /im myprocess.exe /f因此,命中断点或处理异常会在程序本身(以及调试器中)中产生挂起,您只能通过硬方式(例如命令行)终止被调试进程来打破死锁。

所以我的建议是:

  • 避免在这些代码部分放置断点

  • 将这些函数包装在try/catch块中并使用一些非侵入式跟踪机制,如下所示:

      void System.Runtime.InteropServices.ComTypes.IDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium)
      {
          try
          {
              ....
          }
          catch(Exception ex)
          {
              Trace.WriteLine("GetData Error: " + ex);
              throw; // rethrow as is
          }
      }
    
    Run Code Online (Sandbox Code Playgroud)