c++shortcutwindows-shellclsidshell-namespace-extension

How to create shortcut for virtual folder in C++ on windows 7?


The platform I am using is windows 7. I need to create shortcut for the virtual folder on windows 7. I use windows 7 SDK sample to create a virtual folder under Computer:

enter image description here

The sample project name is called ExplorerDataProvider, it defines the CLSID for the IShellFolder class:

// add classes supported by this module here
const CLASS_OBJECT_INIT c_rgClassObjectInit[] =
{
{ &CLSID_FolderViewImpl,            CFolderViewImplFolder_CreateInstance },
{ &CLSID_FolderViewImplContextMenu,CFolderViewImplContextMenu_CreateInstance },
};

The definition for CFolderViewImplFolder_CreateInstance is:

HRESULT CFolderViewImplFolder_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CFolderViewImplFolder* pFolderViewImplShellFolder = new (std::nothrow) CFolderViewImplFolder(0);
HRESULT hr = pFolderViewImplShellFolder ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
    hr = pFolderViewImplShellFolder->QueryInterface(riid, ppv);
    pFolderViewImplShellFolder->Release();
}
return hr;
}

And the CFolderViewImplFolder implement IShellFolder2 amd IPersistFolder2. I found a similar code which is used to create shortcut for printer here: https://www.codeproject.com/Articles/596642/Creating-a-shortcut-programmatically-in-Cplusplus and in https://msdn.microsoft.com/en-us/library/aa969393.aspx#Shellink_Item_Identifiers

Once you have the class identifier for IShellFolder, you can call the CoCreateInstance function to retrieve the address of the interface. Then you can call the interface to enumerate the objects in the folder and retrieve the address of the item identifier for the object that you are searching for. Finally, you can use the address in a call to the IShellLink::SetIDList member function to create a shortcut to the object.

I revised

hr = SHGetMalloc(&pMalloc);
hr = SHGetDesktopFolder( &pDesktopFolder );
hr = SHGetSpecialFolderLocation( NULL, CSIDL_PRINTERS, &netItemIdLst );
hr = pDesktopFolder->BindToObject( netItemIdLst, NULL, IID_IShellFolder, (void **)&pPrinterFolder );

to

// testFolder is the CLSID for the virtual folder implementation
hr = CoCreateInstance(testFolder, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder, (LPVOID*)&pVirtualFolder);

or

hr = CoCreateInstance(testFolder, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder2, (LPVOID*)&pVirtualFolder);

But the pVirtualFolder is still NULL, and it prints that "cannot find the corresponding interface".

Is there anything wrong with CoCreateInstance when I use it ? Or I shouldn't use this solution ? Any sample code for it ?


Solution

  • To create a shortcut, you can use the official documentation. Here is a sample code that creates a shortcut for a children of "This PC" (aka: ComputerFolder)

    int main()
    {
        CoInitialize(NULL);
        // I've used my Apple's iCloud as an example (name is in french)
        // it's a virtual folder, a shell namespace extension
        HRESULT hr = CreateComputerChildShortCut(L"Photos iCloud", L"c:\\temp\\my icloud");
        printf("hr:0x%08X\n", hr);
        CoUninitialize();
        return 0;
    }
    
    HRESULT CreateComputerChildShortCut(LPWSTR childDisplayName, LPWSTR path)
    {
        // get My Computer folder's ShellItem (there are other ways for this...)
        CComPtr<IShellItem> folder;
        HRESULT hr = SHCreateItemInKnownFolder(FOLDERID_ComputerFolder, 0, NULL, IID_PPV_ARGS(&folder));
        if (FAILED(hr)) return hr;
    
        // enumerate children
        CComPtr<IEnumShellItems> items;
        hr = folder->BindToHandler(NULL, BHID_EnumItems, IID_PPV_ARGS(&items));
        if (FAILED(hr)) return hr;
    
        for (CComPtr<IShellItem> item; items->Next(1, &item, NULL) == S_OK; item.Release())
        {
            // get parsing path (if's often useful)
            CComHeapPtr<wchar_t> parsingPath;
            item->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &parsingPath);
            wprintf(L"Path: %s\n", parsingPath);
    
            // get display name
            CComHeapPtr<wchar_t> displayName;
            item->GetDisplayName(SIGDN_NORMALDISPLAY, &displayName);
            wprintf(L" Name: %s\n", displayName);
    
            if (!lstrcmpi(childDisplayName, displayName))
            {
                // get pidl
                // it's the unambiguous way of referencing a shell thing
                CComHeapPtr<ITEMIDLIST> pidl;
                hr = SHGetIDListFromObject(item, &pidl);
                if (FAILED(hr)) return hr;
    
                // create an instance of the standard Shell's folder shortcut creator
                CComPtr<IShellLink> link;
                hr = link.CoCreateInstance(CLSID_FolderShortcut);
                if (FAILED(hr)) return hr;
    
                // just use the pidl
                hr = link->SetIDList(pidl);
                if (FAILED(hr)) return hr;
    
                CComPtr<IPersistFile> file;
                hr = link->QueryInterface(&file);
                if (FAILED(hr)) return hr;
    
                // save the shortcut (we could also change other IShellLink parameters)
                hr = file->Save(path, FALSE);
                if (FAILED(hr)) return hr;
                break;
            }
        }
        return S_OK;
    }
    

    Of course, if you know an absolute parsing path or an absolute pidl, you don't have to enumerate anything, this was just for demonstration purposes.