【问题标题】:Drag Drop Win API 32拖放 Win API 32
【发布时间】:2012-11-27 01:49:15
【问题描述】:

我正在尝试将一个 ListView 项从我的程序拖放到另一个程序中(例如将路径拖到 VLC 中,它会播放一个视频文件)。我正在使用 CF_HDROP 剪贴板格式。 CopySelection 将 STGMEDIUM hglobal 变量设置为 DROPFILES 结构。

void CopySelection(HWND hwndList, STGMEDIUM &stgmed)
{
    HGLOBAL hMem;
    DROPFILES  *ptr;
    DROPFILES dfiles;
    POINT p;

    // get the selection inside the list control
    int iPos = SendMessage(hwndList, LVM_GETNEXTITEM, (WPARAM)-1,(LPARAM)LVNI_SELECTED);
    cout << "iPos: " << iPos << endl;
    LVITEM item;
    char buffer[256];
    string fileDir = "";
    item.iItem = iPos;
    item.iSubItem = 1;
    item.cchTextMax = 256;
    item.pszText = buffer;
    item.mask = LVIF_TEXT;

    ListView_GetItem(hwndList, &item);
    fileDir += string(item.pszText);
    fileDir += "\\";
    item.iItem = iPos;
    item.iSubItem = 0;

    ListView_GetItem(hwndList, &item);
    fileDir += string(item.pszText);
    item.iItem = iPos;
    item.iSubItem = 2;

    ListView_GetItem(hwndList, &item);
    fileDir += string(item.pszText);

    cout << "fileDir: " << fileDir << endl;

    hMem = GlobalAlloc(GHND, sizeof(DROPFILES));
    ptr  = (DROPFILES *)GlobalLock(hMem);

    dfiles.fNC = TRUE;
    dfiles.fWide = FALSE;
    memcpy((void*)&dfiles.pFiles, (fileDir.c_str()+'\0'), fileDir.size()+1);

    GetCursorPos(&p);
    dfiles.pt=p;

    // copy the selected text and nul-terminate
    memcpy(ptr, (void*)&dfiles, sizeof(DROPFILES));

    GlobalUnlock(hMem);

    stgmed.hGlobal = hMem;

    //return hMem;
}

但这似乎会导致段错误。这是调用它的 MouseMove 列表消息代码:

case WM_MOUSEMOVE:
{
    // stop drag-drop from happening when the mouse is released.
    if(fMouseDown)
    {
        IDataObject *pDataObject;
        IDropSource *pDropSource;
        DWORD        dwEffect;
        DWORD        dwResult;

        FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
        STGMEDIUM stgmed = { TYMED_HGLOBAL   , { 0 }, 0 };

        // transfer the current selection into the IDataObject
        CopySelection(hwnd, stgmed);
        cout << "DO WE?" << endl;

        // Create IDataObject and IDropSource COM objects
        CreateDropSource(&pDropSource);
        CreateDataObject(&fmtetc, &stgmed, 1, &pDataObject);
        //
        //  ** ** ** The drag-drop operation starts here! ** ** **
        //
        //dwResult = DoDragDrop(pDataObject, pDropSource, DROPEFFECT_COPY|DROPEFFECT_MOVE, &dwEffect);
        dwResult = DoDragDrop(pDataObject, pDropSource, DROPEFFECT_COPY, &dwEffect);

        // success!
        if(dwResult == DRAGDROP_S_DROP)
        {
            if(dwEffect & DROPEFFECT_MOVE)
            {
                // remove selection from list control
            }
            else if(dwEffect & DROPEFFECT_LINK)
            {
            }
        }
        // cancelled
        else if(dwResult == DRAGDROP_S_CANCEL)
        {
        }

        pDataObject->Release();
        pDropSource->Release();

        ReleaseCapture();
        fMouseDown = FALSE;
        fDidDragDrop = TRUE;
    }

代码格式正确(我已经检查过),但不确定为什么这不起作用。我什至使用正确的 OLE 剪贴板格式来实现这一点?我不确定要使用哪个,而且我找到的文档也不是很好。

干杯, 抢

附:我试图调整这个例子: http://www.catch22.net/tuts/drop-source

不同之处在于,他只是移动文本,而我试图移动文件列表(例如在窗口中选择图标并拖到 vid 播放器上)。

【问题讨论】:

  • 你能附加一个调试器看看它崩溃的地方吗?
  • 我会尝试,但是我在 Boost::filesystem 中使用带有路径类的 try-catch 异常循环,并且由于某种该死的原因,Code:Blocks 附带的调试器看不到我处理这些异常,因此它会在我无法访问的每个目录(通常是系统目录)上停止。如果有人也可以在那个方面帮助我,它会让这变得容易得多。
  • 好吧,它不喜欢这一行:memcpy(ptr, (void*)&dfiles, sizeof(DROPFILES));我正在尝试将 DROPFILES 结构复制到我拥有的 HGLOBAL ptr。
  • 第一个 memcpy() 将数据复制到无效的内存地址,因此第二个 memcpy() 可能试图访问被第一个 memcpy() 破坏的内存。

标签: c++ api winapi ole


【解决方案1】:

您没有为HGLOBAL 块分配足够的内存。您只分配了足够的内存来保存DROPFILES 本身,但没有内存来保存伴随它的文件名。但即使您正确分配内存,您也没有正确使用DROPFILES::pFiles 字段。它需要指定从文件名列表开始的DROPFILES 结构开始的偏移量,但您将其视为内存地址。

试试这个:

HGLOBAL CopySelection(HWND hwndList)
{
    // get the selection inside the list control

    int iPos = SendMessage(hwndList, LVM_GETNEXTITEM, (WPARAM)-1,(LPARAM)LVNI_SELECTED);
    if (iPos == -1)
        return NULL;

    cout << "iPos: " << iPos << endl;

    LVITEM item = {0};
    char buffer[256];
    string fileDir;

    item.cchTextMax = 256;
    item.pszText = buffer;
    item.mask = LVIF_TEXT;

    item.iItem = iPos;
    item.iSubItem = 1;
    ListView_GetItem(hwndList, &item);
    fileDir = item.pszText;
    fileDir += "\\";

    item.iItem = iPos;
    item.iSubItem = 0;
    ListView_GetItem(hwndList, &item);
    fileDir += item.pszText;

    item.iItem = iPos;
    item.iSubItem = 2;
    ListView_GetItem(hwndList, &item);
    fileDir += item.pszText;

    cout << "fileDir: " << fileDir << endl;

    // +2 = the filename's null terminator and the file list's null terminator
    HGLOBAL hMem = GlobalAlloc(GHND, sizeof(DROPFILES) + fileDir.length() + 2);
    if (!hMem)
        return NULL;

    DROPFILES *dfiles = (DROPFILES*) GlobalLock(hMem);
    if (!dfiles)
    {
        GlobalFree(hMem);
        return NULL;
    }

    dfiles->pFiles = sizeof(DROPFILES);
    GetCursorPos(&(dfiles->pt));
    dfiles->fNC = TRUE;
    dfiles->fWide = FALSE;
    memcpy(&dfiles[1], fileDir.c_str(), fileDir.length());

    GlobalUnlock(hMem);
    return hMem;
}

.

case WM_MOUSEMOVE:
{
    // stop drag-drop from happening when the mouse is released.
    if (fMouseDown)
    {
        IDataObject *pDataObject;
        IDropSource *pDropSource;
        DWORD        dwEffect;
        DWORD        dwResult;

        FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
        STGMEDIUM stgmed = { TYMED_HGLOBAL   , { 0 }, 0 };

        // transfer the current selection into the IDataObject
        stgmed.hGlobal = CopySelection(hwnd);
        if (stgmed.hGlobal)
        {
            cout << "DO WE?" << endl;

            // Create IDataObject and IDropSource COM objects
            CreateDropSource(&pDropSource);
            CreateDataObject(&fmtetc, &stgmed, 1, &pDataObject);
            //
            //  ** ** ** The drag-drop operation starts here! ** ** **
            //
            //dwResult = DoDragDrop(pDataObject, pDropSource, DROPEFFECT_COPY|DROPEFFECT_MOVE, &dwEffect);
            dwResult = DoDragDrop(pDataObject, pDropSource, DROPEFFECT_COPY, &dwEffect);

            // success!
            if(dwResult == DRAGDROP_S_DROP)
            {
                if(dwEffect & DROPEFFECT_MOVE)
                {
                    // remove selection from list control
                }
                else if(dwEffect & DROPEFFECT_LINK)
                {
                }
            }
            // cancelled
            else if(dwResult == DRAGDROP_S_CANCEL)
            {
            }

            pDataObject->Release();
            pDropSource->Release();

            ReleaseCapture();
            fMouseDown = FALSE;
            fDidDragDrop = TRUE;
        }
    }

如果您想一次拖动多个选定的文件,请尝试以下操作:

HGLOBAL CopySelection(HWND hwndList)
{
    vector<string> files;
    UINT len = 0;

    // get the selection inside the list control

    int iPos = -1;
    do
    {
        int iPos = SendMessage(hwndList, LVM_GETNEXTITEM, iPos, LVNI_SELECTED);
        if (iPos == -1)
            break;

        LVITEM item = {0};
        char buffer[256];
        string fileDir;

        item.cchTextMax = 256;
        item.pszText = buffer;
        item.mask = LVIF_TEXT;

        item.iItem = iPos;
        item.iSubItem = 1;
        ListView_GetItem(hwndList, &item);
        fileDir = item.pszText;
        fileDir += "\\";

        item.iItem = iPos;
        item.iSubItem = 0;
        ListView_GetItem(hwndList, &item);
        fileDir += item.pszText;

        item.iItem = iPos;
        item.iSubItem = 2;
        ListView_GetItem(hwndList, &item);
        fileDir += item.pszText;

        files.push_back(fileDir);

        // +1 = the filename's null terminator
        len += (fileDir.length() + 1);

        cout << "iPos: " << iPos << ", fileDir: " << fileDir << endl;
    }
    while (true);

    if (files.empty())
        return NULL;

    // +1 = the file list's null terminator
    HGLOBAL hMem = GlobalAlloc(GHND, sizeof(DROPFILES) + len + 1);
    if (!hMem)
        return NULL;

    DROPFILES *dfiles = (DROPFILES*) GlobalLock(hMem);
    if (!dfiles)
    {
        GlobalFree(hMem);
        return NULL;
    }

    dfiles->pFiles = sizeof(DROPFILES);
    GetCursorPos(&(dfiles->pt));
    dfiles->fNC = TRUE;
    dfiles->fWide = FALSE;

    char *pFile = (char*) &dfiles[1];
    for (vector<string>::size_type i = 0; i < files.size(); ++i)
    {
        string &fileDir = files[i];

        // +1 = the filename's null terminator
        len = (fileDir.length() + 1);

        memcpy(pFile, fileDir.c_str(), len);
        pFile += len;
    }

    GlobalUnlock(hMem);
    return hMem;
}

【讨论】:

  • 非常感谢!不过我有点不确定:dfiles->pFiles = sizeof(DROPFILES);那就是说'在我们的文件列表的这个结构的大小之后在内存中查找',是吗?但是这个:memcpy(&dfiles[1], fileDir.c_str(), fileDir.length());将 char* 复制到结构之后。我想这就是为什么我对 C 内存分配的了解持平 - &dfiles[1] 是指数组吗?如果是这样,它是一个dropfile数组吗?但是分配的内存中只有 DROPFILE 和字符串的长度(加上 2 个字符)。
  • 是的,这就是dfiles-&gt;pFiles 所指的。 &amp;dfiles[1] 语法利用 dfiles 是一个指针这一事实,通过调用指针算法来获取从分配的 DROPFILES 结构的开头精确到 1 * sizeof(DROPFILES) 字节数的内存地址。如果你愿意,它也可以写成(char*)(dfiles + 1),或者写成(char*)(((LPBYTE)dfiles) + dfiles-&gt;pFiles)
  • 为解释干杯。我对一个实际的指针数组很好,但这是我头脑中的内存分配。:)
  • 嗯,不过还是个问题。我无法从我的列表视图拖放到另一个应用程序上。在正常的 Windows 功能中,它使您将鼠标悬停在(屏幕上或工具栏上)的应用程序成为焦点,以便您可以转储文件名。这是我需要自己添加的额外功能吗?或者我是否需要在我想要拖放到的应用程序的某处设置窗口句柄?
  • 两者都不是。 Windows 为您处理一切。如果你不能拖放,那么要么你没有正确实现IDropSource和/或IDataObject,要么目标应用根本不接受CF_HDROP或你的DROPEFFECT
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多