How to use SHCreateItemFromParsingName with names from wrapper namespace?

I use SHCreateItemFromParsingName

to turn the path into IShellItem

:

IShellItem ParseName(String path)
{
    IShellItem shellItem;

    HRESULT hr = SHCreateItemFromParsingName(path, null, IShellItem, out shellItem);
    if (Failed(hr)) 
        throw new ECOMException(hr);
    return shellItem;
}

      

Note : A IShellItem

was introduced around 2006 to provide a nice wrapper around IShellFolder

+ constructs pidl

under Windows 95. You can even ask to IShellItem

flip it under IShellFolder

and pidl

using the interface and method IParentAndItem.GetParentAndItem

.

Different things have different display names

I can get some known locations in the shell namespace and see their absolute parsing ( SIGDN_DESKTOPABSOLUTEPARSING

) and edit ( SIGDN_DESKTOPABSOLUTEEDITING

) the displayed names:

| Path              | Editing               | Parsing                                                           |
|-------------------|-----------------------|-------------------------------------------------------------------|
| C:\               | "C:\"                 | "C:\"                                                             |
| C:\Windows        | "C:\Windows"          | "C:\Windows"                                                      |
| Desktop           | "Desktop"             | "C:\Users\Ian\Desktop"                                            |
| Computer          | "This PC"             | "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}"                        |
| Recycle Bin       | "Recycle Bin"         | "::{645FF040-5081-101B-9F08-00AA002F954E}"                        |
| Documents Library | "Libraries\Documents" | "::{031E4825-7B94-4DC3-B131-E946B44C8DD5}\Documents.library-ms" " |
| Startup           | "C:\Users\Ian\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup" | "C:\Users\Ian\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup" |

      

How to parse them when the user enters them into them?

I can use IFileOpenDialog

to let the user select one of these folders. But I would really like the user to be able to print

  • "C: \ Users"
  • "C: \ Windows \ Fonts"
  • "This PC"
  • "Basket"
  • "Library"
  • "Launch"
  • "Fonts"

and you can analyze it on IShellItem

.

The problem is that some of the paths are not parsed SHCreateItemFromParsingName

:

  • SHCreateItemFromParsingName("C:\")

    : Parsi
  • SHCreateItemFromParsingName("C:\Windows")

    : Parsis
  • SHCreateItemFromParsingName("")

    : Parsis (but becomes " This PC ")
  • SHCreateItemFromParsingName("This PC")

    : Fails
  • SHCreateItemFromParsingName("Recycle Bin")

    : Fails
  • SHCreateItemFromParsingName("Libraries")

    : Fails
  • SHCreateItemFromParsingName("OneDrive")

    : Fails
  • SHCreateItemFromParsingName("Libraries\Documents")

    : Fails
  • SHCreateItemFromParsingName("Network")

    : Fails
  • SHCreateItemFromParsingName("Startup")

    : Fails

Meanwhile, the IFileOpenDialog control that my program uses is fine with them:

enter image description here

How can I parse the various special shell name fields that the user can enter (in that the Windows Explorer dialog and the IFileOpen dialog can parse) into IShellItem

for this folder?

The real question is that I want the user to be able to have a recent MRU list that contains things like:

  • C: \ Windows
  • Baskets
  • This PC

and you can analyze them later.

+3


source to share


2 answers


It would be interesting to debug Explorer and see how it does it.

My suggestion; if the original parsing failed, add shell:

to the path string and try expanding it again with SHParseDisplayName

. If you set STR_PARSE_SHELL_PROTOCOL_TO_FILE_OBJECTS

in the context of the bindings, you can also bind to special files. Shell protocol: allows parsing the internal / canonical name of special / known folders, but I don't know if it checks the display name as well.

Edit:

I had a chance to play around a bit now, and shell: prefix is ​​not a big improvement because it only checks for known canonical folder names:

PCWSTR paths[] = {
    TEXT("C:\\"),
    TEXT("C:\\Windows"),
    TEXT(""),
    TEXT("This PC"),
    TEXT("MyComputerFolder"), // Canonical KF name
    TEXT("Recycle Bin"),
    TEXT("RecycleBinFolder"), // Canonical KF name
    TEXT("Libraries"),
    TEXT("OneDrive"),
    TEXT("Libraries\\Documents"),
    TEXT("Network"),
    TEXT("NetworkPlacesFolder"), // Canonical KF name
    TEXT("Startup"),
};

OleInitialize(0);
INT pad = 0, fill, i;
for (i = 0; i < ARRAYSIZE(paths); ++i) pad = max(pad, lstrlen(paths[i]));
for (i = 1, fill = printf("%-*s | Original | shell:   |\n", pad, ""); i < fill; ++i) printf("-"); printf("\n");
for (i = 0; i < ARRAYSIZE(paths); ++i)
{
    WCHAR buf[MAX_PATH], *p1 = NULL, *p2 = NULL;
    IShellItem*pSI;
    HRESULT hr = SHCreateItemFromParsingName(paths[i], NULL, IID_IShellItem, (void**) &pSI);
    if (SUCCEEDED(hr)) pSI->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &p1), pSI->Release();
    wsprintf(buf, L"shell:%s", paths[i]);
    HRESULT hr2 = SHCreateItemFromParsingName(buf, NULL, IID_IShellItem, (void**) &pSI);
    if (SUCCEEDED(hr2)) pSI->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &p2), pSI->Release();
    wprintf(L"%-*s | %.8x | %.8x | %s\n", pad, paths[i], hr, hr2, p2 && *p2 ? p2 : p1 ? p1 : L"");
    CoTaskMemFree(p1), CoTaskMemFree(p2);
}

      



gives me this result:

                    | Original | shell:   |
-------------------------------------------
C:\                 | 00000000 | 80070003 | C:\
C:\Windows          | 00000000 | 80070003 | C:\Windows
                    | 00000000 | 80070003 | ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}
This PC             | 80070002 | 80070003 | 
MyComputerFolder    | 80070002 | 00000000 | ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}
Recycle Bin         | 80070002 | 80070003 | 
RecycleBinFolder    | 80070002 | 00000000 | ::{645FF040-5081-101B-9F08-00AA002F954E}
Libraries           | 80070002 | 00000000 | ::{031E4825-7B94-4DC3-B131-E946B44C8DD5}
OneDrive            | 80070002 | 80070003 | 
Libraries\Documents | 80070002 | 80070002 | 
Network             | 80070002 | 80070003 | 
NetworkPlacesFolder | 80070002 | 00000000 | ::{F02C1A0D-BE21-4350-88B0-7367FC96EF3C}
Startup             | 80070002 | 00000000 | C:\Users\Anders\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup

      

In Windows 8 SHCreateItemFromParsingName

names are SHParseDisplayName

(with STR_PARSE_AND_CREATE_ITEM

and STR_PARSE_TRANSLATE_ALIASES

), so even Microsoft is not able to separate parsing and display names in their API.

If you want to stay away from undocumented interfaces, you will need to add a third pass where you check the names of the known folder names. Or alternatively, as Raymond Chen suggests in the comments; parse each path component manually against the item mapping names in IShellFolder

.

+1


source


It would be interesting to debug Explorer and see how it does it.

start from Windows 7 shell use the following undocumented interface (until it changes from win 7 to last win 10)

MIDL_INTERFACE("88DF9332-6ADB-4604-8218-508673EF7F8A") IShellUrl : public IUnknown
{
    virtual HRESULT STDMETHODCALLTYPE ParseFromOutsideSource(PCWSTR,DWORD);
    virtual HRESULT STDMETHODCALLTYPE GetUrl(PWSTR,DWORD);
    virtual HRESULT STDMETHODCALLTYPE SetUrl(PCWSTR,DWORD);
    virtual HRESULT STDMETHODCALLTYPE GetDisplayName(PWSTR,DWORD);
    virtual HRESULT STDMETHODCALLTYPE GetPidl(ITEMIDLIST_ABSOLUTE * *);
    virtual HRESULT STDMETHODCALLTYPE SetPidl(ITEMIDLIST_ABSOLUTE const *);
    virtual HRESULT STDMETHODCALLTYPE SetPidlAndArgs(ITEMIDLIST_ABSOLUTE const *,PCWSTR);
    virtual PWSTR STDMETHODCALLTYPE GetArgs();
    virtual HRESULT STDMETHODCALLTYPE AddPath(ITEMIDLIST_ABSOLUTE const *);
    virtual void STDMETHODCALLTYPE SetCancelObject(ICancelMethodCalls *);
    virtual HRESULT STDMETHODCALLTYPE StartAsyncPathParse(HWND,PCWSTR,DWORD,ICancelMethodCalls *);
    virtual HRESULT STDMETHODCALLTYPE GetParseResult();
    virtual HRESULT STDMETHODCALLTYPE SetRequestID(int);
    virtual HRESULT STDMETHODCALLTYPE GetRequestID(int *);
    virtual HRESULT STDMETHODCALLTYPE SetNavFlags(int,int);
    virtual HRESULT STDMETHODCALLTYPE GetNavFlags(long *);
    virtual HRESULT STDMETHODCALLTYPE Execute(struct IShellNavigationTarget *,int *,DWORD);
    virtual HRESULT STDMETHODCALLTYPE SetCurrentWorkingDir(ITEMIDLIST_ABSOLUTE const *);
    virtual void STDMETHODCALLTYPE SetMessageBoxParent(HWND);
    virtual HRESULT STDMETHODCALLTYPE GetPidlNoGenerate(ITEMIDLIST_ABSOLUTE * *);
    virtual DWORD STDMETHODCALLTYPE GetStandardParsingFlags(BOOL);
};

class DECLSPEC_UUID("4BEC2015-BFA1-42FA-9C0C-59431BBE880E") ShellUrl;

      

we can use it to display display names like Recycle Bin

, This PC

etc. (use IFileOpenDialog dialog)

we can use it synchronously or asynchronously. for synchronous call needs



ParseFromOutsideSource(L"your name", flags = GetStandardParsingFlags(0))

if this call is ok, we can get and use ITEMIDLIST_ABSOLUTE*

on call GetPidl

(when you no longer need to free it on ILFree

), also if the file system path exists, we can get it GetUrl

otherwise the original name is returned.

it is also possible to use asynchronous parsing - you need to call StartAsyncPathParse

- pass your own hwnd and optional interface ICancelMethodCalls

. when the operation completes, message the shell RegisterWindowMessage(L"AC_ParseComplete")

( wParam == IShellUrl*, lParam == 0

) to your window. you can get the final status on call GetParseResult()

and if it's ok - useGetPidl

example code for synchronous parsing

HRESULT ParsePath(PCWSTR path, IShellItem **ppsi)
{
    IShellUrl* pShUrl;

    HRESULT hr = CoCreateInstance(__uuidof(ShellUrl), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pShUrl));

    if (hr == S_OK)
    {
        if (SUCCEEDED(hr = pShUrl->ParseFromOutsideSource(path, pShUrl->GetStandardParsingFlags(TRUE))))
        {
            ITEMIDLIST_ABSOLUTE *pidl;

            if (SUCCEEDED(hr = pShUrl->GetPidl(&pidl)))
            {
                hr = SHCreateItemFromIDList(pidl, IID_PPV_ARGS(ppsi));

                //WCHAR sz[MAX_PATH];
                //if (SUCCEEDED(pShUrl->GetUrl(sz, RTL_NUMBER_OF(sz)))) DbgPrint(">%S\n", sz);

                ILFree(pidl);
            }
        }

        pShUrl->Release();
    }

    return hr;
}

void tt(PCWSTR path)
{
    IShellItem *psi;

    if (0 <= ParsePath(path, &psi))
    {
        PWSTR szName;

        if (S_OK == psi->GetDisplayName(SIGDN_NORMALDISPLAY, &szName))
        {
            DbgPrint("NORMALDISPLAY>%S\n", szName);
            CoTaskMemFree(szName);
        }

        if (S_OK == psi->GetDisplayName(SIGDN_FILESYSPATH, &szName))
        {
            DbgPrint("FILESYSPATH>%S\n", szName);
            CoTaskMemFree(szName);
        }

        psi->Release();
    }
}

void tt()
{
    if (0 <= CoInitialize(0))
    {
        tt(L"Recycle Bin");
        tt(L"Startup");
        CoUninitialize();
    }
}

      

-1


source







All Articles