显示不同目录中文件的文件属性对话框

Display file properties dialog for files in different directories

我正在尝试打开默认文件属性对话框,但无法使其对不同目录中的文件正常工作属性。 我需要为不同目录(甚至驱动器)中的文件获取 IContextMenu* 接口指针。 我希望在属性对话框中看到 "Various folders"。

这是我天真的做法:

int main() {
    CoInitialize(NULL);
    LPOLESTR pszFile = OLESTR("c:\Windows\notepad.exe");
    LPOLESTR pszFile2 = OLESTR("c:\Windows\System32\notepad.exe");
    LPITEMIDLIST pidl;
    LPITEMIDLIST pidl2;
    LPCITEMIDLIST pidlItem;
    LPCITEMIDLIST pidlItem2;
    HRESULT hr;
    IShellFolder* pFolder;
    IContextMenu* pContextMenu;
    CMINVOKECOMMANDINFO cmi;

    hr = SHGetDesktopFolder(&pFolder);
    if (FAILED(hr)) return 0;

    hr = pFolder->ParseDisplayName(HWND_DESKTOP, NULL, pszFile, NULL, &pidl, NULL);
    hr = pFolder->ParseDisplayName(HWND_DESKTOP, NULL, pszFile2, NULL, &pidl2, NULL);
    pFolder->Release();
    if (FAILED(hr)) return 0;

    hr = SHBindToParent(pidl, IID_IShellFolder, (void **)&pFolder, &pidlItem);
    if (FAILED(hr)) {
        SHFree(pidl);
        return 0;
    }
    //pFolder->Release();
    hr = SHBindToParent(pidl2, IID_IShellFolder, (void **)&pFolder, &pidlItem2);
    if (FAILED(hr)) {
        SHFree(pidl2);
        return 0;
    }

    LPCITEMIDLIST list[] = {pidlItem, pidlItem2};
    hr = pFolder->GetUIObjectOf(HWND_DESKTOP, 2, (LPCITEMIDLIST *)list, IID_IContextMenu, NULL, (void **)&pContextMenu);
    pFolder->Release();
    if (SUCCEEDED(hr)) {
        ZeroMemory(&cmi, sizeof(cmi));
        cmi.cbSize = sizeof(cmi);
        cmi.lpVerb = "properties";
        cmi.nShow = SW_SHOWNORMAL;

        hr = pContextMenu->InvokeCommand(&cmi);
        MessageBox(0, _T("Dummy message box"), 0, 0);
        Sleep(10000); // Give the system time to show the dialog before exiting
        pContextMenu->Release();
    }

    SHFree(pidl);

    return 0;
}

我也试过 this,但它也根本不起作用。 我还尝试使用桌面 IShellFolder 作为我的 PIDL 的父级,但它也不起作用。

有什么想法吗?

IShellFolder::GetUIObjectOf() 仅适用于 single-level PIDL,这些 PIDL 与被查询的 IShellFolder 相关。这在 GetUIObjectOf() documentation:

中有明确说明

The address of an array of pointers to ITEMIDLIST structures, each of which uniquely identifies a file object or subfolder relative to the parent folder. Each item identifier list must contain exactly one SHITEMID structure followed by a terminating zero.

您正在将 2 个绝对 PIDL 转换为它们各自文件夹的相对 PIDL,但是您正在使用 Windows\System32 文件夹的 IShellFolder 来检索两个文件的 IContextMenu .这不适用于属于 Windows\ 文件夹的相关 PIDL,因为 Windows\System32 文件夹的 IShellFolder 只知道 Windows\System32 文件夹中的文件。

网上各种例子显示,涉及多个文件时,从桌面IShellFolder查询IContextMenu。该方法仅在文件位于同一父文件夹中时才有效(如 Raymond Chen's example 中所示)。当文件位于不同的文件夹中时,事情会变得更加复杂。

您的代码中也有一些内存泄漏。

正确处理这种情况的方法是使用SHMultiFileProperties()函数:

Displays a merged property sheet for a set of files. Property values common to all the files are shown while those that differ display the string (multiple values).

使用桌面的 IShellFolder 获取绝对 PIDL 的 IDataObject(这是允许 GetUIObjectOf() 违反“每个项目标识符列表必须包含恰好一个 SHITEMID 结构”规则),然后将其传递给 SHMultiFileProperties()。例如:

int main()
{
    CoInitialize(NULL);

    LPOLESTR pszFile = OLESTR("c:\Windows\notepad.exe");
    LPOLESTR pszFile2 = OLESTR("c:\Windows\System32\notepad.exe");
    LPITEMIDLIST pidl;
    LPITEMIDLIST pidl2;
    HRESULT hr;
    IShellFolder* pDesktop;
    IDataObject *pDataObject;

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

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

    hr = pDesktop->ParseDisplayName(HWND_DESKTOP, NULL, pszFile2, NULL, &pidl2, NULL);
    if (FAILED(hr)) {
        SHFree(pidl);
        pDesktop->Release();
        CoUninitialize();
        return 0;
    }

    LPCITEMIDLIST list[] = {pidl, pidl2};
    hr = pDesktop->GetUIObjectOf(HWND_DESKTOP, 2, (LPCITEMIDLIST *)list, IID_IDataObject, NULL, (void **)&pDataObject);
    // alternatively, you can also use SHCreateDataObject() or CIDLData_CreateFromIDArray() to create the IDataObject
    pDesktop->Release();
    SHFree(pidl);
    SHFree(pidl2);

    if (SUCCEEDED(hr)) {
        hr = SHMultiFileProperties(pDataObject, 0);
        pDataObject->Release();

        if (SUCCEEDED(hr)) {
            MessageBox(0, _T("Dummy message box"), 0, 0);
            Sleep(10000); // Give the system time to show the dialog before exiting
        }
    }

    CoUninitialize();
    return 0;
}