如何在MFC中实现IExplorerBrowser

发布于 2024-08-08 20:20:34 字数 620 浏览 6 评论 0 原文

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

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

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

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

我从哪里获取 IShellFolder * 才能首先开始工作? 如何指定宿主窗口来容纳 shell 视图控件? 如何指定 shell 视图的边界矩形(并调整其大小)?

是否有一套全面的文档(或白页)记录了 Windows Shell 的这些接口?到目前为止,我收集到的信息似乎非常支离破碎,有一些示例非常过时,甚至无法编译(它们需要对当前版本的 ATL 进行大量重写),而且我找不到任何示例根本就没有MFC。

Apparently, there's a fairly easy way to host Explorer in your app starting with Vista:
http://www.codeproject.com/KB/vista/ExplorerBrowser.aspx

However, that interface is only available starting with Vista.

I see that there is another way to do it: "going all the way back to 95, but it requires more work - implement IExplorerBrowser and obtain a view from the data source via IShellFolder::CreateViewObject(IID_IShellView)"

So I'd like to go this latter route: implement IExplorerBrowser.

Where do I get a IShellFolder * from to get the ball rolling in the first place?
How do I specify the host window to house the shell view control?
How do I specify the bounds rect for the shell view (and resize it)?

Is there a comprehensive set of docs - or a whitepages - someplace that documents these interfaces for the Windows Shell? The information that I've gleaned so far seems to be very fractured, with a few examples that are very outdated and won't even compile (they require extensive rewriting to the current version of ATL), and no examples that I can find for MFC at all.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

剧终人散尽 2024-08-15 20:20:34

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

You can get the ball rolling by first calling SHGetDesktopFolder(). This will give you the IShellFolder for the desktop. Then call ISF::BindToObject() to get the IShellFolder for the particular sub-folder that you want a view of. If you don't have a PIDL for the child folder you want, you can call SHParseDisplayName() to get that PIDL.

征﹌骨岁月お 2024-08-15 20:20:34

不幸的是,我最终没有走这条路。相反,我以 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;
}

嗯,希望这能给您带来想法,您可以从这里开始。总帐! ;)

Unfortunately, I never did end up going this route. Instead, I adapted the CFileDialog to achieve what I wanted, in an XP..Windows 7 compatible way.

The crux of the solution is to obtain the IShellBrowser* instance from the common dialog control:

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

In order to do fancier stuff (like figure out what really is selected - what is its true identity regardless of whether the user has hidden file extensions or not) - I use the resulting IShellBrowser*.

For example:

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

Which calls:

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

Which calls:

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

Well, hopefully that gives you the idea, and you can go from here. G/L! ;)

过气美图社 2024-08-15 20:20:34

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

jeffamaphone的答案应该足以获取初始界面获取IShellView。之后 IShellView::CreateViewWindow 方法是可能是一个很好的起点。

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

You don't really want to implement IExplorerBrowser, you want to know how to work with IShellView directly.

jeffamaphone's answer should suffice for getting the initial interface from which to obtain the IShellView. After that the IShellView::CreateViewWindow method is probably a good place to get started.

By the way, MFC is probably irrelevant to this process. Use the ATL smart pointers CComPtr or CComQIPtr to hold the interface pointers. MFC is a wrapper for pure Windows objects, but the COM interfaces hide all that from you.

秉烛思 2024-08-15 20:20:34

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

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

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

Doing so could make some shell namespace extensions think they are running on Vista and trigger undesired results.

Why you think you need to implement IExplorerBrowser for Window versions earlier than Vista? Who would be your interface's clients? This interface is guarded in Windows SDK header files to prevent it to be used in earlier versions.

There are some shell view hosting examples at http://www.codeproject.com/KB/shell/. I am afraid shell view hosting in earlier versions is not as easy as using Vista's IExplorerBrowser .

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文