This question is related to my previous question.
I need to obtain IContextMenu*
interface pointer for files in different directories (or even drives).
See my code, which is not working properly (e.g. the file properties dialog shows wrong information), because I provide wrong relative PIDLs (as mentioned in this answer).
int main() {
CoInitialize(NULL);
LPOLESTR pszFile = OLESTR("c:\\Windows\\notepad.exe");
LPOLESTR pszFile2 = OLESTR("c:\\Windows\\System32\\notepad.exe");
//LPOLESTR pszDir = OLESTR("c:\\Windows\\");
LPITEMIDLIST pidl = NULL;
LPITEMIDLIST pidl2 = NULL;
LPITEMIDLIST pidlDir;
LPCITEMIDLIST pidlItem;
LPCITEMIDLIST pidlItem2;
HRESULT hr;
IShellFolder* pFolder;
//IShellFolder* pDir;
IShellFolder* pDesktop;
IContextMenu* pContextMenu;
HMENU hMenu;
CMINVOKECOMMANDINFO cmi;
TCHAR szTemp[256];
hr = SHGetDesktopFolder(&pDesktop);
if (FAILED(hr)) {
CoUninitialize();
return 0;
}
HWND wnd = ::CreateWindowA("STATIC", "dummy", WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL);
/*hr = pDesktop->ParseDisplayName(wnd, NULL, pszDir, NULL, &pidlDir, NULL);
if (FAILED(hr)) {
goto clear;
}
hr = pDesktop->BindToObject(pidlDir, 0, IID_IShellFolder, (void**)&pDir);
if (FAILED(hr)) {
goto clear;
}
*/
hr = pDesktop->ParseDisplayName(wnd, NULL, pszFile, NULL, &pidl, NULL);
if (FAILED(hr)) {
goto clear;
}
hr = pDesktop->ParseDisplayName(wnd, NULL, pszFile2, NULL, &pidl2, NULL);
if (FAILED(hr)) {
goto clear;
}
hr = SHBindToParent(pidl, IID_IShellFolder, (void **)&pFolder, &pidlItem);
if (FAILED(hr)) {
goto clear;
}
pFolder->Release();
hr = SHBindToParent(pidl2, IID_IShellFolder, (void **)&pFolder, &pidlItem2);
if (FAILED(hr)) {
goto clear;
}
LPCITEMIDLIST list[] = {pidlItem, pidlItem2};
hr = pFolder->GetUIObjectOf(wnd, 2, (LPCITEMIDLIST *)list, IID_IContextMenu, NULL, (void **)&pContextMenu);
pFolder->Release();
if (SUCCEEDED(hr)) {
hMenu = CreatePopupMenu();
if (hMenu) {
hr = pContextMenu->QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_EXPLORE);
if (SUCCEEDED(hr)) {
int idCmd = TrackPopupMenu(hMenu,
TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON,
1, 1, 0, wnd, NULL);
if (idCmd) {
cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
cmi.fMask = 0;
cmi.hwnd = wnd;
cmi.lpVerb = MAKEINTRESOURCEA(idCmd-1);
cmi.lpParameters = NULL;
cmi.lpDirectory = NULL;
cmi.nShow = SW_SHOWNORMAL;
cmi.dwHotKey = 0;
cmi.hIcon = NULL;
hr = pContextMenu->InvokeCommand(&cmi);
if (!SUCCEEDED(hr)) {
wsprintf(szTemp, _T("InvokeCommand failed. hr=%lx"), hr);
MessageBox(0, szTemp, 0, 0);
PostQuitMessage(0);
}
}
}
DestroyMenu(hMenu);
}
pContextMenu->Release();
}
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (bRet == -1) {
// Handle Error
}
else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
clear:
pDesktop->Release();
SHFree(pidl);
SHFree(pidl2);
CoUninitialize();
return 0;
}
Maybe it is possible to achieve using SHCreateDefaultContextMenu
or CDefFolderMenu_Create2
but I don't know how.
It is possible if you embed
IExplorerBrowser
UI object into your app, which is available since Windows Vista. The you can fill it with any PIDLs throughIResultsFolder
. Here is sample code, which is just slightly modified example from Win7 Platform SDK (C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\winui\shell\appplatform\ExplorerBrowserCustomContents\ExplorerBrowserCustomContents.sln)There is definitely an another method using
IShellView
which works on Windows XP but I didn't find it anyway.