// ON ENTRY:
// hwnd = Handle of the window that will own the context menu.
// psfFolder = IShellFolder pointer for the itemís container.
// ppidl = Pointer to PIDL identifying the item.
// x, y = Screen coordinates where menu will be displayed.
//
// Get an IContextMenu pointer for the item.
//
LPCONTEXTMENU pContextMenu;
psfFolder->lpVtbl->GetUIObjectOf (psfFolder, hwnd, 1, ppidl,
&IID_IContextMenu, NULL, &pContextMenu);
//
// Create an empty context menu.
//
HMENU hMenu = CreatePopupMenu ();
//
// Let Explorer fill the menu with menu items.
//
pContextMenu->lpVtbl->QueryContextMenu (pContextMenu, hMenu, 0, 1,
0x7FFF, CMF_EXPLORE);
//
// Display the menu.
//
UINT nCmd = TrackPopupMenu (hMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON |
TPM_RIGHTBUTTON | TPM_RETURNCMD, x, y, 0, hwnd, NULL);
//
// If a command was selected from the menu, execute it.
//
if (nCmd) {
CMINVOKECOMMANDINFO ici;
ici.cbSize = sizeof (CMINVOKECOMMANDINFO);
ici.fMask = 0;
ici.hwnd = hwnd;
ici.lpVerb = MAKEINTRESOURCE (nCmd - 1);
ici.lpParameters = NULL;
ici.lpDirectory = NULL;
ici.nShow = SW_SHOWNORMAL;
ici.dwHotKey = 0;
ici.hIcon = NULL;
pContextMenu->lpVtbl->InvokeCommand (pContextMenu, &ici);
}
//
// Clean up.
//
DestroyMenu (hMenu);
pContextMenu->lpVtbl->Release (pContextMenu);
Figure 2 ExpMenu
ExpMenu.h
//*************************************************************************** // // ExpMenu.h // //*************************************************************************** #define ID_LISTVIEW 100 #define ID_BITMAP 101ExpMenu.rc
//*************************************************************************** // // ExpMenu.rc // //*************************************************************************** #include "ExpMenu.h" ID_BITMAP BITMAP "Images.bmp"ExpMenu.c
//***************************************************************************
//
// ExpMenu.c
//
//***************************************************************************
#include <windows.h>
#include <commctrl.h>
#include <shlobj.h>
#include "ExpMenu.h"
/////////////////////////////////////////////////////////////////////////////
// Function prototypes
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
void PopulateListView (HWND);
void AddItemToListView (HWND, WIN32_FIND_DATA*);
BOOL DoExplorerMenu (HWND, LPCTSTR, POINT);
UINT GetItemCount (LPITEMIDLIST);
LPITEMIDLIST GetNextItem (LPITEMIDLIST);
LPITEMIDLIST DuplicateItem (LPMALLOC, LPITEMIDLIST);
/////////////////////////////////////////////////////////////////////////////
// WinMain
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
static char szWndClassName[] = "ExpMenuWndClass";
WNDCLASS wc;
HWND hwnd;
MSG msg;
wc.style = 0;
wc.lpfnWndProc = (WNDPROC) WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon (NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = szWndClassName;
RegisterClass (&wc);
hwnd = CreateWindow (szWndClassName, "Explorer Context Menu Demo",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL);
ShowWindow (hwnd, nCmdShow);
UpdateWindow (hwnd);
while (GetMessage (&msg, NULL, 0, 0)) {
TranslateMessage (&msg);
DispatchMessage (&msg);
}
return msg.wParam;
}
/////////////////////////////////////////////////////////////////////////////
// Window procedure
LRESULT CALLBACK WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HBITMAP hBitmap;
HINSTANCE hInstance;
LV_HITTESTINFO hti;
LPNMHDR pnmhdr;
char szLabel[MAX_PATH];
char szPath[MAX_PATH];
DWORD dwPos;
static HWND hwndListView;
static HIMAGELIST hImageList;
switch (msg) {
case WM_CREATE:
//
// Create a list view control in the main window.
//
InitCommonControls ();
hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
hwndListView = CreateWindowEx (WS_EX_CLIENTEDGE, WC_LISTVIEW, "",
WS_CHILD | WS_VISIBLE | LVS_ICON | LVS_SINGLESEL | LVS_AUTOARRANGE,
0, 0, 0, 0, hwnd, (HMENU) ID_LISTVIEW, hInstance, NULL);
hBitmap = LoadBitmap (hInstance, MAKEINTRESOURCE (ID_BITMAP));
hImageList = ImageList_Create (32, 32, ILC_MASK, 2, 1);
ImageList_AddMasked (hImageList, hBitmap, RGB (255, 0, 255));
DeleteObject (hBitmap);
ListView_SetImageList (hwndListView, hImageList, LVSIL_NORMAL);
PopulateListView (hwndListView);
return 0;
case WM_SIZE:
//
// Resize the list view to fit the client area of its parent.
//
if ((wParam == SIZE_RESTORED) || (wParam == SIZE_MAXIMIZED))
SetWindowPos (hwndListView, NULL, 0, 0, (int) LOWORD (lParam),
(int) HIWORD (lParam), SWP_NOMOVE | SWP_NOZORDER);
return 0;
case WM_NOTIFY:
//
// Display a context menu when something is right-clicked.
//
pnmhdr = (LPNMHDR) lParam;
if (pnmhdr->code == NM_RCLICK) {
dwPos = GetMessagePos ();
hti.pt.x = (int) LOWORD (dwPos);
hti.pt.y = (int) HIWORD (dwPos);
ScreenToClient (hwnd, &hti.pt);
ListView_HitTest (hwndListView, &hti);
if (hti.flags & LVHT_ONITEM) {
ListView_GetItemText (hwndListView, hti.iItem, 0, szLabel,
sizeof (szLabel));
GetWindowsDirectory (szPath, sizeof (szPath));
if (szPath[lstrlen (szPath) - 1] != '\\')
lstrcat (szPath, "\\");
lstrcat (szPath, szLabel);
DoExplorerMenu (hwnd, szPath, hti.pt);
//
// NOTE: Here's a great place to refresh the list view control
// in case a file or folder was created, renamed, or deleted
// as a result of a context menu command. We don't do it here
// because that's not the point of this example.
//
}
return 0;
}
break;
case WM_DESTROY:
//
// Clean up and terminate.
//
DestroyWindow (hwndListView);
DeleteObject (hImageList);
PostQuitMessage (0);
return 0;
}
return DefWindowProc (hwnd, msg, wParam, lParam);
}
/////////////////////////////////////////////////////////////////////////////
// Window procedure helper functions
void PopulateListView (HWND hwnd)
{
WIN32_FIND_DATA fd;
char szPath[MAX_PATH];
HANDLE hFind;
//
// Get the path to the Windows directory and append "*.*".
//
GetWindowsDirectory (szPath, sizeof (szPath));
if (szPath[lstrlen (szPath) - 1] != '\\')
lstrcat (szPath, "\\");
lstrcat (szPath, "*.*");
//
// Create one list view item for each file or folder.
//
if ((hFind = FindFirstFile (szPath, &fd)) != INVALID_HANDLE_VALUE) {
AddItemToListView (hwnd, &fd);
while (FindNextFile (hFind, &fd))
AddItemToListView (hwnd, &fd);
FindClose (hFind);
}
}
void AddItemToListView (HWND hwnd, WIN32_FIND_DATA* pfd)
{
LV_ITEM lvi;
//
// Initialize the LV_ITEM structure's iImage field to display a file or
// folder icon, and skip "." and ".." folders.
//
if (pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (pfd->cFileName[0] == '.')
return;
lvi.iImage = 1; // Folder
}
else
lvi.iImage = 0; // File
//
// Initialize the remaining members of the LV_ITEM structure.
//
lvi.mask = LVIF_TEXT | LVIF_IMAGE;
lvi.iItem = ListView_GetItemCount (hwnd);
lvi.iSubItem = 0;
lvi.pszText = pfd->cFileName;
//
// Add the item to the list view.
//
ListView_InsertItem (hwnd, &lvi);
}
/////////////////////////////////////////////////////////////////////////////
// Context menu functions
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// FUNCTION: DoExplorerMenu
//
// DESCRIPTION: Given a path name to a file or folder object, displays
// the shell's context menu for that object and executes
// the menu command (if any) selected by the user.
//
// INPUT: hwnd = Handle of the window in which the menu will be
// displayed.
//
// pszPath = Pointer to an ANSI or Unicode string
// specifying the path to the object.
//
// point = x and y coordinates of the point where the
// menu's upper left corner should be located, in
// client coordinates relative to hwnd.
//
// RETURNS: TRUE if successful, FALSE if not.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BOOL DoExplorerMenu (HWND hwnd, LPCTSTR pszPath, POINT point)
{
LPMALLOC pMalloc;
LPSHELLFOLDER psfFolder, psfNextFolder;
LPITEMIDLIST pidlMain, pidlItem, pidlNextItem, *ppidl;
LPCONTEXTMENU pContextMenu;
CMINVOKECOMMANDINFO ici;
ULONG ulCount, ulAttr;
TCHAR tchPath[MAX_PATH];
WCHAR wchPath[MAX_PATH];
UINT nCount, nCmd;
BOOL bResult;
HMENU hMenu;
//
// Make sure the file name is fully qualified and in Unicode format.
//
GetFullPathName (pszPath, sizeof (tchPath) / sizeof (TCHAR), tchPath, NULL);
if (IsTextUnicode (tchPath, lstrlen (tchPath), NULL))
lstrcpy ((char *) wchPath, tchPath);
else
MultiByteToWideChar (CP_ACP, 0, pszPath, -1, wchPath,
sizeof (wchPath) / sizeof (WCHAR));
//
// Get pointers to the shell's IMalloc interface and the desktop's
// IShellFolder interface.
//
bResult = FALSE;
if (!SUCCEEDED (SHGetMalloc (&pMalloc)))
return bResult;
if (!SUCCEEDED (SHGetDesktopFolder (&psfFolder))) {
pMalloc->lpVtbl->Release (pMalloc);
return bResult;
}
//
// Convert the path name into a pointer to an item ID list (pidl).
//
if (SUCCEEDED (psfFolder->lpVtbl->ParseDisplayName (psfFolder, hwnd,
NULL, wchPath, &ulCount, &pidlMain, &ulAttr)) && (pidlMain != NULL)) {
if (nCount = GetItemCount (pidlMain)) { // nCount must be > 0
//
// Initialize psfFolder with a pointer to the IShellFolder
// interface of the folder that contains the item whose context
// menu we're after, and initialize pidlItem with a pointer to
// the item's item ID. If nCount > 1, this requires us to walk
// the list of item IDs stored in pidlMain and bind to each
// subfolder referenced in the list.
//
pidlItem = pidlMain;
while (--nCount) {
//
// Create a 1-item item ID list for the next item in pidlMain.
//
pidlNextItem = DuplicateItem (pMalloc, pidlItem);
if (pidlNextItem == NULL) {
pMalloc->lpVtbl->Free (pMalloc, pidlMain);
psfFolder->lpVtbl->Release (psfFolder);
pMalloc->lpVtbl->Release (pMalloc);
return bResult;
}
//
// Bind to the folder specified in the new item ID list.
//
if (!SUCCEEDED (psfFolder->lpVtbl->BindToObject (psfFolder,
pidlNextItem, NULL, &IID_IShellFolder, &psfNextFolder))) {
pMalloc->lpVtbl->Free (pMalloc, pidlNextItem);
pMalloc->lpVtbl->Free (pMalloc, pidlMain);
psfFolder->lpVtbl->Release (psfFolder);
pMalloc->lpVtbl->Release (pMalloc);
return bResult;
}
//
// Release the IShellFolder pointer to the parent folder
// and set psfFolder equal to the IShellFolder pointer for
// the current folder.
//
psfFolder->lpVtbl->Release (psfFolder);
psfFolder = psfNextFolder;
//
// Release the storage for the 1-item item ID list we created
// just a moment ago and initialize pidlItem so that it points
// to the next item in pidlMain.
//
pMalloc->lpVtbl->Free (pMalloc, pidlNextItem);
pidlItem = GetNextItem (pidlItem);
}
//
// Get a pointer to the item's IContextMenu interface and call
// IContextMenu::QueryContextMenu to initialize a context menu.
//
ppidl = &pidlItem;
if (SUCCEEDED (psfFolder->lpVtbl->GetUIObjectOf (psfFolder,
hwnd, 1, ppidl, &IID_IContextMenu, NULL, &pContextMenu))) {
hMenu = CreatePopupMenu ();
if (SUCCEEDED (pContextMenu->lpVtbl->QueryContextMenu (
pContextMenu, hMenu, 0, 1, 0x7FFF, CMF_EXPLORE))) {
ClientToScreen (hwnd, &point);
//
// Display the context menu.
//
nCmd = TrackPopupMenu (hMenu, TPM_LEFTALIGN |
TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD,
point.x, point.y, 0, hwnd, NULL);
//
// If a command was selected from the menu, execute it.
//
if (nCmd) {
ici.cbSize = sizeof (CMINVOKECOMMANDINFO);
ici.fMask = 0;
ici.hwnd = hwnd;
ici.lpVerb = MAKEINTRESOURCE (nCmd - 1);
ici.lpParameters = NULL;
ici.lpDirectory = NULL;
ici.nShow = SW_SHOWNORMAL;
ici.dwHotKey = 0;
ici.hIcon = NULL;
if (SUCCEEDED (
pContextMenu->lpVtbl->InvokeCommand (
pContextMenu, &ici)))
bResult = TRUE;
}
}
DestroyMenu (hMenu);
pContextMenu->lpVtbl->Release (pContextMenu);
}
}
pMalloc->lpVtbl->Free (pMalloc, pidlMain);
}
//
// Clean up and return.
//
psfFolder->lpVtbl->Release (psfFolder);
pMalloc->lpVtbl->Release (pMalloc);
return bResult;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// FUNCTION: GetItemCount
//
// DESCRIPTION: Computes the number of item IDs in an item ID list.
//
// INPUT: pidl = Pointer to an item ID list.
//
// RETURNS: Number of item IDs in the list.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UINT GetItemCount (LPITEMIDLIST pidl)
{
USHORT nLen;
UINT nCount;
nCount = 0;
while ((nLen = pidl->mkid.cb) != 0) {
pidl = GetNextItem (pidl);
nCount++;
}
return nCount;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// FUNCTION: GetNextItem
//
// DESCRIPTION: Finds the next item in an item ID list.
//
// INPUT: pidl = Pointer to an item ID list.
//
// RETURNS: Pointer to the next item.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LPITEMIDLIST GetNextItem (LPITEMIDLIST pidl)
{
USHORT nLen;
if ((nLen = pidl->mkid.cb) == 0)
return NULL;
return (LPITEMIDLIST) (((LPBYTE) pidl) + nLen);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// FUNCTION: DuplicateItem
//
// DESCRIPTION: Makes a copy of the next item in an item ID list.
//
// INPUT: pMalloc = Pointer to an IMalloc interface.
// pidl = Pointer to an item ID list.
//
// RETURNS: Pointer to an ITEMIDLIST containing the copied item ID.
//
// NOTES: It is the caller's responsibility to free the memory
// allocated by this function when the item ID is no longer
// needed. Example:
//
// pidlItem = DuplicateItem (pMalloc, pidl);
// .
// .
// .
// pMalloc->lpVtbl->Free (pMalloc, pidlItem);
//
// Failure to free the ITEMIDLIST will result in memory
// leaks.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LPITEMIDLIST DuplicateItem (LPMALLOC pMalloc, LPITEMIDLIST pidl)
{
USHORT nLen;
LPITEMIDLIST pidlNew;
nLen = pidl->mkid.cb;
if (nLen == 0)
return NULL;
pidlNew = (LPITEMIDLIST) pMalloc->lpVtbl->Alloc (pMalloc,
nLen + sizeof (USHORT));
if (pidlNew == NULL)
return NULL;
CopyMemory (pidlNew, pidl, nLen);
*((USHORT*) (((LPBYTE) pidlNew) + nLen)) = 0;
return pidlNew;
}