c++ 当右键单击文件夹的背景并使用shell扩展调用上下文菜单时,如何获得文件夹路径?

im9ewurl  于 2023-06-07  发布在  Shell
关注(0)|答案(1)|浏览(165)

如何获取用户在其背景中右键单击以调用上下文菜单的文件夹的路径?例如,用户打开“D:\projects”文件夹并在该文件夹的空白背景区域中右键单击,然后在上下文菜单中看到名为“显示路径”的菜单项。点击它后,它应该调用一个简单的控制台应用程序来显示字符串“D:\projects”。
可以通过注册表将“%V”作为参数添加到控制台应用程序的命令中,例如“C:\myfolder\myapp.exe”“%V”。因此,这个%V给出了myuapp.exe的main()参数列表的文件夹路径。简单哈!
如何使用shell扩展菜单处理程序来完成?我写了一个简单的shell上下文菜单dll,它工作得很好,除了我不知道如何将用户在后台右键单击的文件夹路径作为字符串。
我发现路径是IShellExtInit::Initialize()方法中的PCIDLIST_ABSOLUTE pidl参数。但是,我不能得到它在简单的字符串格式。下面的代码当然会崩溃。

HRESULT __stdcall Initialize(PCIDLIST_ABSOLUTE pidlFilder, IDataObject* pdtobj, HKEY hkeyProgID)
    {
        std::wstring s = L"null";
        
        // check msg, this msgbox is shown as expected
        MessageBox(NULL, L"Before", L"Initialize()", MB_OK);
                
                //have problem in this line, I guess
        SHGetPathFromIDList((LPCITEMIDLIST) pidlFilder, (PWSTR) &s);
        
        // check msg, sometimes this msgbox is also shown as expected
        MessageBox(NULL, L"After", L"Initialize()", MB_OK);
        
        // but this msgbox is never shown. I removed it but code still crashes
        MessageBox(NULL, std::wstring(s).c_str(), L"Initialize()", MB_OK);
        
        return S_OK;
    }

当我右键单击文件夹背景时,它崩溃,资源管理器重新启动。
有谁知道这个问题和它的解决方法吗?如何获取文件夹路径时,右键单击文件夹的背景调用上下文菜单使用 shell 扩展?
另外,如何获得文件/文件夹路径时,右键单击它调用上下文菜单使用 shell 扩展?
先谢谢你了
我也试过用这个代码,还是崩溃

IShellFolder *sf = NULL;
        STRRET pName = {};
        sf->GetDisplayNameOf(pidlFilder, SHGDN_FORPARSING, &pName);
        wchar_t *d = new wchar_t;
        lstrcpyW(d,L"nulld");
        size_t inst = MAX_PATH, outst ;
        mbstowcs_s(&outst, d, inst, pName.cStr, MAX_PATH);
        s = std::wstring(d);
                MessageBox(NULL, std::wstring(s).c_str(), L"Initialize()", MB_OK);
ngynwnxp

ngynwnxp1#

您试图让SHGetPathFromIDList()将字符串数据写入std::wstring对象所在的内存地址,这将无法工作。
使用固定的WCHAR[]数组代替,例如:

HRESULT __stdcall Initialize(PCIDLIST_ABSOLUTE pidlFilder, IDataObject* pdtobj, HKEY hkeyProgID)
{
    WCHAR szPath[MAX_PATH] = {};

    SHGetPathFromIDList(pidlFilder, szPath);

    MessageBox(NULL, szPath, L"Initialize()", MB_OK);
        
    return S_OK;
}

或者,如果你想将字符串数据接收到std::wstring对象中,那么你必须预先分配其内部字符缓冲区,然后接收到该缓冲区中,例如:

HRESULT __stdcall Initialize(PCIDLIST_ABSOLUTE pidlFilder, IDataObject* pdtobj, HKEY hkeyProgID)
{
    std::wstring s;
    s.resize(MAX_PATH);
        
    SHGetPathFromIDList(pidlFilder, s.data() /* or &s[0] before C++17 */ );
    s.erase(s.find(L'\0'));

    MessageBox(NULL, s.c_str(), L"Initialize()", MB_OK);
        
    return S_OK;
}

否则,您可以简单地接收到WCHAR[],然后将其分配给std::wstring,例如:

HRESULT __stdcall Initialize(PCIDLIST_ABSOLUTE pidlFilder, IDataObject* pdtobj, HKEY hkeyProgID)
{
    WCHAR szPath[MAX_PATH] = {};
    SHGetPathFromIDList(pidlFilder, szPath);
        
    std::wstring s = szPath;
    MessageBox(NULL, s.c_str(), L"Initialize()", MB_OK);
        
    return S_OK;
}

你的第二个例子不起作用有几个原因:

  • 你的IShellFolder *sf没有指向任何有意义的地方。使用SHGetDesktopFolder()获取顶级IShellFolder对象,然后可以使用该对象解析pidlFilder
  • 你只分配了1个wchar_twchar_t *d指向,但是你试图复制超过1个wchar_t到那个内存中。实际上根本不需要分配任何内存,因为解析后的STRRET已经包含了必要的字符串数据,所以只需按原样使用即可。否则,您可以将STRRET传递给StrRetToBuf()StrRetToStr()以获得更可用格式的数据。
  • 您没有注意STRRET::uType字段以了解它保存的是哪种字符串数据。除非uType字段设置为STRRET_CSTR,否则不要访问cStr字段。StrRetToBuf()/StrRetToStr()将为您处理此问题。

试试这个:

HRESULT __stdcall Initialize(PCIDLIST_ABSOLUTE pidlFilder, IDataObject* pdtobj, HKEY hkeyProgID)
{
    IShellFolder *sf = NULL;
    if (SUCCEEDED(SHGetDesktopFolder(&sf))
    {
        STRRET pName = {};
        if (SUCCEEDED(sf->GetDisplayNameOf(pidlFilder, SHGDN_FORPARSING, &pName))
        {
            WCHAR szPath[MAX_PATH] = {};
            StrRetToBufW(&pName, pidlFilder, szPath, MAX_PATH);

            MessageBox(NULL, szPath, L"Initialize()", MB_OK);
        }

        sf->Release();
    }

    return S_OK;
}

相关问题