user1853098
user1853098

Reputation: 119

Drag Drop Win API 32

I'm trying to drag and drop a ListView item from my program to another (like dragging a path to something lie VLC, and it plays a video file). I'm using the CF_HDROP clipboard format. CopySelection is what sets the STGMEDIUM hglobal variable to a DROPFILES struct.

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;
}

But this seems to cause a segfault. Here's the MouseMove list message code that calls it:

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;
    }

The code is properly formatted (I've checked) but unsure why this isn't working. Am I even using the right OLE clipboard format to make this happen? I'm unsure which to use and the documentation I've found isn't great.

Cheers, Rob

P.S. I have tried to adapt this example: http://www.catch22.net/tuts/drop-source

The difference is, he merely moves text, whereas I'm trying to move a list of files (like selecting icons in windows and dragging onto a vid player).

Upvotes: 4

Views: 5161

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 595305

You are not allocating enough memory for the HGLOBAL block. You are allocating only enough memory to hold the DROPFILES itself, but no memory to hold the filename that goes with it. But even if you were allocating memory correctly, you are not using the DROPFILES::pFiles field correctly. It needs to specify the offset from the start of the DROPFILES struct where the filename list begins, but you are treating it as a memory address instead.

Try this instead:

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;
        }
    }

If you want to drag multiple selected files at a time, try this instead:

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;
}

Upvotes: 8

Related Questions