如何在MFC中实现IExplorerBrowser

时间:2009-10-22 21:21:24

标签: c++ com mfc windows-shell

显然,从Vista开始,在应用程序中托管资源管理器是一种相当简单的方法: http://www.codeproject.com/KB/vista/ExplorerBrowser.aspx

但是,该界面仅适用于Vista。

我看到还有另一种方法可以做到:“一直回到95,但需要更多工作 - 实现IExplorerBrowser并通过IShellFolder :: CreateViewObject(IID_IShellView)从数据源获取视图”

所以我想要采用后一种方法:实现IExplorerBrowser。

我从哪里获得一个IShellFolder *来首先让球滚动? 如何指定容纳shell视图控件的主机窗口? 如何为shell视图指定bounds(并调整其大小)?

是否有一套全面的文档 - 或者是一个白页 - 为Windows Shell记录这些界面的地方?到目前为止我收集到的信息似乎非常破碎,有一些例子非常过时甚至无法编译(它们需要对当前版本的ATL进行大量重写),而且没有我能找到的例子MFC。

4 个答案:

答案 0 :(得分:1)

首先打电话给SHGetDesktopFolder(),你可以让球滚动。这将为您提供桌面的IShellFolder。然后调用ISF::BindToObject()以获取要查看的特定子文件夹的IShellFolder。如果您没有所需子文件夹的PIDL,可以调用SHParseDisplayName()来获取该PIDL。

答案 1 :(得分:0)

不幸的是,我从未走过这条路。相反,我改编了CFileDialog来实现我想要的,以XP..Windows 7兼容的方式。

解决方案的关键是从通用对话框控件中获取IShellBrowser *实例:

// return the IShellBrowser for the common dialog
// NOTE: we force CComPtr to create a new AddRef'd copy (since the one that this gives us is synthesized, and hasn't had an AddRef on our behalf)
CComPtr<IShellBrowser> GetShellBrowser() const { return (IShellBrowser*)::SendMessage(GetCommonDialogHwnd(), CDM_GETISHELLBROWSER, 0, 0); }

为了做更好的东西(比如找出真正选择的内容 - 无论用户是否隐藏了文件扩展名,它的真实身份是什么) - 我使用生成的IShellBrowser *。

例如:

//////////////////////////////////////////////////////////////////////////
// Get display name of item in file open dialog. Flags tell how.
// SHGDN_FORPARSING gets the full path name even when user has
// checked `Hide extensions for known file types` in Explorer.
//
CString CMFCToolboxAdvancedFileDialog::GetDisplayNameOfItem(int nItem) const
{
    // get the item ID of the given item from the list control
    LPITEMIDLIST pidlAbsolute = GetItemIDListOf(nItem);

    // no PIDL = no display name 
    if (!pidlAbsolute)
        return "";

    // get the display name of our item from the folder IShellFolder interface
    CString path = GetDisplayNameOf(pidlAbsolute);

    // deallocate the PIDL
    ILFree(pidlAbsolute);

    // return the pathname
    return path;
}

哪个电话:

// return the ITEMIDLIST for the item at the specified index in the list view (caller is responsible for freeing)
LPITEMIDLIST CMFCToolboxAdvancedFileDialog::GetItemIDListOf(UINT nItem) const
{
    // This can only succeed if there is an IShellView currently (which implies there is a list control)
    CListCtrl * pListCtrl = GetListCtrl();
    if (!pListCtrl)
        return NULL;

    // Use undocumented method (the pidl is stored in the item data)
    // NOTE: Much thanks to Paul DiLascia for this technique (worked up until Vista)
    //       http://www.dilascia.com/index.htm
    if (LPCITEMIDLIST pidlChild = (LPCITEMIDLIST)pListCtrl->GetItemData(nItem))
    {

        // get PIDL of current folder from the common dialog
        LRESULT len = ::SendMessage(GetCommonDialogHwnd(), CDM_GETFOLDERIDLIST, 0, NULL);
        if (!len)
            return NULL;
        LPCITEMIDLIST pidlFolder = (LPCITEMIDLIST)CoTaskMemAlloc(len);
        ::SendMessage(GetCommonDialogHwnd(), CDM_GETFOLDERIDLIST, len, (LPARAM)(void*)pidlFolder);

        // return the absolute ITEMIDLIST
        return ILCombine(pidlFolder, pidlChild);
    }

    // Use another undocumented feature: WM_GETISHELLBROWSER
    CComPtr<IShellBrowser> pShellBrowser(GetShellBrowser());
    if (!pShellBrowser)
        return NULL;

    // attempt to get access to the view
    CComPtr<IShellView> pShellView;
    if (FAILED(pShellBrowser->QueryActiveShellView(&pShellView)))
        return NULL;

    // attempt to get an IDataObject of all items in the view (in view-order)
    CComPtr<IDataObject> pDataObj;
    if (FAILED(pShellView->GetItemObject(SVGIO_ALLVIEW|SVGIO_FLAG_VIEWORDER, IID_IDataObject, (void**)&pDataObj)))
        return NULL;

    // attempt to get the ITEMIDLIST from our clipboard data object
    const UINT cfFormat = RegisterClipboardFormat(CFSTR_SHELLIDLIST);
    FORMATETC fmtetc = { cfFormat, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    ClipboardStorageMedium stgmed;
    if (FAILED(pDataObj->GetData(&fmtetc, &stgmed)))
        return NULL;

    // cast to the actual data requested
    CIDA * pida = (CIDA*)stgmed.hGlobal;

    // ensure we have that index
    ASSERT(pida->cidl > nItem);
    if (nItem >= pida->cidl)
        return NULL;

    // find the data for the item requested
    const ITEMIDLIST * pidlParent = GetPIDLFolder(pida);
    const ITEMIDLIST * pidlChild = GetPIDLItem(pida, nItem);

    // return the absolute PIDL
    return ILCombine(pidlParent, pidlChild);
}

哪个电话:

// NOTE: this is the only way I know to get the actual list control!
CListCtrl * GetListCtrl() const
{
    // return &GetListView()->GetListCtrl();

    // we have to be a window to answer such a question
    ASSERT(IsWindow(GetCommonDialogHwnd()));

    HWND hwnd = ::GetDlgItem(GetCommonDialogHwnd(), IDC_FILE_LIST_VIEW);
    if (hwnd)
        return static_cast<CListCtrl*>(CListCtrl::FromHandle(::GetWindow(hwnd, GW_CHILD)));
    return NULL;
}

嗯,希望能给你这个想法,你可以从这里开始。克/升! ;)

答案 2 :(得分:0)

您真的不想实现IExplorerBrowser,您想知道如何直接使用IShellView

jeffamaphone's answer应该足以获得从中获取IShellView的初始接口。之后,IShellView::CreateViewWindow方法可能是一个开始的好地方。

顺便说一句,MFC可能与此过程无关。使用ATL智能指针CComPtr或CComQIPtr来保存接口指针。 MFC是纯Windows对象的包装器,但COM接口隐藏了所有这些内容。

答案 3 :(得分:-1)

这样做可能会使某些shell命名空间扩展认为它们在Vista上运行并触发不希望的结果。

为什么您认为需要为Vista之前的Window版本实现IExplorerBrowser?谁将成为您界面的客户?此界面在Windows SDK头文件中受到保护,以防止在早期版本中使用它。

http://www.codeproject.com/KB/shell/有一些shell视图托管示例。我担心早期版本中的shell视图托管并不像使用Vista的IExplorerBrowser那么容易。