将文件从我的应用程序拖放到另一个应用程序的 Win32 API 过程

The Win32 API procedure of drag-and-dropping a file from my app to another app

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

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

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

  1. 当鼠标在 UI 控件上时,调用 SetCapture 来捕获事件。
  2. IDropSourceQueryContinueDrag 保持 returning S_OK 直到鼠标按钮弹起,然后 DRAGDROP_S_DROP.
  3. 我的 "DataObject" 实施 IDataObjectEnumFormatEtc。其中我return只有一个FORMATETC.

        new FORMATETC()
        {
            cfFormat = CF_HDROP,
            ptd = IntPtr.Zero,
            dwAspect = DVASPECT.DVASPECT_ICON,
            lindex = -1,
            tymed = TYMED.TYMED_FILE
        }
    
    1. QueryGetData中,如果formattymedTYMED_FILE,returnS_OK意思是我我正在拖一个文件。否则 return DV_E_TYMED 意味着我没有那种类型的数据。

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

          medium = new STGMEDIUM();
          medium.tymed = TYMED.TYMED_FILE;
          medium.unionmember = Marshal.StringToHGlobalUni(@"C:\test.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 没有。为什么记事本不要求这样做?

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

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.

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

如果您开始拖动鼠标左键,return S_OK 如果 grfKeyState 参数包含 MK_LBUTTON 标志,并且 return DRAGDROP_S_DROP 如果 MK_LBUTTON 标志被清除。 Return DRAGDROP_S_CANCEL 如果 fEscapePressed 参数为真。这记录在 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?

CF_HDROP不能使用TYMED_FILE,必须使用TYMED_HGLOBAL。分配的 HGLOBAL 的内容必须是一个 DROPFILES 结构,后跟一个以双空结尾的文件路径列表。这记录在 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:\temp1.txt and c:\temp2.txt are being transferred, the character array looks like this:

c:\temp1.txt'[=50=]'c:\temp2.txt'[=50=]''[=50=]'

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

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

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