How to use SHCreateItemFromParsingName with names from the shell namespace? How to use SHCreateItemFromParsingName with names from the shell namespace? windows windows

How to use SHCreateItemFromParsingName with names from the shell namespace?


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

My suggestion is; if the initial parse fails, prepend shell: to the path string and try parsing it again with SHParseDisplayName. If you set STR_PARSE_SHELL_PROTOCOL_TO_FILE_OBJECTS in the bind context you can also bind to special files. The shell: protocol is able to parse the internal/canonical name of special/known folders but I don't know if it also checks the display name.

Edit:

I had a chance to play around a bit now and the shell: prefix is not a huge improvement because it only checks the known folder canonical 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 output:

                    | 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

On Windows 8 SHCreateItemFromParsingName calls SHParseDisplayName (with STR_PARSE_AND_CREATE_ITEM and STR_PARSE_TRANSLATE_ALIASES) so even Microsoft have trouble separating parsing and display names in their API.

If you want to stay away from undocumented interfaces then you would have to add a third pass where you check the known folder display names. Or alternatively as Raymond Chen suggests in the comments; parse every path component manually against item display names in that IShellFolder.


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

begin from windows 7 shell use next undocumented interface (until it unchanged from win 7 up to latest 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 for parse display names like Recycle Bin, This PC, etc.. (IFileOpenDialog dialog use it)

we can use it synchronous or asynchronous. for synchronous need call

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

if this call is ok, we can get and use ITEMIDLIST_ABSOLUTE* by call GetPidl (when no longer need free it by ILFree) also if file system path exist can get it by GetUrl otherwise original name returned.

also possible use asynchronous parsing - you need call StartAsyncPathParse - pass own hwnd and optional ICancelMethodCalls interface. when operation finished shell post RegisterWindowMessage(L"AC_ParseComplete") (wParam == IShellUrl*, lParam == 0) to your window. you can get final status by call GetParseResult() and if it ok - use GetPidl

code example 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();    }}


For Windows 10 IShellUrl GUID is different:("4F33718D-BAE1-4F9B-96F2-D2A16E683346")