Страница 1 из 2

Как добавить файлы из папки в listbox методом Drag'n'drop?

Добавлено: 04 янв 2009, 17:08
[:: SynTronic ::]
Доброго времени суток.
Подскажите, пожалуйста, как в коде обработать перетаскивание папки из проводника в ListBox? В общем чтобы получить список файлов в этой папке...
Файлы у меня он уже умеет принимать. А вот чтобы папки - ума не приложу как сделать.

Re: Как добавить файлы из папки в listbox методом Drag'n'drop?

Добавлено: 06 янв 2009, 11:59
Romeo
Если мне не изменяет память, то из драгинфо ты получаешь PIDL айтема файловой системы. Из PIDL можно получить путь с помощью GetPathFromIDList (не важно путь это к файлу или к папке). Потом с помощью FindFirstFile и FindNextFile найти все файлы в папке.

Re: Как добавить файлы из папки в listbox методом Drag'n'drop?

Добавлено: 16 янв 2009, 15:37
[:: SynTronic ::]
Я так понимаю это с COM связано? :) НЕ умею я пока так делать. Спасибо за ответ!

Re: Как добавить файлы из папки в listbox методом Drag'n'drop?

Добавлено: 28 янв 2009, 22:59
[:: SynTronic ::]
Так, освоил интерфейсы COM. Всё равно вопрос :)
В МСДН конечно описан Drag'n'Drop чего угодно, но только не того как получить имя папки, которая бросается на окно :)
В общем надо реализовывать интерфейс IDropTarget и уже в нём извлекать что-то там? Если извлекать, то что и откуда? Единственное что там передаётся, это IDataObject. Его самому надо определять? Или Windows указатель на созданный экземпляр возвращает?

И вытекающий вопрос. Правильно ли я понимаю как это делается?
1) создаю пораждённый от IDropTarget класс, реализую его методы.
2) вызываю RegisterDragDrop и в качестве IDropTarget * pDropTarget передаю свой класс?

Re: Как добавить файлы из папки в listbox методом Drag'n'drop?

Добавлено: 30 янв 2009, 02:06
Romeo
По поводу вопросов. Да, два написанных пункта верны. Теперь обсудим, что нужно делать дальше.

В AllowDrop тебе передают указатель на FORMATETC. Эта структура определяет формат перетаскиваемых данных. Для того, чтобы отсеять все ненужные форматы объектов, которые глупый пользователь может попытаться перетащить в твоё окно, используй сравнение с полем FORMATETC::cfFormat. Системный формат для списка файлов равен CF_HDROP, так что на 95% именно с ним у нужно сравнивать, но ты на всякий случай проверь. Во всех остальных случаях возвращай из AllowDrop false.

Слудующий шаг - это извлечение данных из IDataObject (его создавать не нужно, он является входным параметром в AllowDrop). Самый главный метод интерфейса - это GetData. Методу передаётся поинтер на FORMATETC (который уже есть) и указатель на структуру STGMEDIUM, которая будет output параметром (то есть метод заполнит её данными, как следствие туда нельзя передавать NULL, иначе всё умрёт). После того, как поработаешь со структой STGMEDIUM, не забудь освободить структуру с помощью функции ReleaseStgMedium.

Последняя структура является обобщённым хранилищем информации. Первое, что нужно сделать, это проверить тип данных, которые содержаться в поле tymed. Есть большая вероятность, что он будет равняться TYMED_FILE. Если это так, то следует использовать поле lpszFileName, в котором будет храниться путь к фолдеру. Есть ещё важное замечание. Если вдруг формат окажется TYMED_HGLOBAL, то напрямую поле hGlobal использовать нельзя. Нужно сначала залочить глобальную память (GlobalLock), затем поработать с ней, потом разлочить (GlobalUnlock).

Всё, я выдохся. Хотел ещё рассказать как можно зарегистрировать свой собственный формат перетаскиваемых объектов, но уже спать охота. Если это тоже понадобится, то напишу в следующем посте.

Вот ссылки на техническую документацию об обсуждённых сущностях:

FORMATETC
FORMATETC::cfFormat
STGMEDIUM
STGMEDIUM::TYMED

Re: Как добавить файлы из папки в listbox методом Drag'n'drop?

Добавлено: 30 янв 2009, 10:52
[:: SynTronic ::]
Romeo, спасибо огромное! ты - мозг :)

Re: Как добавить файлы из папки в listbox методом Drag'n'drop?

Добавлено: 30 янв 2009, 20:44
[:: SynTronic ::]
Яхуууу!!!!!

Код: Выделить всё

HRESULT __stdcall CDropTarget: :D rop(IDataObject *pDataObject, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
	FORMATETC fm = {0};
	fm.cfFormat = CF_HDROP;
	fm.tymed = TYMED_HGLOBAL;
	fm.dwAspect = DVASPECT_CONTENT;
	STGMEDIUM stg = {0};
	HDROP drop = {0};
	WCHAR szBuffer[MAX_PATH] = {0};

	pDataObject->GetData(&fm, &stg);
	drop = (HDROP)stg.hGlobal;
	int count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
	for (int i=0; i < count; i++)
	{
		DragQueryFile(drop, i, szBuffer, MAX_PATH);
		MessageBox(0, szBuffer, 0, 0);
	}
            ReleaseStgMedium(&stg);
	return S_OK;
}

Re: Как добавить файлы из папки в listbox методом Drag'n'drop?

Добавлено: 31 янв 2009, 10:55
Romeo
Помнишь что я про hGlobal писал? Нужно ему Lock делать, иначе система в любой момент может переместить даные, к примеру в своп, и ты будешь обращаться по невалидному адресу.

Код: Выделить всё

pDataObject->GetData(&fm, &stg);
drop = (HDROP)GlobalLock(stg.hGlobal);
...
GlobalUnlock(std.hGlobal);
ReleaseStgMedium(&stg);
Ещё зачемание. Самому заполнять FORMATETC непринято. Более красиво было бы сделать его полем класса и заполнять в AllowDrop. Это защитит тебя от ситуации, когда ты будешь пытать вытащить из IDataObject не те данные, которые там есть.

Re: Как добавить файлы из папки в listbox методом Drag'n'drop?

Добавлено: 31 янв 2009, 17:43
[:: SynTronic ::]
Я хоть убей не понимаю, что за AllowDrop :)
Это первый вопрос.
ПО поводу заполнения FORMATETC. Путём жестко-хардкорной работы мозга и логики (по-моему работать с drag'n'drop'ом по другому не получается :) понял что надо делать так: IDataObject (полученный в IDropTarget) имеет метод EnumFormatEtc. С его помощью получаем указатель на интерфейс IEnumFORMATETC. Уже с его помощью методом NEXT перечисляем FORMATETC (кстати, откуда их там штук 5, когда я только 1 файл перетаскиваю?). Сравниваю поле FORMATETC.cfFormat с CF_HDROP, если они равны то прерываю цикл и уже только теперь можно вызвать чудный метод IDataObject::GetData, с помощью которого получаем STGMEDIUM. А далее уже делаю делаю сравнение STGMEDIUM::tymed с TYMED_HGLOBAL (потому что он у меня всегда такой) и присваиваю переменной HDROP STGMEDIUM::hGlobal (ну как я уже понял, предворительно залочив память).
Всё именно так? :)
Просто оооочень не хочется заного переписывать код, Чтобы проверить работоспособность вышенаписанного... Хотя до своего поста выше я такой уже написал, только не знал тогда что делать c hGlobal.

Re: Как добавить файлы из папки в listbox методом Drag'n'drop?

Добавлено: 01 фев 2009, 14:38
Romeo
Я просто при программировании перетаскивания использовал IDropTargetImpl<> из ATL. В нём уже переопределены методы чистого интерфейса и сделаны свои чисто виртуальные функции, которые следует перегружать. Если ты имплементируешь чистый интерфейс, то никакого AllowDrop там не будет :)

С помощью EnumFormatEtc можно получить все форматы в которых IDataObject может вернуть данные, которые находятся в нём. То, что в нём 5 форматов, это значит, что он может в 5-ти разных форматах может представить данные внутри себя. На программиста возлагается принятие решение о том, с какими данными ему будет потом удобно работать. Поэтому, если тебе наиболее удобно работать именно с CF_HDROP, то ищи его в списке поддерживаемых форматов, как ты и делаешь, если не удобно, то можешь попробовать какой-то другой формат.