Today’s Little Program will show a property sheet that covers multiple files, just like the one you get from Explorer if you multi-select a bunch of files and right-click them all then select Properties.
In fact, that description of how you do the operation interactively maps directly to how you do the operation programmatically!
#define UNICODE #define _UNICODE #define STRICT_TYPED_ITEMIDS #include <windows.h> #include <ole2.h> #include <shlobj.h> #include <atlbase.h> #include <atlalloc.h>HRESULT GetUIObjectOf( IShellFolder *psf, HWND hwndOwner, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, REFIID riid, void **ppv) { return psf->GetUIObjectOf(hwndOwner, cidl, apidl, riid, nullptr, ppv); }
The GetUIObjectOf
helper function
merely wraps the
IShellFolder::GetUIObjectOf
method to insert the pesky nullptr
parameter
between the riid
and ppv
.
The riid
and ppv
parameters
by convention go right next to each other,
and the IID_PPV_ARGS
macro assumes
that the function you’re calling follows that convention.
Unfortunately, the people who designed
IShellFolder::GetUIObjectOf
didn’t get the memo, and we’ve been stuck with it ever since.
HRESULT InvokeCommandByVerb( IContextMenu *pcm, HWND hwnd, LPCSTR pszVerb) { HMENU hmenu = CreatePopupMenu(); HRESULT hr = hmenu ? S_OK : E_OUTOFMEMORY; if (SUCCEEDED(hr)) { hr = pcm->QueryContextMenu(hmenu, 0, 1, 0x7FFF, CMF_NORMAL); if (SUCCEEDED(hr)) { CMINVOKECOMMANDINFO info = { 0 }; info.cbSize = sizeof(info); info.hwnd = hwnd; info.lpVerb = pszVerb; hr = pcm->InvokeCommand(&info); } DestroyMenu(hmenu); } return hr; }
The InvokeCommandByVerb
function
merely
hosts an IContextMenu
and invokes a single verb.
Okay, those are the only two helper functions we need this week. The rest we can steal from earlier articles.
For the purpose of illustration, the program will display a multi-file property sheet for the first two files in your My Documents folder folder. Remember, Little Programs do little to no error checking.
int __cdecl wmain(int, wchar_t **) { CCoInitialize init; ProcessReference ref; CComPtr<IShellFolder> spsf; BindToCsidl(CSIDL_MYDOCUMENTS, IID_PPV_ARGS(&spsf)); CComPtr<IEnumIDList> speidl; spsf->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &speidl); if (!speidl) return 0; CComHeapPtr<ITEMID_CHILD> spidl1; CComHeapPtr<ITEMID_CHILD> spidl2; if (speidl->Next(1, &spidl1, nullptr) != S_OK) return 0; if (speidl->Next(1, &spidl2, nullptr) != S_OK) return 0; PCUITEMID_CHILD rgpidl[2] = { spidl1, spidl2 }; CComPtr<IContextMenu> spcm; GetUIObjectOf(spsf, nullptr, 2, rgpidl, IID_PPV_ARGS(&spcm)); if (!spcm) return 0; InvokeCommandByVerb(spcm, “properties”); return 0; }
Because everybody freaks out if I write code that doesn’t
run on Windows XP,
I used the
BindToCSIDL
function
instead of one of its more modern equivalents
to get access to the My Documents folder.
Once we have My Documents,
we ask to enumerate its non-folders.
If the enumeration fails or says that there are no items (by returning
S_FALSE
), then we bail immediately.
Next, we enumerate two items from the folder. If we can’t get both, then we bail.
We then create a two-item array and
get the IContextMenu
UI object
for the collection.
Finally, we invoke the “properties”
verb on the context menu.
And that’s it. If you run this program, you’ll see a context menu for the first two files in your My Documents folder.
0 comments