Сообщений 10    Оценка 111        Оценить  
Система Orphus

Как вывести для файла контекстное меню, как в эксплорере?

Автор: Александр Шаргин
Опубликовано: 6.04.2001
Исправлено: 15.03.2005
Версия текста: 1.0

Для отображения контекстного меню, связанного с объектами пространства имён оболочки Windows, эксплорер использует интерфейс IContextMenu соответствующего объекта. Мы тоже можем воспользоваться этим интерфейсом в нашей программе. Последовательность шагов при этом будет следующей:

Основную сложность на самом деле представляет первый этап. Получить указатель на IContextMenu мы можем только, имея указатель на интерфейс IShellFolder для объекта оболочки Windows, содержащего требуемый файл или каталог, но Windows не предоставляет простого способа получить этот указатель. Соответствующая процедура подробно описана в статье "Как получить IShellFolder для заданного файла или каталога?", поэтому здесь я просто воспользуюсь алгоритмом, описанным в этой статье.

Функция, отображающая контекстное меню, может выглядеть примерно так (её можно использовать как в программе на "чистом" API, так и на MFC).

#include <shlobj.h>
#include <atlbase.h>

...

void ShowContextMenu(HWND hWnd, LPCTSTR pszPath, int x, int y)
{
   USES_CONVERSION;

   // Строим полное имя файла/каталога
   TCHAR tchFullPath[MAX_PATH];
   GetFullPathName(pszPath, sizeof(tchFullPath)/sizeof(TCHAR), tchFullPath, NULL);

   // Получаем интерфейс IShellFolder рабочего стола
   IShellFolder *pDesktopFolder;
   SHGetDesktopFolder(&pDesktopFolder);

   // Преобразуем заданный путь в LPITEMIDLIST
   LPITEMIDLIST pidl;
   pDesktopFolder->ParseDisplayName(hWnd, NULL, T2OLE(tchFullPath), NULL, &pidl, NULL);

   // Ищем последний идентификатор в полученном списке pidl
   LPITEMIDLIST pLastId = pidl;
   USHORT temp;
   while(1)
   {
       int offset = pLastId->mkid.cb;
       temp = *(USHORT*)((BYTE*)pLastId + offset);

       if(temp == 0)
           break;

       pLastId = (LPITEMIDLIST)((BYTE*)pLastId + offset);
   }
   
   // Получаем интерфейс IShellFolder родительского объекта для заданного файла/каталога
   // Примечание: родительский каталог идентифицируется списком pidl за вычетом последнего
   //             элемента, поэтому мы временно зануляем pLastId->mkid.cb, отрезая его от списка
   temp = pLastId->mkid.cb;
   pLastId->mkid.cb = 0;
   IShellFolder *pFolder;
   pDesktopFolder->BindToObject(pidl, NULL, IID_IShellFolder, (void**)&pFolder);

   // Получаем интерфейс IContextMenu для заданного файла/каталога
   // Примечание: относительно родительского объекта заданный файл/каталог идентифицируется
   //             единственным элементом pLastId
   pLastId->mkid.cb = temp;
   IContextMenu *pContextMenu;
   pFolder->GetUIObjectOf(
      hWnd, 1, (LPCITEMIDLIST *)&pLastId, IID_IContextMenu, NULL, (void**)&pContextMenu);

   // Создаём меню
   HMENU hPopupMenu = CreatePopupMenu();

   // Заполняем меню
   pContextMenu->QueryContextMenu(hPopupMenu, 0, 1, 0x7FFF, 0);

   // Отображаем меню
   UINT nCmd = TrackPopupMenu(hPopupMenu,
      TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_RETURNCMD, x, y, 0, hWnd, 0);

   // Выполняем команду (если она была выбрана)
   if(nCmd)
   {
      CMINVOKECOMMANDINFO ici;
      ZeroMemory(&ici, sizeof(CMINVOKECOMMANDINFO));
      ici.cbSize = sizeof(CMINVOKECOMMANDINFO);

      ici.hwnd = hWnd;
      ici.lpVerb = MAKEINTRESOURCE(nCmd-1);
      ici.nShow = SW_SHOWNORMAL;

      pContextMenu->InvokeCommand(&ici);
   }

   // Получаем интерфейс IMalloc
   IMalloc *pMalloc;
   SHGetMalloc(&pMalloc);

   // Освобождаем память, выделенную для pidl
   pMalloc->Free(pidl);

   // Освобождаем все полученные интерфейсы
   pDesktopFolder->Release();
   pFolder->Release();
   pContextMenu->Release();
   pMalloc->Release();

   return;
}

Для преобразования строк из TCHAR в OLECHAR я воспользовался макросами из библиотеки ATL. Обратите внимание, что функция TrackPopupMenu вызывается с флагом TPM_RETURNCMD. Этот флаг сообщает функции, что она не должна посылать программе сообщение WM_COMMAND, а вместо этого должна вернуть идентификатор выбранной команды в вызывающую программу.

Функцию ShowContextMenu можно вызывать, например, из обработчика WM_CONTEXTMENU. Делается это так:

#include <windowsx.h>

...

case WM_CONTEXTMENU:
   ShowContextMenu(hWnd, "C:\\command.com", GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));

В MFC обработчик выглядит аналогично:

void CMyView::OnContextMenu(CWnd* pWnd, CPoint point)
{
   ShowContextMenu(pWnd->m_hWnd, "C:\\command.com", point.x, point.y);
}

Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 10    Оценка 111        Оценить