Невозможно получить путь к виртуальной папке в Windows 7 C++(связано расширение пространства имен оболочки)

Используя Microsoft Windows SDK 7.0, explorerDataProvider, я установил виртуальную папку на Windows 7.

образ

Когда я открываю диалог просмотра файлов из приложения,

CFileDialog dlg(TRUE, NULL, 0, OFN_ENABLESIZING | OFN_ALLOWMULTISELECT, L"all(*.*)|*.*||", this);

INT_PTR result = dlg.DoModal();

if (result == IDOK)
{
   .
   . 
   .
   //some actions
}

он также может отображать виртуальную папку:

образ

Но когда я выбираю файл, например "Ноль", то нажимает "Открыть",

образ

Я пытался добавить точки останова в

INT_PTR result = dlg.DoModal();

Но похоже, что эта ошибка происходит внутри DoModal(),

Я провожу некоторые исследования и пересматриваю код виртуальной папки:

HRESULT CFolderViewImplFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, ULONG *rgfInOut)
{
// If SFGAO_FILESYSTEM is returned, GetDisplayNameOf(SHGDN_FORPARSING) on that item MUST
// return a filesystem path.
HRESULT hr = E_INVALIDARG;

DWORD dwAttribs = 0;
dwAttribs |= SFGAO_BROWSABLE;
if (1 == cidl)
{
    int nLevel = 0;
    hr = _GetLevel(apidl[0], &nLevel);
    if (SUCCEEDED(hr))
    {
        BOOL fIsFolder = FALSE;
        hr = _GetFolderness(apidl[0], &fIsFolder);
        if (SUCCEEDED(hr))
        {

            if (fIsFolder)
            {
                dwAttribs |= SFGAO_FOLDER;

                dwAttribs |= SFGAO_FILESYSANCESTOR;
            }
            else
            {
                dwAttribs |= SFGAO_SYSTEM;

                dwAttribs |= SFGAO_FILESYSTEM;
            }

            if (nLevel < g_nMaxLevel)
            {
                dwAttribs |= SFGAO_HASSUBFOLDER;
            }
        }
    }
}

*rgfInOut &= dwAttribs;

return hr;
}

И вернуть путь вGetDisplayNameOf()

HRESULT CFolderViewImplFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, SHGDNF shgdnFlags, STRRET *pName)
{
HRESULT hr = S_OK;
if (shgdnFlags & SHGDN_FORPARSING)
{
    WCHAR szDisplayName[MAX_PATH];
    if (shgdnFlags & SHGDN_INFOLDER)
    {
        // This form of the display name needs to be handled by ParseDisplayName.
        hr = _GetName(pidl, szDisplayName, ARRAYSIZE(szDisplayName));
    }
    else
    {
        PWSTR pszThisFolder = L"Computer\\Jerry";

            StringCchCopy(szDisplayName, ARRAYSIZE(szDisplayName), pszThisFolder);
            StringCchCat(szDisplayName, ARRAYSIZE(szDisplayName), L"\\");

            WCHAR szName[MAX_PATH];
            hr = _GetName(pidl, szName, ARRAYSIZE(szName));
            if (SUCCEEDED(hr))
            {
                StringCchCat(szDisplayName, ARRAYSIZE(szDisplayName), szName);
            }
    }
    if (SUCCEEDED(hr))
    {
        hr = StringToStrRet(szDisplayName, pName);
    }
}
else
{
    PWSTR pszName;
    hr = _GetName(pidl, &pszName);
    if (SUCCEEDED(hr))
    {
        hr = StringToStrRet(pszName, pName);
        CoTaskMemFree(pszName);
    }
}
return hr;
}

Но это все-таки статистика ошибок сообщения "Путь не существует" в приложении. Я добавляю точки останова в GetDisplayNameOf, но он никогда не запускается, когда приложение открывает диалог просмотра файлов. Но если я открою обычную папку, просто дважды щелкнув "Компьютер", она будет запущена.

Сообщение об ошибке "Путь не существует", по-видимому, не может быть признано устаревшим или скрыто. Любой способ, которым я могу пересмотреть виртуальную папку, чтобы вернуть правильный путь?


Обновление: я пытался IFileDialogЯ скопировал код из примера MSDN, добавил код и добавил точки останова, чтобы посмотреть, что произойдет. Тем не мение,

// Show the dialog
hr = pfd->Show(NULL);//the breakpoint is added here
if (SUCCEEDED(hr))
{
  // Obtain the result once the user clicks 
 // the 'Open' button.
 // The result is an IShellItem object.
 IShellItem *psiResult;
 hr = pfd->GetResult(&psiResult);
 if (SUCCEEDED(hr))
 {
   //do something
 }
}

Когда я нажимаю кнопку "Открыть", появляется сообщение об ошибке "Путь не существует, проверьте путь и повторите попытку". И это никогда не выходит из pfd->Show(NULL) после того, как я нажму кнопку "Открыть".

Основываясь на комментариях в примере кода, он должен выходить из hr = pfd->Show(NULL); и добраться до следующей строки. но сообщение об ошибке происходит внутри hr = pfd->Show(NULL);,

Обновление:

в IFileDialog, Я старался

hr = pfd->GetOptions(&dwFlags);
            if (SUCCEEDED(hr))
            {

                hr = pfd->SetOptions(dwFlags | FOS_ALLNONSTORAGEITEMS);
                if (SUCCEEDED(hr))
                {
                    // Show the dialog
                    hr = pfd->Show(NULL);
                    if (SUCCEEDED(hr))
                    {
                        // Obtain the result once the user clicks 
                        // the 'Open' button.
                        // The result is an IShellItem object.
                        IShellItem *psiResult;
                        hr = pfd->GetResult(&psiResult);
                        if (SUCCEEDED(hr))
                        {
                            // We are just going to print out the 
                            // name of the file for sample sake.
                            PWSTR pszFilePath = NULL;
                            hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH,
                                &pszFilePath);
                            if (SUCCEEDED(hr))
                            {
                                TaskDialog(NULL,
                                    NULL,
                                    L"CommonFileDialogApp",
                                    pszFilePath,
                                    NULL,
                                    TDCBF_OK_BUTTON,
                                    TD_INFORMATION_ICON,
                                    NULL);
                                CoTaskMemFree(pszFilePath);
                            }
                            psiResult->Release();
                        }
                    }

                }

Я тоже пробовал

hr = pfd->SetOptions(dwFlags);//delete FOS_FORCEFILESYSTEM         

И попробовал

DWORD dwFlags = 0;
if (SUCCEEDED(hr))
{
    // overwrite options completely
    hr = pfd->SetOptions(dwFlags | FOS_ALLNONSTORAGEITEMS);

Я также попытался вернуть все имя в коде расширения пространства имен:

WCHAR szDisplayName[MAX_PATH];
    if (shgdnFlags & SHGDN_INFOLDER)
    {
        // This form of the display name needs to be handled by ParseDisplayName.
        hr = _GetName(pidl, szDisplayName, ARRAYSIZE(szDisplayName));
    }
    else
    {
        PWSTR pszThisFolder;// = L"Computer\\Jerry";
        hr = SHGetNameFromIDList(m_pidl, (shgdnFlags & SHGDN_FORADDRESSBAR) ? SIGDN_DESKTOPABSOLUTEEDITING : SIGDN_DESKTOPABSOLUTEPARSING, &pszThisFolder);
        if (SUCCEEDED(hr))
        {
            StringCchCopy(szDisplayName, ARRAYSIZE(szDisplayName), pszThisFolder);
            StringCchCat(szDisplayName, ARRAYSIZE(szDisplayName), L"\\");

            WCHAR szName[MAX_PATH];
            hr = _GetName(pidl, szName, ARRAYSIZE(szName));
            if (SUCCEEDED(hr))
            {
                StringCchCat(szDisplayName, ARRAYSIZE(szDisplayName), szName);
            }
            CoTaskMemFree(pszThisFolder);
        }
    }
    if (SUCCEEDED(hr))
    {
        hr = StringToStrRet(szDisplayName, pName);
    }

Но результат: введите описание изображения здесь

Обновление:

hr = pfd->SetOptions(dwFlags | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE);

Это работает для меня!

1 ответ

Решение

С помощью @RemyLebeau я провел несколько тестов в своем коде. Наконец, я нашел рабочее решение для меня: я скопировал код из MSDN для IFileDialog: https://msdn.microsoft.com/en-us/library/windows/desktop/bb776913.aspx а затем немного измените его:

HRESULT BasicFileOpen()
{
// CoCreate the File Open Dialog object.
IFileDialog *pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog,
    NULL,
    CLSCTX_INPROC_SERVER,
    IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
    // Create an event handling object, and hook it up to the dialog.
    IFileDialogEvents *pfde = NULL;
    hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
    if (SUCCEEDED(hr))
    {
        // Hook up the event handler.
        DWORD dwCookie;
        hr = pfd->Advise(pfde, &dwCookie);
        if (SUCCEEDED(hr))
        {
            // Set the options on the dialog.
            DWORD dwFlags = 0;
            if (SUCCEEDED(hr))
            {
                // the default flag is FOS_PATHMUSTEXIST and FOS_FILEMUSTEXIST, so I set overwrite it with these two flags.
                hr = pfd->SetOptions(dwFlags | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE);
                if (SUCCEEDED(hr))
                {
                    // Show the dialog
                    hr = pfd->Show(NULL);
                    if (SUCCEEDED(hr))
                    {
                        // Obtain the result once the user clicks 
                        // the 'Open' button.
                        // The result is an IShellItem object.
                        IShellItem *psiResult;
                        hr = pfd->GetResult(&psiResult);
                        if (SUCCEEDED(hr))
                        {
                            // We are just going to print out the 
                            // name of the file for sample sake.
                            PWSTR pszFilePath = NULL;
                            hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH,
                                &pszFilePath);
                            if (SUCCEEDED(hr))
                            {
                                TaskDialog(NULL,
                                    NULL,
                                    L"CommonFileDialogApp",
                                    pszFilePath,
                                    NULL,
                                    TDCBF_OK_BUTTON,
                                    TD_INFORMATION_ICON,
                                    NULL);
                                CoTaskMemFree(pszFilePath);
                            }
                            psiResult->Release();
                        }
                    }

                }
            }
            // Unhook the event handler.
            pfd->Unadvise(dwCookie);
        }
        pfde->Release();
    }
    pfd->Release();
}
return hr;
}

Ключевым моментом является перезапись опций SetOptions, Основано на MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/dn457282(v=vs.85).aspx,

FOS_PATHMUSTEXIST
The item returned must be in an existing folder. This is a default value.
FOS_FILEMUSTEXIST
The item returned must exist. This is a default value for the Open dialog.

Эти флаги являются значениями по умолчанию, которые предотвращают его возврат. Поэтому я установил его на:

hr = pfd->SetOptions(dwFlags | FOS_ALLNONSTORAGEITEMS | FOS_NOVALIDATE);

Эти флаги означают:

FOS_NOVALIDATE
Do not check for situations that would prevent an application from opening the selected file, such as sharing violations or access denied errors.

а также

FOS_ALLNONSTORAGEITEMS
Enables the user to choose any item in the Shell namespace, not just those with SFGAO_STREAM or SFAGO_FILESYSTEM attributes. This flag cannot be combined with FOS_FORCEFILESYSTEM.

Тогда это наконец работает!

Другие вопросы по тегам