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

Добавлено: 09 мар 2005, 12:17
Romeo
Вот необходимая функция:

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

struct CGlobalLock
{
	CGlobalLock(HGLOBAL hMem)
		: m_hMem(hMem)
	{
		m_pMem = ::GlobalLock(m_hMem);
	}

	~CGlobalLock()
	{
		if (m_hMem != NULL)
		{
			::GlobalUnlock(m_hMem);
		}
	}

	HGLOBAL m_hMem;
	LPVOID m_pMem;
};

struct CStgMedium : public STGMEDIUM
{
	CStgMedium()
	{
		tymed = 0;
		hGlobal = NULL;
		pUnkForRelease = NULL;
	}

	~CStgMedium()
	{
		::ReleaseStgMedium(this);
	}
};

PIDLWrapper GetSelectedItem(IShellView* pShellView, UINT uItem)
{
	HRESULT hr = S_OK;

	IDataObjectPtr spDataItem;		
	hr = pShellView->GetItemObject(uItem, IID_IDataObject, (void**)&spDataItem);
	if (FAILED(hr) || spDataItem == NULL)
	{
		return NULL;
	}

	FORMATETC fetc;
	fetc.cfFormat = CF_IDLIST;
	fetc.dwAspect = DVASPECT_CONTENT;
	fetc.ptd = NULL;
	fetc.lindex = -1;
	fetc.tymed = TYMED_HGLOBAL;

	hr = spDataItem->QueryGetData(&fetc);
	ATLASSERT(SUCCEEDED(hr));

	CStgMedium stgmed;

	hr = spDataItem->GetData(&fetc, &stgmed);
	ATLASSERT(SUCCEEDED(hr));

	CGlobalLock gl(stgmed.hGlobal);

	CIDA* pData = (CIDA*)gl.m_pMem;

	ATLASSERT(pData != NULL);
	if (pData == NULL || pData->cidl < 1)
	{
		return NULL;
	}

	UINT offset = pData->aoffset[1];
	char* pcData = reinterpret_cast<char*>(pData) + offset;
	LPITEMIDLIST pidlSource = (LPITEMIDLIST)pcData;

	return PIDLWrapper(pidlSource); // copy pidlSource
}
Вот вариант её вызова:

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

PIDLWrapper pidl = GetSelectedItem(m_spShellView, SVGIO_SELECTION);
Так же в начале исполнения программы следует зарегистрировать соответствующий клипборд формат следующим образом:

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

CF_IDLIST = ::RegisterClipboardFormat(CFSTR_SHELLIDLIST);
Ещё одно замечание. В имплементации использован класс PIDLWrapper. Он, фактически, эквивалентен обёртке _bstr_t, сделанной для BSTR-строк, лишь с той разницей, что вместо BSTR контейнерезирует шеловский PIDL. PIDLWrapper придётся написать самому, либо, если лень, достаточно просто написать функцию для копирования PIDL-ов - её можно скопировать прямо из статьи MSDN о PIDL'ах. После написания такой функции достаточно скопировать PIDL в самом конце функции GetSelectedItem и вернуть его копию. Также следует не забыть удалить этот PIDL после того, как он станет не нужнен с помощью IMalloc::Free.

Удачи.

Добавлено: 28 мар 2005, 18:37
Sera
Спасибо, Romeo. Отличный ответ.
Раз уже пошла тут такая тема, то задам еще один вопрос. Может, кто-то уже над этим думал.

В этой теме говорится про создание своего експлорера => реализацию IShellBrowser.
А мой вопрос -- противоположный. У любого стандартного окна explorer-а можно получить указатель на IShellBrowser (посылая сообщение WM_GETISHELLBROWSER = WM_USER+7), у этого интерфейса можно запросить активный ShellView (QueryActiveShellView).
Это то, что мне известно. А нужно мне получить текущую папку -- для которой создан данный ViewObject -- shell view.

Исходя из предыдущих сообщений, получается, что если у IShellView попросить IDataObject по SelectedItems -- то там будет и folder parent pidl. А вот если текущий shellview не имеет текущего выделения? Как тогда получить его ShellFolder-отца?

Заранее спасибо за любые ответы

Добавлено: 28 мар 2005, 19:18
Romeo
Предполагаю нужно попросить у объекта ShellView интерфейс IPersistFolder2. Это теоретически. Практически не пробовал, честно говорю. Если не получится, Sera, сразу дай знать. Вообще для таких целей MSDN было бы неплохо почитать. Но, смотрю, как тебе, так и мне лень :) .

Добавлено: 29 мар 2005, 19:13
Sera
Вообще для таких целей MSDN было бы неплохо почитать. Но, смотрю, как тебе, так и мне лень
Ну, к MSDN-у я уже обращался не раз, да и с темой немного знаком (писал свой NSE).
У ShellView-а запросить PersistFolder2 не получится, потому как они реализованы в разных ком-классах (это ShellFolder реализует PersistFolder и часто PersistFolder2, но не ShellView).
А для верности я проверил -- у полученного с помощью QueryActiveShellView интерфейса IShellView запросил IPersistFolder -- failed.
Вобщем, вопрос остается открытым.

Добавлено: 29 мар 2005, 20:15
Romeo
А IDataObject не пробовал спрашивать? По крайней мере было бы логично, если бы стандартный ShellView объект имплементировал этот интерфейс и поддерживал передачу данных в виде Shell PIDL.

Добавлено: 29 мар 2005, 20:34
Sera
Попробовал, но это тоже не то.
Вообще NSE IShellView реализует еще IOleCommandTarget, IOleWindow. Думаю, шеловый стандартный СShellView примерно так же, поэтому запрашивать через QueryInterface бессмысленно.
Остаются сами методы IShellView. Единственный, который может возвращать интерфейс, -- это GetItemObject. И здесь опять тупик.

Добавлено: 30 мар 2005, 11:21
Romeo
Sera, извини за два предыдущих необдуманных ответа. Просто я был несколько занят и отвечал невпопад. Сегодня почитал MSDN. Для получения PIDL'а, который в данный момент отображается нашим ShellView, нужно вызвать IShellView::GetItemObject с первым параметром SVGIO_BACKGROUND, в результате мы получим IDataObject, из которого извлечём PIDL. Думаю, стоит просто переюзать готовую функцию GetSelectedItem, приведённую ранее, с тем же загадочным параметром SVGIO_BACKGROUND :)

Добавлено: 30 мар 2005, 15:14
Sera
Вот текущий результат моей разведки:
IShellView::GetItemObject с SVGIO_BACKGROUND совсем не поддерживает IDataObject (в MSDN-е говорится, что это для запроса IContextMenu).
Зато попробовал использовать параметр SVGIO_ALLVIEW -- он дает тот же результат, что и SVGIO_SELECTION, только возвращает пидлы всех своих item-ов. Здесь, конечно, трата времени и памяти, зато можно получить родительский pidl даже когда в listview-е нет выделения.
Но это не решает мои проблемы :( , потому что если папка пустая, ShellView есть, но при вызове GetItemObject(SVGIO_ALLVIEW, IID_IDataObject, ..) возвращает fail-result.
Попробовал зайти с левого фланга -- просто получить текстовое содержимое адресс-бара, но это однозначно плохо -- и текст типа "Мой компьютер" не парсится, да и бывает, что там мусор остается, введенный пользователем.
Еще попробовал использовать IWebBrowser2::get_LocationURL, но тот возвращ. пустую строку для специальных папок. И вообще, строка -- это нехорошо. Мне бы получить pidl.
Вот такая петрушка, в эксплорере может одновременно отображаться только одна папка, а получить ее совсем непросто.

Может, есть еще идеи?
Спасибо