将 SHBrowseForFolder 和 struct BROWSEINFO 的使用更改为 - IFileOpenDialog

Posted

技术标签:

【中文标题】将 SHBrowseForFolder 和 struct BROWSEINFO 的使用更改为 - IFileOpenDialog【英文标题】:change the use of SHBrowseForFolder and struct BROWSEINFO TO - IFileOpenDialog 【发布时间】:2020-08-31 17:08:13 【问题描述】:

直到现在——为了打开一个对话框,我使用了SHBrowseForFolder 函数。它会导致一些错误,我被告知使用IFileOpenDialog。 这是我需要替换的代码:

bool wvFM::SelectFileSystemObjectDialogTree(const WCDialogCreationOptions& in_options,
                                            WCDialogReply& out_Reply)

    AUTO_FUNC_DEBUG;

    DWORD osErr = NO_ERROR;
    WTErr wtErr = eNoErr;

    out_Reply.accept = false;

    if(in_options.m_flags[WCDialogCreationOptions::eSelectFolder]) 
        BROWSEINFO bi = 0;
        bi.hwndOwner = (HWND)in_options.m_owner;
        bi.pidlRoot = NULL; // TBD by callback BrowseCallbackProc
        bi.lpszTitle = in_options.m_windowTitle.c_str();
        bi.ulFlags = BIF_USENEWUI;    // to enable pasting path
        bi.lpfn = BrowseCallbackProc; // for initial dir option
        WTPathString initialLocationPathString(
            in_options.m_InitialDir.GetNativePathString());
        bi.lParam = in_options.m_InitialDir.IsValid()
                        ? (LPARAM)initialLocationPathString.c_str()
                        : NULL;

        LPITEMIDLIST pidl = SHBrowseForFolder(&bi);

        // returns focus to the internal window of the plug
        ::SetFocus((HWND)in_options.m_owner);

        if(pidl != NULL) 
            TCHAR szPath[MAX_PATH];
            SHGetPathFromIDList(pidl, szPath);
            out_Reply.m_filePathRef = wvFM::WCStPath((char*)szPath);
            out_Reply.accept = true;
            IMalloc* imalloc = 0;
            if(SUCCEEDED(SHGetMalloc(&imalloc))) 
                imalloc->Free(pidl);
                imalloc->Release();
            
        
        return true;
    

我不太确定如何进行这种转换。 感谢任何愿意提供帮助的人!

【问题讨论】:

您似乎缺少函数的结尾。左大括号比右大括号多一个。 @danieloren 在尝试将此代码从 SHBrowseForFolder() 转换为 IFileOpenDialog 时,您到底遇到了什么问题?到目前为止,您尝试过哪些不适合您的方法? @TedLyngmo 因为有问题的代码侧重于设置了 eSelectFolder 标志的情况,因此您所指的“缺失”代码包含更多代码来处理以下情况eSelectFolder 未设置。 @RemyLebeau 可能是。原来的缩进让它看起来好像真的不见了。 @danieloren "[SHBrowseForFolder] 导致一些错误" - 究竟是什么错误? 【参考方案1】:

您显然正在使用具有某种自定义字符串类型的 3rd 方库,但您没有提供有关该字符串类型实际是什么的任何详细信息。我猜测字符串类型是基于char 的,因为您在将SHGetPathFromIDList() 的结果分配给out_Reply.m_filePathRef 时将其类型转换为char*,所以您很可能使用UNICODE undefined 编译您的项目,以便TCHAR 映射到char 而不是wchar_t。在这种情况下,SHBrowseForFolder() 将调用SHBrowseForFolderA() 而不是SHBrowseForFolderW()

IFileOpenDialog 根本不处理 ANSI 字符串,只处理 UNICODE 字符串,因此您必须根据需要在 3rd 方字符串类型和 Windows UTF-16 字符串之间来回转换。

SHBrowseForFolder()IFileOpenDialog 的转换大致如下所示(其中 toWString() 中的 YourStringType 是第 3 方字符串类型 - 在指示的地方进行所需的任何调整) :

std::wstring toWString(const YourStringType &str)

    const char pStr = ... pointer to str's characters ...;
    int sLen = ... length of str in chars, not counting the null terminator ...;

    std::wstring ws;

    int wLen = MultiByteToWideChar(CP_ACP, 0, pStr, slen, NULL, 0);
    if (wLen > 0)
    
        ws.resize(wLen);
        MultiByteToWideChar(CP_ACP, 0, pStr, sLen, ws.data(), wLen);
    

    return ws;


std::string toString(const wchar_t *ws)

    std::string s;

    int wLen = lstrlenW(s);
    int sLen = WideCharToMultiByte(CP_ACP, 0, ws, wLen, NULL, 0, NULL, NULL);
    if (sLen > 0)
    
        s.resize(sLen);
        WideCharToMultiByte(CP_ACP, 0, ws, wLen, s.data(), sLen, NULL, NULL);
    

    return s;


IShellItem* toShellItem(const YourStringType &path)

    IShellItem *pItem = NULL;
    if (FAILED(SHCreateItemFromParsingName(toWString(path).c_str(), NULL, IID_PPV_ARGS(&pItem))))
        pItem = NULL;
    return pItem;


bool wvFM::SelectFileSystemObjectDialogTree(const WCDialogCreationOptions& in_options,
                                            WCDialogReply& out_Reply)

    AUTO_FUNC_DEBUG;

    out_Reply.accept = false;

    if (in_options.m_flags[WCDialogCreationOptions::eSelectFolder]) 

        IFileOpenDialog *pFileOpen = NULL;
        if (FAILED(CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pFileOpen))))
            return false;

        FILEOPENDIALOGOPTIONS opts = 0;
        pFileOpen->GetOptions(&opts);
        
        pFileOpen->SetTitle(toWString(in_options.m_windowTitle).c_str());
        pFileOpen->SetOptions(opts | FOS_PICKFOLDERS);

        IShellItem *pItem = toShellItem(in_options.m_InitialDir);
        if (pItem)
        
            pFileOpen->SetFolder(pItem);
            pItem->Release();
        

        HWND hwndOwner = (HWND) in_options.m_owner;

        HRESULT hr = pFileOpen->Show(hwndOwner);
        ::SetFocus(hwndOwner);

        if (SUCCEEDED(hr))
        
            if (SUCCEEDED(pFileOpen->GetResult(&pItem)))
            
                PWSTR pszFilePath;
                if (SUCCEEDED(pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath)))
                
                    out_Reply.m_filePathRef = wvFM::WCStPath(toString(pszFilePath).c_str());
                    out_Reply.accept = true;
                    CoTaskMemFree(pszFilePath);
                
                pItem->Release();
            
        

        pFileOpen->Release();
        return true;
    

    ...

【讨论】:

以上是关于将 SHBrowseForFolder 和 struct BROWSEINFO 的使用更改为 - IFileOpenDialog的主要内容,如果未能解决你的问题,请参考以下文章

CFolderPickerDialog - 没有 MFC

如何使用 .str 和 .split 将 pandas 代码转换为 Pyspark

R语言使用str_split函数和str_split_fixed函数将字符串分割(分裂split)成几个部分:str_split函数使用指定的字符或者字符串分割字符串str_split_fixed

python 中将str类型转化为int

表中的一列值有字符和数字,如何将两者通过查询筛选出来

repr和str的区别