从我的应用程序的 ListView 拖放到外部应用程序(例如 Windows Explorer)

Drag and drop from my app's ListView to external apps (such as Windows Explorer)

我有一个 ListView 包含文件列表:

hList = CreateWindowEx(0, WC_LISTVIEW, L"", WS_CHILD | WS_VISIBLE | LVS_REPORT, 0, 0, 500, 400, hWnd, (HMENU)ID_LISTVIEW, hInst, NULL);

假设它包含一行 c:\temp\hello.txt

如何允许将此文件从我的应用程序 ListView 拖放到外部应用程序(例如 Windows Explorer),如 "Copy"?

问题的 GUI 部分可能很明显 (or not?) 通过:

case WM_NOTIFY:
{
    ...
    case LVN_BEGINDRAG:

但这里的问题是关于将文件实际发送到外部应用程序,例如 Windows Explorer。如何做到这一点?

实施 IDropSourceIDropSourceNotify(可选)和 IDataObject 并调用 DoDragDrop:

If you are developing an application that can act as a data source for an OLE drag-and-drop operation, you must call DoDragDrop when you detect that the user has started an OLE drag-and-drop operation.

The DoDragDrop function enters a loop in which it calls various methods in the IDropSource and IDropTarget interfaces. (For a successful drag-and-drop operation, the application acting as the data source must also implement IDropSource, while the target application must implement IDropTarget.)

SHCreateDataObject can provide a IDataObject instance for you but you often end up having to code your own because the shell provided implementation is not perfect.

IDragSourceHelper 可以帮助您获得精美的拖动图像。

另请参阅:

下面是一些实现执行此类 ListView 文件拖放所需的所有代码的代码。首先是一些包括:

#define CINTERFACE
#define COBJMACROS
#include "ShObjIdl.h"
#include "ShlObj.h"
#include "oleidl.h"

然后这个在WinMain函数中,初始化OLE操作。

OleInitialize(NULL);
InitCommonControls();

然后,IDropSource部分:

typedef struct __DSV_TDropSource {
    IDropSource     This;
    IDropSourceVtbl Func;
    ULONG           RefCnt;
} __DSV_TDropSource;

HRESULT WINAPI __DSV_QueryInterface(IDropSource *This, REFIID riid, void **ppvObject)
{
    IUnknown *punk = NULL;

    if (riid == IID_IUnknown)
    {
        punk = (IUnknown*)This;
    }
    else if (riid == IID_IDropSource)
    {
        punk = (IUnknown*)This;
    }

    *ppvObject = punk;

    if (punk)
    {
        IUnknown_AddRef(punk);
        return S_OK;
    }
    else {
        return E_NOINTERFACE;
    }
}

ULONG WINAPI __DSV_AddRef(IDropSource *This)
{
    __DSV_TDropSource *pThis = (__DSV_TDropSource*)This;
    return pThis->RefCnt++;
}

ULONG WINAPI __DSV_Release(IDropSource *This)
{
    __DSV_TDropSource *pThis = (__DSV_TDropSource*)This;

    LONG iRes = (LONG)pThis->RefCnt - 1;
    if (iRes < 1) { iRes = 0; }
    pThis->RefCnt = iRes;

    if (iRes == 0) { free(pThis); }
    return iRes;
}

HRESULT WINAPI __DSV_QueryContinueDrag(IDropSource *This, BOOL fEscapePressed, DWORD grfKeyState)
{
    if (fEscapePressed) { return DRAGDROP_S_CANCEL; }

    if (!(grfKeyState & (MK_LBUTTON | MK_RBUTTON))) { return DRAGDROP_S_DROP; }

    return S_OK;
}

HRESULT WINAPI __DSV_GiveFeedback(IDropSource *This, DWORD dwEffect)
{
    return DRAGDROP_S_USEDEFAULTCURSORS;
}

IDropSource* CreateDropSource()
{
    __DSV_TDropSource *pResu = (__DSV_TDropSource*)malloc(sizeof(__DSV_TDropSource));
    if (!pResu) { return 0; }

    pResu->This.lpVtbl = &(pResu->Func);
    pResu->Func.QueryInterface = __DSV_QueryInterface;
    pResu->Func.AddRef = __DSV_AddRef;
    pResu->Func.Release = __DSV_Release;
    pResu->Func.QueryContinueDrag = __DSV_QueryContinueDrag;
    pResu->Func.GiveFeedback = __DSV_GiveFeedback;

    pResu->RefCnt = 1;
    return (IDropSource*)pResu;
}


void** GetFileUiObject(TCHAR *ptFile, REFIID riid)
{

    void** pInterfaceResu = 0;
    IShellFolder *pFolder;
    PIDLIST_RELATIVE pFile;
    PIDLIST_ABSOLUTE pITEMDLIST_File;
    HRESULT iResu;

    pITEMDLIST_File = ILCreateFromPath(ptFile);
    if (!pITEMDLIST_File)
        return 0;

    iResu = SHBindToParent(pITEMDLIST_File, IID_IShellFolder, (void**)&pFolder, (PCUITEMID_CHILD*)&pFile);
    if (iResu != S_OK)
        return 0;

    const ITEMIDLIST* pArray[1] = { pFile };
    iResu = IShellFolder_GetUIObjectOf(pFolder, NULL, 1, pArray, riid, NULL, (void**)&pInterfaceResu);
    if (iResu != S_OK)
        return 0;

    IShellFolder_Release(pFolder);

    return pInterfaceResu;
}

最后,这应该在消息循环中执行:

case WM_NOTIFY:
        pdi = (NMLVDISPINFO*) lParam;
        nmlv = (NMLISTVIEW*) lParam;
        switch (pdi->hdr.code)
        {

        case LVN_BEGINDRAG:
            wstring fName = L"C:\test.txt";
            IDataObject *pObj;
            IDropSource *pSrc;
            pObj = (IDataObject*)GetFileUiObject(LPWSTR(fName.c_str()), IID_IDataObject);
            if (!pObj)
                break;

            pSrc = CreateDropSource();
            if (!pSrc)
            {
                IDataObject_Release(pObj);
                break;
            }

            DWORD dwEffect;
            DoDragDrop(pObj, pSrc, DROPEFFECT_COPY | DROPEFFECT_LINK, &dwEffect);

            IDropSource_Release(pSrc);
            IDataObject_Release(pObj);
            break;