C++ 显示不同目录中文件的“文件属性”对话框

C++ 显示不同目录中文件的“文件属性”对话框,c++,winapi,com,C++,Winapi,Com,我试图打开“默认文件属性”对话框,但无法为不同目录中的文件获取其工作属性。 我需要为不同目录(甚至驱动器)中的文件获取IContextMenu*接口指针。 我希望在“属性”对话框中看到“各种文件夹” 这是我天真的做法: int main() { CoInitialize(NULL); LPOLESTR pszFile = OLESTR("c:\\Windows\\notepad.exe"); LPOLESTR pszFile2 = OLESTR("c:\\Windows\

我试图打开“默认文件属性”对话框,但无法为不同目录中的文件获取其工作属性。 我需要为不同目录(甚至驱动器)中的文件获取
IContextMenu*
接口指针。 我希望在“属性”对话框中看到“各种文件夹”

这是我天真的做法:

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;
}
我也试过了,但也没用。 我还尝试使用桌面IShellFolder作为我的PIDL的父级,但它也不起作用

有什么想法吗


IShellFolder::GetUIObjectOf()
仅适用于与所查询的
IShellFolder
相关的单级PIDL。这在以下文件中明确说明:

指向ITEMIDLIST结构的指针数组的地址,每个指针唯一地标识相对于父文件夹的文件对象或子文件夹。每个项目标识符列表必须只包含一个SHITEMID结构,后跟一个终止零

您正在将两个绝对PIDL转换为各自文件夹的相对PIDL,但随后使用
Windows\System32
文件夹的
IShellFolder
检索两个文件的
IContextMenu
。这对于属于
Windows\
文件夹的相对PIDL不起作用,因为
Windows\System32
文件夹的
IShellFolder
只知道
Windows\System32
文件夹中的文件

各种在线示例显示了当涉及多个文件时,从桌面的
IShellFolder
查询
IContextMenu
。这种方法仅在文件位于同一父文件夹(如中所示)时有效。当文件位于不同的文件夹中时,事情会变得更加复杂

代码中也有一些内存泄漏

处理这种情况的正确方法是使用以下功能:

显示一组文件的合并属性页。将显示所有文件共有的特性值,而不同的特性值将显示字符串(多个值)

使用桌面的
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;
}

这是否意味着无法使用
IContextMenu
存档相同的行为?据我所知,文件管理器分别处理菜单项“属性”并执行
SHMultiFileProperties
,而不是用于所有其他菜单项的
IContextMenu::InvokeCommand
?但显示的菜单功能齐全,您可以复制、剪切、删除选定的文件,即使这些文件位于不同的目录中。使用
IContextMenu
可以实现,但并不容易。Raymond Chen说:“您必须编写自己的复合上下文菜单。查找文件就是这样做的……这并不容易。您必须获取UIObjectOf(IContextMenu)对于组中的每个对象,然后取所有菜单项的交集,并将其作为组合的上下文菜单。或者,如果存在多个不同文件类型的文件,则可以执行“查找文件”的操作并将其下注。”不过,我怀疑编写“复合上下文菜单”是否适用于调用“属性”对话框。在对实现相同谓词的文件调用非可视操作时,这会更有意义。您只需在收集的
IContextMenu
对象中循环,对每个对象调用所需的谓词。有趣的观察结果是,Total Commander的属性对话框对于不同驱动器上的文件无法正常工作(“常规”选项卡缺失)。这意味着TotalCmd不使用
SHMultiFileProperties
函数。当我使用
IContextMenu
编写测试代码,并使用
CSIDL\u驱动器
或类似于PIDL父级的东西编写测试代码时,我也遇到了同样的问题。您可以尝试使用(及其相关兄弟)为跨多个文件夹的文件获取
IContextMenu
。有关此主题的讨论,请参阅。