【问题标题】:Handle file drop in TShellListView descendant处理 TShellListView 后代中的文件删除
【发布时间】:2013-01-18 17:57:45
【问题描述】:

我正在尝试创建一个接受从 Windows 资源管理器中删除的文件的 TShellListView 的后代。我想在我的组件定义中处理拖放,而不必在任何使用该组件的应用程序中实现它(我找到了接受从 Windows 资源管理器中删除的文件的示例,但都是在应用程序/TForm 级别)。

我在构造函数中调用 DragAcceptFiles(),并为 WM_DROPFILES 定义了一个消息处理程序。但是,当我在示例项目中使用此组件并从 Windows 资源管理器中拖动文件时:

  1. 我看到“不接受”图标(带斜线的圆圈),而不是我可以删除文件的指示。

  2. 如果我尝试删除文件,我不会听到 Beep()。

我认为我没有正确提醒 Windows 我的控件愿意接受拖动的文件这一事实。谁能建议我缺少什么?

这是我的组件代码,去掉了不感兴趣的部分:

    unit LJLShellListView;

    interface

    type

      TLJLShellListView = class(TShellListView)
      private
        procedure WMDropFiles(var Msg: TWMDropFiles); message WM_DROPFILES;
      published
        constructor Create(AOwner: TComponent);
      end;

    implementation

    uses ShellAPI;

    constructor TLJLShellListView.Create(AOwner: TComponent);
    begin
      inherited Create(AOwner);
      DragAcceptFiles(self.Handle, True);
    end;

    procedure TLJLShellListView.WMDropFiles(var Msg: TWMDropFiles);
    begin
      Beep();  // I never hear this.
    end;

    end.

【问题讨论】:

  • 也许控件正在重新创建并且句柄正在改变?
  • @Sertac:HandleNeeded 调用会阻止这种情况吗?
  • @jachguate - 不,访问句柄会产生完全相同的效果。在继承后调用“DragAcceptFiles”行到覆盖的 CreateWnd 将是解决方案。
  • @LarryLustig:TWinControl.Handle 属性提供的HWND不是持久的。它可以在组件的生命周期内被销毁和重新创建,甚至多次。每次TShellListView 分配一个新的HWND 时,您都必须再次调用DragAcceptFiles(TRUE)。覆盖CreateWnd() 允许您这样做。为了更好地衡量,您还应该在 HWND 被销毁之前覆盖 DestroyWnd() 以调用 DragAcceptFiles(FALSE)
  • @jachguate:更准确地说,CreateHandle() 被首先调用,它调用CreateWnd(),它调用CreateWindowHandle() 来创建实际的HWND。稍后,DestroyHandle()被调用,它调用DestroyWnd(),它调用DestroyWindowHandle()来销毁HWND。还有一些情况是直接调用DestroyWindowHandle()而不调用DestroyWnd(),比如TWinControl析构函数。

标签: delphi drag-and-drop delphi-2010 windows-shell


【解决方案1】:

问题代码中的DragAcceptFiles 调用需要ShellListView 的窗口句柄。当代码访问 wincontrol 的句柄时,VCL 检查窗口是否已创建,如果没有,VCL 调用CreateHandle 并继续创建窗口并返回其句柄。在这个阶段,问题中的代码成功注册了ShellListView的窗口进行文件拖放。但是有一个问题,控件还没有父级。当它要成为父级时,本机控件将被销毁,然后在其新父级中重新创建,当然会获取不同的句柄,从而使注册状态无效。

控件也可能由于各种其他原因而重新创建。因此,最好将我们的代码放在重写的CreateWndDestroyWnd 方法中,只要句柄发生变化或窗口即将被销毁,我们的代码就会运行。

【讨论】:

  • 谢谢。本机 Windows 控件具有与 VCL 控件完全不同的存在这一事实对我来说是新闻。您的解释使我有可能了解(至少,足够)内部发生的事情。
【解决方案2】:

正如@SertacAkyuz 所说,您问题的解决方案是覆盖CreateWnd() 方法来调用DragAcceptFiles(),而不是在构造函数中这样做。我只想提一下,在 Windows Vista 及更高版本下,用户界面特权隔离 (UIPI) 生效,因此如果您的应用在 UAC 下以提升状态运行,那么您还需要调用 ChangeWindowMessageFilter() 以允许 WM_COPYGLOBALDATA ( $0049) 和 WM_DROPFILES 消息从低权限进程(如 Windows 资源管理器)传递到您的应用程序,否则拖放将无法正常工作。

WM_DROPFILES 已经被弃用了很长时间,取而代之的是更新的IDropTarget 接口,它更加强大和灵活。新代码应使用IDropTarget 而不是WM_DROPFILES。有关详细信息,请参阅 MSDN:

Transferring Shell Objects with Drag-and-Drop and the Clipboard

IDropTarget 提供而WM_DROPFILES 不具备的灵活功能之一是能够使用单个IDropTarget 对象来接受拖放,不仅在特定的HWND 上,而且在您的应用程序的 .exe 文件本身,以及在 Shell 弹出菜单中使用它,甚至允许其他应用程序直接将数据传递给您的应用程序,而无需使用窗口消息或其他 IPC 机制。

【讨论】:

  • 我的回答可能对 IDropTarget 有所帮助:stackoverflow.com/questions/4354071/…
  • 感谢您对 IDropTarget 的建议。将来我可能会使用这种技术而不是 WM_DROPFILES 重新实现这个控件。然而,对于我的直接案例,我正在创建几个 TShellListView 实例并让用户“排序”一堆传入文件,WM_DROPFILES 具有我需要的所有功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-07-16
  • 2017-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多