如何在不同文件夹中显示多个文件的系统上下文菜单?

时间:2016-01-01 05:40:42

标签: c++ winapi com

此问题与my previous question有关。

我需要为不同目录(甚至驱动器)中的文件获取IContextMenu*接口指针。

查看我的代码,无法正常工作(例如文件属性对话框显示错误的信息),因为我提供了错误的相对PIDL(如{{3中所述) }})。

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;
}

也许可以使用this answerSHCreateDefaultContextMenu来实现,但我不知道如何使用。

1 个答案:

答案 0 :(得分:1)

如果您将IExplorerBrowser UI对象嵌入到自Windows Vista以来可用的应用程序中,则可以这样做。 您可以通过IResultsFolder填充任何PIDL。 下面是示例代码,它只是Win7 Platform SDK(C:\ Program Files \ Microsoft SDKs \ Windows \ v7.1 \ Samples \ winui \ shell \ appplatform \ ExplorerBrowserCustomContents \ ExplorerBrowserCustomContents.sln)中的略微修改示例

肯定有另一种使用IShellView的方法可以在Windows XP上运行,但我还是找不到它。

enter image description here

#define STRICT_TYPED_ITEMIDS
#include <windows.h>
#include <windowsx.h>
#include <shlobj.h>
#include <shellapi.h>
#include <shlwapi.h>
#include <propkey.h>
#include <new>
#include "resource.h"

#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

HINSTANCE g_hinst = 0;

UINT const KFD_SELCHANGE = WM_USER;

class CFillResultsOnBackgroundThread;

class CExplorerBrowserHostDialog : public IServiceProvider, public ICommDlgBrowser
{
public:
    CExplorerBrowserHostDialog() : _cRef(1), _hdlg(NULL), _peb(NULL), _fEnumerated(FALSE), _prf(NULL)
    {
    }

    HRESULT DoModal(HWND hwnd)
    {
        DialogBoxParam(g_hinst, MAKEINTRESOURCE(IDD_DIALOG1), hwnd, s_DlgProc, (LPARAM)this);
        return S_OK;
    }

    // IUnknown
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
    {
        static const QITAB qit[] =
        {
            QITABENT(CExplorerBrowserHostDialog, IServiceProvider),
            QITABENT(CExplorerBrowserHostDialog, ICommDlgBrowser),
            { 0 },
        };
        return QISearch(this, qit, riid, ppv);
    }

    STDMETHODIMP_(ULONG) AddRef()
    {
        return InterlockedIncrement(&_cRef);
    }

    STDMETHODIMP_(ULONG) Release()
    {
        long cRef = InterlockedDecrement(&_cRef);
        if (!cRef)
            delete this;
        return cRef;
    }

    // IServiceProvider
    STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppv)
    {
        HRESULT hr = E_NOINTERFACE;
        *ppv = NULL;
        if (guidService == SID_SExplorerBrowserFrame)
        {
            // responding to this SID allows us to hook up our ICommDlgBrowser
            // implementation so we get selection change events from the view
            hr = QueryInterface(riid, ppv);
        }
        return hr;
    }

    // ICommDlgBrowser
    STDMETHODIMP OnDefaultCommand(IShellView * /* psv */)
    {
        _OnExplore();
        return S_OK;
    }

    STDMETHODIMP OnStateChange(IShellView * /* psv */, ULONG uChange)
    {
        if (uChange == CDBOSC_SELCHANGE)
        {
            PostMessage(_hdlg, KFD_SELCHANGE, 0, 0);
        }
        return S_OK;
    }

    STDMETHODIMP IncludeObject(IShellView * /* psv */, PCUITEMID_CHILD /* pidl */)
    {
        return S_OK;
    }

    void FillResultsOnBackgroundThread(IResultsFolder *prf);

private:
    ~CExplorerBrowserHostDialog()
    {
    }

    static INT_PTR CALLBACK s_DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        CExplorerBrowserHostDialog *pebhd = reinterpret_cast<CExplorerBrowserHostDialog *>(GetWindowLongPtr(hdlg, DWLP_USER));
        if (uMsg == WM_INITDIALOG)
        {
            pebhd = reinterpret_cast<CExplorerBrowserHostDialog *>(lParam);
            pebhd->_hdlg = hdlg;
            SetWindowLongPtr(hdlg, DWLP_USER, reinterpret_cast<LONG_PTR>(pebhd));
        }
        return pebhd ? pebhd->_DlgProc(uMsg, wParam, lParam) : 0;
    }

    INT_PTR _DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
    HRESULT _FillViewWithKnownFolders(IResultsFolder *prf);
    void _OnInitDlg();
    void _OnDestroyDlg();
    void _StartFolderEnum();
    void _OnSelChange();
    void _OnExplore();
    void _OnRefresh();

    long _cRef;
    HWND _hdlg;

    IExplorerBrowser *_peb;
    IResultsFolder *_prf;
    BOOL _fEnumerated;
    static const UINT c_rgControlsShownOnEnum[3];  // controls that will be shown while known folder list is populated
    static const UINT c_rgControlsHiddenOnEnum[4]; // controls that will be hidden while known folder list is populated
};

const UINT CExplorerBrowserHostDialog::c_rgControlsShownOnEnum[] =
{
    IDC_STATUS,
    IDC_ENUMNAME,
    IDC_ENUMPATH
};

const UINT CExplorerBrowserHostDialog::c_rgControlsHiddenOnEnum[] =
{
    IDC_FOLDERNAME,
    IDC_FOLDERPATH,
    IDC_LBLFOLDER,
    IDC_LBLPATH
};

HRESULT CExplorerBrowserHostDialog::_FillViewWithKnownFolders(IResultsFolder *prf)
{
    LPOLESTR pszFile = OLESTR("c:\\Windows\\notepad.exe");
    LPOLESTR pszFile2 = OLESTR("c:\\Windows\\System32\\notepad.exe");
    IShellFolder* pDesktop;
    PIDLIST_RELATIVE pidl = NULL;
    PIDLIST_RELATIVE pidl2 = NULL;
    HRESULT hr;

    hr = SHGetDesktopFolder(&pDesktop);
    if (FAILED(hr)) {
        return E_FAIL;
    }

    hr = pDesktop->ParseDisplayName(0, NULL, pszFile, NULL, &pidl, NULL);
    if (FAILED(hr)) {
        pDesktop->Release();
        return E_FAIL;
    }

    hr = pDesktop->ParseDisplayName(0, NULL, pszFile2, NULL, &pidl2, NULL);
    if (FAILED(hr)) {
        pDesktop->Release();
        return E_FAIL;
    }
    prf->AddIDList((PIDLIST_ABSOLUTE)pidl, 0);
    prf->AddIDList((PIDLIST_ABSOLUTE)pidl2, 0);
    pDesktop->Release();
    IFolderView2 *pfv2;
    IShellView *shellView;
    hr = _peb->GetCurrentView(IID_PPV_ARGS(&pfv2));
    if (SUCCEEDED(hr)) {
        pfv2->SelectItem(0, SVSI_SELECT);
        pfv2->SelectItem(1, SVSI_SELECT);
        //hr = pfv2->QueryInterface(IID_IOleWindow, (void**)&oleWnd);
        hr = _peb->GetCurrentView(IID_PPV_ARGS(&shellView));
        if (SUCCEEDED(hr)) {
            HWND wnd = 0;
            shellView->GetWindow(&wnd);
            //SendMessage(wnd, WM_CONTEXTMENU, 0, MAKELPARAM(0,0)); not working
        }
    }

    return S_OK;
}

void CExplorerBrowserHostDialog::_OnInitDlg()
{
    // Hide initial folder information
    for (UINT i = 0; i < ARRAYSIZE(c_rgControlsHiddenOnEnum); i++)
    {
        ShowWindow(GetDlgItem(_hdlg, c_rgControlsHiddenOnEnum[i]), SW_HIDE);
    }

    HWND hwndStatic = GetDlgItem(_hdlg, IDC_BROWSER);
    if (hwndStatic)
    {
        RECT rc;
        GetWindowRect(hwndStatic, &rc);
        MapWindowRect(HWND_DESKTOP, _hdlg, &rc);

        HRESULT hr = CoCreateInstance(CLSID_ExplorerBrowser, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&_peb));
        if (SUCCEEDED(hr))
        {
            IUnknown_SetSite(_peb, static_cast<IServiceProvider *>(this));

            FOLDERSETTINGS fs = {0};
            fs.ViewMode = FVM_DETAILS;
            fs.fFlags = FWF_AUTOARRANGE | FWF_NOWEBVIEW;
            hr = _peb->Initialize(_hdlg, &rc, &fs);
            if (SUCCEEDED(hr))
            {
                _peb->SetOptions(EBO_NAVIGATEONCE); // do not allow navigations

                // Initialize the explorer browser so that we can use the results folder
                // as the data source. This enables us to program the contents of
                // the view via IResultsFolder

                hr = _peb->FillFromObject(NULL, EBF_NODROPTARGET);
                if (SUCCEEDED(hr))
                {
                    IFolderView2 *pfv2;
                    hr = _peb->GetCurrentView(IID_PPV_ARGS(&pfv2));
                    if (SUCCEEDED(hr))
                    {
                        IColumnManager *pcm;
                        hr = pfv2->QueryInterface(IID_PPV_ARGS(&pcm));
                        if (SUCCEEDED(hr))
                        {
                            PROPERTYKEY rgkeys[] = {PKEY_ItemNameDisplay, PKEY_ItemFolderPathDisplay};
                            hr = pcm->SetColumns(rgkeys, ARRAYSIZE(rgkeys));
                            if (SUCCEEDED(hr))
                            {
                                CM_COLUMNINFO ci = {sizeof(ci), CM_MASK_WIDTH | CM_MASK_DEFAULTWIDTH | CM_MASK_IDEALWIDTH};
                                hr = pcm->GetColumnInfo(PKEY_ItemFolderPathDisplay, &ci);
                                if (SUCCEEDED(hr))
                                {
                                    ci.uWidth += 100;
                                    ci.uDefaultWidth += 100;
                                    ci.uIdealWidth += 100;
                                    pcm->SetColumnInfo(PKEY_ItemFolderPathDisplay, &ci);
                                }
                            }
                            pcm->Release();
                        }

                        hr = pfv2->GetFolder(IID_PPV_ARGS(&_prf));
                        if (SUCCEEDED(hr))
                        {
                            _StartFolderEnum();
                        }
                        pfv2->Release();
                    }
                }
            }
        }
        // If we fail to initialize properly, close the dialog
        if (FAILED(hr))
        {
            EndDialog(_hdlg, IDCLOSE);
        }
    }
}

// pass -1 for the current selected item
// returns an IShellItem type object

HRESULT GetItemFromView(IFolderView2 *pfv, int iItem, REFIID riid, void **ppv)
{
    *ppv = NULL;

    HRESULT hr = S_OK;

    if (iItem == -1)
    {
        hr = pfv->GetSelectedItem(-1, &iItem); // Returns S_FALSE if none selected
    }

    if (S_OK == hr)
    {
        hr = pfv->GetItem(iItem, riid, ppv);
    }
    else
    {
        hr = E_FAIL;
    }
    return hr;
}

void CExplorerBrowserHostDialog::_OnSelChange()
{
    if (_fEnumerated)
    {
        IFolderView2 *pfv2;
        HRESULT hr = _peb->GetCurrentView(IID_PPV_ARGS(&pfv2));
        if (SUCCEEDED(hr))
        {
            IShellItem2 *psi;
            hr = GetItemFromView(pfv2, -1, IID_PPV_ARGS(&psi));
            if (SUCCEEDED(hr))
            {
                PWSTR pszName;
                hr = psi->GetDisplayName(SIGDN_NORMALDISPLAY, &pszName);
                if (SUCCEEDED(hr))
                {
                    SetDlgItemText(_hdlg, IDC_FOLDERNAME, pszName);
                    CoTaskMemFree(pszName);
                }

                hr = psi->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszName);
                if (SUCCEEDED(hr))
                {
                    SetDlgItemText(_hdlg, IDC_FOLDERPATH, pszName);
                    CoTaskMemFree(pszName);
                }

                psi->Release();
            }
            else if (hr == S_FALSE)
            {
                SetDlgItemText(_hdlg, IDC_FOLDERNAME, TEXT(""));
                SetDlgItemText(_hdlg, IDC_FOLDERPATH, TEXT(""));
            }

            EnableWindow(GetDlgItem(_hdlg, IDC_EXPLORE), hr == S_OK);

            pfv2->Release();
        }
    }
}

void CExplorerBrowserHostDialog::_OnDestroyDlg()
{
    if (_peb)
    {
        IUnknown_SetSite(_peb, NULL);
        _peb->Destroy();
        _peb->Release();
        _peb = NULL;
    }

    if (_prf)
    {
        _prf->Release();
        _prf = NULL;
    }
}

void CExplorerBrowserHostDialog::_OnExplore()
{
    IFolderView2 *pfv2;
    HRESULT hr = _peb->GetCurrentView(IID_PPV_ARGS(&pfv2));
    if (SUCCEEDED(hr))
    {
        IShellItem *psi;
        hr = GetItemFromView(pfv2, -1, IID_PPV_ARGS(&psi));
        if (SUCCEEDED(hr))
        {
            PIDLIST_ABSOLUTE pidl;
            hr = SHGetIDListFromObject(psi, &pidl);
            if (SUCCEEDED(hr))
            {
                SHELLEXECUTEINFO ei = { sizeof(ei) };
                ei.fMask        = SEE_MASK_INVOKEIDLIST;
                ei.hwnd         = _hdlg;
                ei.nShow        = SW_NORMAL;
                ei.lpIDList     = pidl;

                ShellExecuteEx(&ei);

                CoTaskMemFree(pidl);
            }
            psi->Release();
        }
        pfv2->Release();
    }
}

void CExplorerBrowserHostDialog::_OnRefresh()
{
    _fEnumerated = FALSE;

    // Update UI
    EnableWindow(GetDlgItem(_hdlg, IDC_EXPLORE), FALSE);
    EnableWindow(GetDlgItem(_hdlg, IDC_REFRESH), FALSE);

    if (SUCCEEDED(_peb->RemoveAll()))
    {
        _StartFolderEnum();
    }
}

void CExplorerBrowserHostDialog::_StartFolderEnum()
{
    FillResultsOnBackgroundThread(_prf);
}

void CExplorerBrowserHostDialog::FillResultsOnBackgroundThread(IResultsFolder *prf)
{
    _FillViewWithKnownFolders(prf);

    _fEnumerated = TRUE;
/*
    // Adjust dialog to show proper view info and buttons
    for (UINT k = 0; k < ARRAYSIZE(c_rgControlsShownOnEnum); k++)
    {
        ShowWindow(GetDlgItem(_hdlg, c_rgControlsShownOnEnum[k]), SW_HIDE);
    }
    for (UINT l = 0; l < ARRAYSIZE(c_rgControlsHiddenOnEnum); l++)
    {
        ShowWindow(GetDlgItem(_hdlg, c_rgControlsHiddenOnEnum[l]), SW_SHOW);
    }*/
    EnableWindow(GetDlgItem(_hdlg, IDC_REFRESH), TRUE);
}

INT_PTR CExplorerBrowserHostDialog::_DlgProc(UINT uMsg, WPARAM wParam, LPARAM /* lParam */)
{
    INT_PTR iRet = 1;   // default for all handled cases in switch below

    switch (uMsg)
    {
    case WM_INITDIALOG:
        _OnInitDlg();
        break;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
        case IDCANCEL:
        case IDC_CANCEL:
            return EndDialog(_hdlg, TRUE);

        case IDC_REFRESH:
            _OnRefresh();
            break;

        case IDC_EXPLORE:
            _OnExplore();
            break;
        }
        break;

    case KFD_SELCHANGE:
        _OnSelChange();
        break;

    case WM_DESTROY:
        _OnDestroyDlg();
        break;

    default:
        iRet = 0;
        break;
    }
    return iRet;
}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int)
{
    g_hinst = hInstance;

    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (SUCCEEDED(hr))
    {
        OleInitialize(0);   // for drag and drop

        CExplorerBrowserHostDialog *pdlg = new (std::nothrow) CExplorerBrowserHostDialog();
        if (pdlg)
        {
            pdlg->DoModal(NULL);
            pdlg->Release();
        }

        OleUninitialize();
        CoUninitialize();
    }

    return 0;
}