将文件从我的应用程序拖放到另一个应用程序的 Win32 API 过程 [英] The Win32 API procedure of drag-and-dropping a file from my app to another app

查看:21
本文介绍了将文件从我的应用程序拖放到另一个应用程序的 Win32 API 过程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简而言之,尝试在不使用 System.Windows.Forms 的情况下执行以下两行 C# 代码的操作,因为它是 .NET Core 而不是 WinForms 项目.

var data = new System.Windows.Forms.DataObject(System.Windows.Forms.DataFormats.FileDrop, new string[] { @"C:	est.txt"});dummyControl.DoDragDrop(data, System.Windows.Forms.DragDropEffects.Copy);

但它不起作用.我做错了什么?我的程序是,

  1. 当鼠标在 UI 控件上按下时,调用 SetCapture 来捕获事件.
  2. IDropSourceQueryContinueDrag 不断返回S_OK 直到鼠标按下,然后DRAGDROP_S_DROP.
  3. 我实现 IDataObject 的DataObject"有 EnumFormatEtc.其中我只返回一个 FORMATETC.

     new FORMATETC(){cfFormat = CF_HDROP,ptd = IntPtr.零,dwAspect = DVASPECT.DVASPECT_ICON,lindex = -1,tymed = TYMED.TYMED_FILE}

    1. QueryGetData中,如果formattymedTYMED_FILE,则返回S_OK 表示我正在拖动文件.否则返回 DV_E_TYMED 意味着我没有那种类型的数据.

    2. GetData中,如果formattymedTYMED_FILE,我设置一个文件像这样

       medium = new STGMEDIUM();medium.tymed = TYMED.TYMED_FILE;medium.unionmember = Marshal.StringToHGlobalUni(@"C:	est.txt");medium.pUnkForRelease = IntPtr.Zero;

我尝试拖放到记事本.问题是,

  1. 当鼠标按下事件没有调用DoDragDrop 时,鼠标被捕获并接收鼠标按下事件.但是当 DoDragDrop 被调用时,QueryContinueDrag 被无休止地调用并且我没有得到鼠标向上事件,即使在我释放鼠标之后.我试图在另一个线程中调用 DoDragDrop,但没有奏效.
  2. 我收到类似 QueryGetData(TYMED_HGLOBAL)QueryGetData(TYMED_HGLOBAL, TYMED_ISTREAM, TYMED_GDI, TYMED_MFPICT, TYMED_ENHMF) 的回调,但不适用于 TYMED_FILE.为什么记事本不要求这样做?

拖放似乎不必要地复杂,但目前,我只对拖放文件感兴趣,所以我不想实现除此之外的部分.上面的程序或假设有什么错误?

解决方案

当鼠标按下事件未调用 DoDragDrop 时,将捕获鼠标并接收鼠标抬起事件.但是当 DoDragDrop 被调用时,QueryContinueDrag 被无休止地调用并且我没有得到鼠标向上事件,即使在我释放鼠标之后也是如此.

您不会收到鼠标抬起事件,因为 DoDragDrop() 会阻止您的 UI 消息循环,直到拖动操作完成.因此,您需要使用为 QueryContinueDrag() 实现提供的输入标志来决定是继续拖动、执行放置还是中止操作.

如果开始拖动鼠标左键,如果 grfKeyState 参数包含 MK_LBUTTON 标志,则返回 S_OK,并返回 DRAGDROP_S_DROP 如果 MK_LBUTTON 标志被清除.如果 fEscapePressed 参数为真,则返回 DRAGDROP_S_CANCEL.这在 MSDN 上有记录:

IDropSource::QueryContinueDrag 方法

<块引用>

参数

fEscapePressed

指示自上次调用 QueryContinueDragDoDragDrop 后是否按下了 Esc 键,如果这是第一次调用 QueryContinueDrag.TRUE 值表示最终用户按下了退出键;FALSE 值表示它没有被按下.

grfKeyState

键盘上的键盘修饰键的当前状态.可能的值可以是任何标志 MK_CONTROL、MK_SHIFT、MK_ALT、MK_BUTTON、MK_LBUTTON 的组合,MK_MBUTTON 和 MK_RBUTTON.

返回值

此方法可以返回以下值.

S_OK
拖动操作应继续.如果未检测到错误,未释放开始拖放操作的鼠标按钮,并且未检测到 Esc 键,则会出现此结果.

DRAGDROP_S_DROP
拖放操作应在完成拖动操作后发生.如果grfKeyState 指示启动拖放操作的键已被释放,则会出现此结果.

DRAGDROP_S_CANCEL
应取消拖动操作,不发生任何放置操作.如果 fEscapePressed 为 TRUE,表示已按下 Esc 键,则会出现此结果.

备注

DoDragDrop 函数在拖放操作期间检测到键盘或鼠标按钮状态发生变化时会调用 QueryContinueDrag.QueryContinueDrag 必须根据参数grfKeyStatefEscapePressed 的内容确定拖放操作是继续、取消还是完成.

<小时><块引用>

我收到了 QueryGetData(TYMED_HGLOBAL) 或 QueryGetData(TYMED_HGLOBAL, TYMED_ISTREAM, TYMED_GDI, TYMED_MFPICT, TYMED_ENHMF) 之类的回调,但不适用于 TYMED_FILE.为什么记事本不要求这样做?

CF_HDROP 不能使用 TYMED_FILE,必须使用 TYMED_HGLOBAL.并且分配的HGLOBAL的内容必须是DROPFILES 结构后跟双空终止的文件路径列表.这在 MSDN 上有记录:

Shell 剪贴板格式

<块引用>

CF_HDROP

这种剪贴板格式用于传输一组现有文件的位置.与其他 Shell 格式不同,它是预定义的,因此无需调用 RegisterClipboardFormat.数据由包含全局内存对象的STGMEDIUM 结构组成.该结构的 hGlobal 成员指向一个 DROPFILES 结构作为其 hGlobal 成员.

DROPFILES 结构的 pFiles 成员包含对包含文件名的双空终止字符数组的偏移量.正在从数据对象中提取 CF_HDROP 格式,您可以使用 DragQueryFile 从全局内存对象中提取单个文件名.如果您要创建CF_HDROP 格式以放置在数据对象中,则需要构造文件名数组.

文件名数组由一系列字符串组成,每个字符串包含一个文件的完全限定路径,包括终止符 NULL.将额外的空字符附加到最终字符串以终止数组. 例如,如果文件 c: emp1.txtc: emp2.​​txt正在传输,字符数组如下所示:

<块引用>

c: emp1.txt''c: emp2.​​txt''''

注意
在此示例中,'' 用于表示空字符,而不是应包含的文字字符.

如果对象作为拖放操作的一部分被复制到剪贴板,则 DROPFILES 结构的 pt 成员包含点的坐标对象被丢弃.您可以使用 DragQueryPoint 来提取光标坐标.

如果此格式存在于数据对象中,则 OLE 拖动循环会使用非 OLE 放置目标模拟 WM_DROPFILES 功能.如果您的应用程序是 Windows 3.1 系统上拖放操作的来源,这一点很重要.

In short, trying to do what the following two lines of C# code do without using System.Windows.Forms, because it is .NET Core and not a WinForms project.

var data = new System.Windows.Forms.DataObject(
    System.Windows.Forms.DataFormats.FileDrop, new string[] { @"C:	est.txt"});
dummyControl.DoDragDrop(data, System.Windows.Forms.DragDropEffects.Copy);

But it does not work. What have I done wrong? My procedure was,

  1. When mouse is down on a UI control, call SetCapture to capture the event.
  2. The IDropSource's QueryContinueDrag keep returning S_OK until the mouse button is up, and then DRAGDROP_S_DROP.
  3. My "DataObject" implementing IDataObject has EnumFormatEtc. In which I return only one FORMATETC.

        new FORMATETC()
        {
            cfFormat = CF_HDROP,
            ptd = IntPtr.Zero,
            dwAspect = DVASPECT.DVASPECT_ICON,
            lindex = -1,
            tymed = TYMED.TYMED_FILE
        }
    

    1. In QueryGetData, if the format's tymed is TYMED_FILE, return S_OK meaning I am dragging a file. Otherwise return DV_E_TYMED meaning I don't have that type of data.

    2. In GetData, if the format's tymed is TYMED_FILE, I set a file like this

          medium = new STGMEDIUM();
          medium.tymed = TYMED.TYMED_FILE;
          medium.unionmember = Marshal.StringToHGlobalUni(@"C:	est.txt");
          medium.pUnkForRelease = IntPtr.Zero;
      

I tried to drag-and-drop to Notepad. Problems are,

  1. When DoDragDrop is not called on the mouse down event, mouse is captured and mouse up event is received. But when DoDragDrop is called, QueryContinueDrag is endlessly called and I don't get the mouse up event, even after I released the mouse. I tried to call DoDragDrop in another thread, but that did not work.
  2. I get callbacks like QueryGetData(TYMED_HGLOBAL) or QueryGetData(TYMED_HGLOBAL, TYMED_ISTREAM, TYMED_GDI, TYMED_MFPICT, TYMED_ENHMF), but not for TYMED_FILE. Why isn't Notepad requesting that?

The drag and drop seems unnecessarily complex, but for now, I am only interested in dragging a file, so I did not want to implement parts other than that. What in the above procedure or assumptions are wrong?

解决方案

When DoDragDrop is not called on the mouse down event, mouse is captured and mouse up event is received. But when DoDragDrop is called, QueryContinueDrag is endlessly called and I don't get the mouse up event, even after I released the mouse.

You don't get a mouse up event because DoDragDrop() blocks your UI message loop until the drag operation is completed. So you need to use the input flags given to your QueryContinueDrag() implementation to decide whether to continue dragging, perform the drop, or abort the operation.

If you start the drag on a left mouse down, return S_OK if the grfKeyState parameter includes the MK_LBUTTON flag, and return DRAGDROP_S_DROP if the MK_LBUTTON flag is cleared. Return DRAGDROP_S_CANCEL if the fEscapePressed parameter is true. This is documented on MSDN:

IDropSource::QueryContinueDrag method

Parameters

fEscapePressed

Indicates whether the Esc key has been pressed since the previous call to QueryContinueDrag or to DoDragDrop if this is the first call to QueryContinueDrag. A TRUE value indicates the end user has pressed the escape key; a FALSE value indicates it has not been pressed.

grfKeyState

The current state of the keyboard modifier keys on the keyboard. Possible values can be a combination of any of the flags MK_CONTROL, MK_SHIFT, MK_ALT, MK_BUTTON, MK_LBUTTON, MK_MBUTTON, and MK_RBUTTON.

Return Value

This method can return the following values.

S_OK
The drag operation should continue. This result occurs if no errors are detected, the mouse button starting the drag-and-drop operation has not been released, and the Esc key has not been detected.

DRAGDROP_S_DROP
The drop operation should occur completing the drag operation. This result occurs if grfKeyState indicates that the key that started the drag-and-drop operation has been released.

DRAGDROP_S_CANCEL
The drag operation should be canceled with no drop operation occurring. This result occurs if fEscapePressed is TRUE, indicating the Esc key has been pressed.

Remarks

The DoDragDrop function calls QueryContinueDrag whenever it detects a change in the keyboard or mouse button state during a drag-and-drop operation. QueryContinueDrag must determine whether the drag-and-drop operation should be continued, canceled, or completed based on the contents of the parameters grfKeyState and fEscapePressed.


I get callbacks like QueryGetData(TYMED_HGLOBAL) or QueryGetData(TYMED_HGLOBAL, TYMED_ISTREAM, TYMED_GDI, TYMED_MFPICT, TYMED_ENHMF), but not for TYMED_FILE. Why isn't Notepad requesting that?

You can't use TYMED_FILE for CF_HDROP, you must use TYMED_HGLOBAL. And the content of the allocated HGLOBAL must be a DROPFILES struct followed by a double-null-terminated list of file paths. This is documented on MSDN:

Shell Clipboard Formats

CF_HDROP

This clipboard format is used when transferring the locations of a group of existing files. Unlike the other Shell formats, it is predefined, so there is no need to call RegisterClipboardFormat. The data consists of an STGMEDIUM structure that contains a global memory object. The structure's hGlobal member points to a DROPFILES structure as its hGlobal member.

The pFiles member of the DROPFILES structure contains an offset to a double null-terminated character array that contains the file names. If you are extracting a CF_HDROP format from a data object, you can use DragQueryFile to extract individual file names from the global memory object. If you are creating a CF_HDROP format to place in a data object, you will need to construct the file name array.

The file name array consists of a series of strings, each containing one file's fully qualified path, including the terminating NULL character. An additional null character is appended to the final string to terminate the array. For example, if the files c: emp1.txt and c: emp2.txt are being transferred, the character array looks like this:

c: emp1.txt''c: emp2.txt''''

Note
In this example, '' is used to represent the null character, not the literal characters that should be included.

If the object was copied to the clipboard as part of a drag-and-drop operation, the pt member of the DROPFILES structure contains the coordinates of the point where the object was dropped. You can use DragQueryPoint to extract the cursor coordinates.

If this format is present in a data object, an OLE drag loop simulates WM_DROPFILES functionality with non-OLE drop targets. This is important if your application is the source of a drag-and-drop operation on a Windows 3.1 system.

这篇关于将文件从我的应用程序拖放到另一个应用程序的 Win32 API 过程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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