1

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

但是,该界面仅从 Vista 开始可用。

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

所以我想走后一条路线:实现 IExplorerBrowser。

我从哪里得到一个 IShellFolder * 来让球滚起来?如何指定主机窗口来容纳 shell 视图控件?如何为 shell 视图指定边界矩形(并调整它的大小)?

是否有一套全面的文档 - 或白页 - 在某个地方记录了 Windows Shell 的这些接口?到目前为止,我收集到的信息似乎非常零散,有一些非常过时的示例,甚至无法编译(它们需要对当前版本的 ATL 进行大量重写),并且没有我可以找到的示例完全是MFC。

4

4 回答 4

1

您可以通过首先调用SHGetDesktopFolder()来获得成功。这将为您提供桌面的 IShellFolder。然后调用ISF::BindToObject()以获取您想要查看的特定子文件夹的 IShellFolder。如果您没有所需的子文件夹的 PIDL,则可以调用SHParseDisplayName()来获取该 PIDL。

于 2009-10-25T18:17:14.047 回答
0

不幸的是,我最终没有走这条路。相反,我以与 XP..Windows 7 兼容的方式调整了 CFileDialog 以实现我想要的。

解决方案的关键是从通用对话框控件中获取 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;
}

好吧,希望这能给你一个想法,你可以从这里开始。总帐!;)

于 2010-09-23T18:42:28.733 回答
0

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

jeffamaphone 的回答应该足以获取从中获取 IShellView 的初始界面。之后,IShellView::CreateViewWindow方法可能是开始的好地方。

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

于 2010-09-23T19:13:05.243 回答
-1

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

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

在http://www.codeproject.com/KB/shell/有一些 shell 视图托管示例。恐怕早期版本中的 shell 视图托管不像使用 Vista 的 IExplorerBrowser 那样容易。

于 2009-10-25T18:09:25.060 回答