Reputation: 119
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
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