Exercise 1: Developing the SLUtil

The Shell Library Utility (SLUtil) is a command line tool that enables you to manage libraries under the default library directory.

The SLUtil commands are:

SLUtil ? [CommandName]
SLUtil Create LibraryName
SLUtil AddFolder LibraryName FolderPath
SLUtil RemoveFolder LibraryName FolderPath
SLUtil Delete LibraryName
SLUtil Rename OldName NewName
SLUtil SaveFolder LibraryName [FolderPath]
SLUtil NavPanePinnedState LibraryName [TRUE|FALSE]
SLUtil Icon LibraryName [Icon]
SLUtil FolderType LibraryName [Documents|Pictures|Music|Videos|Generic]
SLUtil ListFolders LibraryName
SLUtil ManageUI LibraryName [Title] [Instruction]

Task 1 – Create the SLUtil Project

  1. Start Visual Studio 2008 and choose C++ Win32 Console Application.

    1. Enter SLUtil as the name of the new project.
    2. Keep the default settings.

  2. Add the CommandLineInterpreter.h and CommandLineInterpreter.cpp files. You can find them in your Starter folder. You will use the Command macro to add commands to the Command Line Interpreter.
  3. Replace the existing stdafx.h file with the stdafx.h file from the Starter folder to have the necessary #include files.
  4. Change the _WIN32_WINNT in targetver.h to 0x0601 in order to support Windows 7 API.
  5. To use the Command Line Interpreter, add #include "CommandLineInterpreter.h" to the SLUtil.cpp directly under #include "stdafx.h"
  6. Change the main method to call the Command Line Interpreter's Execute function. You also need to initialize COM:

    Note:
    The Command Line Interpreter is a utility class that knows how to interpret command line arguments and execute functions that have registered with the Command macro.

    #include "stdafx.h"
    #include "CommandLineInterpreter.h"

    int _tmain(int argc, _TCHAR* argv[])
    {
    //Initiate COM
    CoInitialize(NULL);
    CCommandLineInterpreter::Execute(argc, argv);
    CoUninitialize();
    return 0;
    }

  7. Compile the program.
  8. To test the current state of the application, add a command line argument in the Debug pane of the project properties.

  9. Execute the application. You should get the following output:

    Note:

    SLUtil ? [CommandName]

    Press any key to continue . . .

Task 2 – Add the Create Command

To program the Shell library we need to use the IShellLibrary interface as well as other Shell interfaces such as IShelItem, IShelItem2, and IShellItemArray. All of them are defined in the shobjidl.h header file. This header file also contains some Shell library helper functions that save some line of codes when doing common shell library operations such as creating the Shell Library COM object or loading the Shell Library. For example the following code is a snippet from the shobjidl.h file:

__inline HRESULT SHCreateLibrary(__in REFIID riid, __deref_out void **ppv)
{
return CoCreateInstance(CLSID_ShellLibrary, NULL, CLSCTX_INPROC_SERVER, riid, ppv);
}

  1. Add the following code to SLUtil.cpp file://Create a new shell library
    void CreateLibrary(const CCommand &command, const vector<PCWSTR> &arguments)
    {
    IShellLibrary *shellLibrary = NULL;
    IShellItem *savedTo = NULL;

    //Create the shell library COM object
    HRESULT hr = SHCreateLibrary(IID_PPV_ARGS(&shellLibrary));
    if (FAILED(hr))
    {
    wcerr << L"Create: Can't create Shell Library COM object." << endl;
    exit(4);
    }

    //Save the new library under the user's Libraries folder.
    //If a library with the same name is already exists, add a number to the //name to create unique name
    hr = shellLibrary->SaveInKnownFolder(FOLDERID_UsersLibraries, arguments[0], LSF_MAKEUNIQUENAME, &savedTo);
    if (FAILED(hr))
    {
    wcerr << L"Create: Can't create Shell Library." << endl;
    exit(5);
    }

    if (shellLibrary != NULL)
    shellLibrary->Release();

    if (savedTo != NULL)
    savedTo->Release();
    }
    COMMAND(L"Create", L"SLUtil Create LibraryName", L"Create a new library", L"SLUtil Create MyLib", 1, CreateLibrary);

  2. Compile and run.
  3. Change the command line argument to: Create MyLib.

  4. Run the application and check to verify that you created a new library.

Task 3 – Add the AddFolder and RemoveFolder Commands

To add or remove a folder from an existing library we need to re-open the library. Since many commands will need to reopen the library, we will create two helper functions. (Note: In this HOL, we refer only to the user's libraries location.)

  1. Add the following code to the SLUtil.cpp:// Get the library COM object and the full path of a library.
    // If shellLibrary is NULL, don't return it.
    // If libraryFullPath is NULL, don't return it.
    // Call relese on the shellLibrary object. Call CoTaskMemFree on the// libraryFullPath
    HRESULT GetLibraryFromLibrariesFolder(PCWSTR libraryName, IShellLibrary **shellLibrary, bool openRead = true, PWSTR *libraryFullPath = NULL)
    {
    //Create the real library file name
    wstring realLibraryName(libraryName);
    realLibraryName += L".library-ms";

    IShellItem2 *shellItem = NULL;

    //Get the shell item that represent the library
    HRESULT hr = SHCreateItemInKnownFolder(FOLDERID_UsersLibraries, KF_FLAG_DEFAULT_PATH|KF_FLAG_NO_ALIAS , realLibraryName.c_str(), IID_PPV_ARGS(&shellItem));
    if (FAILED(hr))
    {
    return hr;
    }

    //In case a file-system full path is needed
    //extract the information from the Shell Item ParsingPath property
    if (libraryFullPath != NULL)
    {
    hr = shellItem->GetString(PKEY_ParsingPath, libraryFullPath);
    }

    //Get the shellLibrary object from the shell item with a read/write //permitions
    if (shellLibrary != NULL)
    {
    hr = SHLoadLibraryFromItem(shellItem, openRead ? STGM_READ : STGM_READWRITE, IID_PPV_ARGS(shellLibrary));
    }

    if (shellItem != NULL)
    shellItem->Release();
    return hr;
    }
    //Open an existing library
    IShellLibrary *OpenLibrary(PCWSTR commandName, PCWSTR libraryName, bool openRead = true)
    {
    IShellLibrary *shellLibrary = NULL;

    HRESULT hr = GetLibraryFromLibrariesFolder(libraryName, &shellLibrary, openRead);
    if (FAILED(hr))
    {
    wcerr << commandName << L": Can't load library." << endl;
    exit(4);
    }
    return shellLibrary;
    }
  2. Now it's time to add the AddFolder and RemoveFolder commands. Add the following code to the SLUtil.cpp file://Add a folder to a library
    void AddFolder(const CCommand &command, const vector<PCWSTR> &arguments)
    {
    IShellLibrary *shellLibrary = OpenLibrary(L"AddFolder", arguments[0], false);

    HRESULT hr = SHAddFolderPathToLibrary(shellLibrary, arguments[1]);
    if (FAILED(hr))
    {
    wcerr << L"AddFolder: Can't add folder to the library." << endl;
    exit(6);
    }

    //Commit the library changes
    shellLibrary->Commit();

    if (shellLibrary != NULL)
    shellLibrary->Release();
    }
    COMMAND(L"AddFolder", L"SLUtil AddFolder LibraryName FolderPath", L"Add a folder to a library", L"SLUtil AddFolder Documents C:\\Docs", 2, AddFolder);

    //Remove a folder from a library
    void RemoveFolder(const CCommand &command, const vector<PCWSTR> &arguments)
    {
    IShellLibrary *shellLibrary = OpenLibrary(L"RemoveFolder", arguments[0], false);

    HRESULT hr = SHRemoveFolderPathFromLibrary(shellLibrary, arguments[1]);
    if (FAILED(hr))
    {
    wcerr << L"RemoveFolder: Can't remove folder from the library." << endl;
    exit(5);
    }

    //Commit the library changes
    shellLibrary->Commit();

    if (shellLibrary != NULL)
    shellLibrary->Release();
    }
    COMMAND(L"RemoveFolder", L"SLUtil RemoveFolder LibraryName FolderPath", L"Remove a folder from a library", L"SLUtil RemoveFolder Documents C:\\Docs", 2, RemoveFolder);
  3. Compile and test the result:
    1. Clear the command line argument.
    2. Open a Command Prompt window and change the directory (cd) to the location of the SLUtil.exe (…\debug\SLUtil.exe).
    3. Open the Libraries Shell Folder next to the Command Prompt Window, so you will be able to see the changes you make with the SLUtil tool.
    4. Try the SLUtil from command line:

      1. SLUtil ?
      2. SLUtil Create NewLibrary
      3. SLUtil AddFolder NewLibrary C:\Users
      4. SLUtil RemoveFolder NewLibrary C:\Users
      Note:

      Since the interpreter finds the best match command, you can use “SLUtil Cr Lib” to create a library and “SLUtil Add Lib C:\” to add a folder. Try it.

Task 4 – Add the Delete and Rename Commands

Deleting and Renaming a library is a matter of deleting or renaming the library corresponding to the *.library-ms file. To delete the file we can use the SHFileOperation() or the DeleteFile(). The first can also send the library to the recycle bin, but the latter is less complicated so we will use it. To rename the file, we can use file-system based APIs again, but we can also use the ability to make a copy of the library and then delete the original instance.

  1. Add the following code to the SLUtil.cpp file://Delete a library
    void Delete(const CCommand &command, const vector<PCWSTR> &arguments)
    {
    PWSTR libraryFullPath;
    HRESULT hr = GetLibraryFromLibrariesFolder(arguments[0], NULL, false, &libraryFullPath);

    if (FAILED(hr))
    {
    wcerr << L"Delete: Can't get library." << endl;
    exit(4);
    }
    //We use delete file with the library file-system based full path
    DeleteFile(libraryFullPath);
    CoTaskMemFree(libraryFullPath);
    }
    COMMAND(L"Delete", L"SLUtil Delete LibraryName", L"Delete a library", L"SLUtil Delete MyLib", 1, Delete);

    //Rename an existing library
    void Rename(const CCommand &command, const vector<PCWSTR> &arguments)
    {
    IShellLibrary *shellLibrary = OpenLibrary(L"Rename", arguments[0]);
    IShellItem *savedTo = NULL;

    //Save a new copy of the library under the user's Libraries folder with //the new name.
    HRESULT hr = shellLibrary->SaveInKnownFolder(FOLDERID_UsersLibraries, arguments[1], LSF_MAKEUNIQUENAME, &savedTo);
    if (FAILED(hr))
    {
    wcerr << L"Rename: Can't save library." << endl;
    exit(5);
    }

    if (shellLibrary != NULL)
    shellLibrary->Release();
    if (savedTo != NULL)
    savedTo->Release();

    //Create parameters to delete the old copy of the library
    vector<PCWSTR> deleteArguments;
    deleteArguments.push_back(arguments[0]);

    //Call the delete command
    Delete(command, deleteArguments);
    }COMMAND(L"Rename", L"SLUtil Rename OldName NewName", L"Rename a library", L"SLUtil Rename MyLib MyLibNewName", 2, Rename);
  2. Compile and Test:
    1. Open a Command Prompt window and change the directory (cd) to the location of the SLUtil.exe.
    2. Open the Libraries Shell Folder next to the Command Prompt Window, so you will be able to see the changes you make with the SLUtil tool.
    3. Test the SLUtil from command line: SLUtil.exe rename mynewlibrary2 mynewlibrary4.
    4. Delete the library using SLUtil.exe del MyLib.
    5. If you delete one of the pre-defined libraries by accident, you can restore it:

Task 5 – Add SaveFolder, NavPanePinnedState, and Icon Commands

These three commands share common behaviors. The user can use these commands to query the state of the library by supplying just the library name.

  1. Add the following code to the SLUtil.cpp file:

    1. SaveFolder -- Lets you query and set the current default save folder. Bear in mind that this value may be empty if the folder list of the library is empty. Since the library holds folder information, this information may be outdated. For example, the Default Save Location may refer to a folder that the user has renamed. To solve these cases, IShellLibrary exposes a method named ResolveFolder().
    2. NavPanePinnedState -- Lets you query and set whether the library name will appear in the Explorer navigation pane. This is done by setting the library Option Flags to LOF_PINNEDTONAVPANE, which is currently the only available option.
    3. Icon -- Lets you query or set the icon resource name; this property may be empty.

    //Set or get the library's save folder path
    void SaveFolder(const CCommand &command, const vector<PCWSTR> &arguments)
    {
    IShellLibrary *shellLibrary = NULL;
    IShellItem2 *shellItemSaveFolder = NULL;
    HRESULT hr = S_OK;
    //Show current default save folder
    if (arguments.size() == 1)
    {
    shellLibrary = OpenLibrary(L"SaveFolder", arguments[0]);
    hr = shellLibrary->GetDefaultSaveFolder(DSFT_DETECT, IID_PPV_ARGS(&shellItemSaveFolder));
    if (FAILED(hr))
    {
    wcerr << L"SaveFolder: Can't extract default save folder." << endl;
    exit(5);
    }

    IShellItem2 *shellItemResolvedSaveFolder = NULL;
    //Fix folder path changes
    hr = shellLibrary->ResolveFolder(shellItemSaveFolder, 5000, IID_PPV_ARGS(&shellItemResolvedSaveFolder));
    if (FAILED(hr))
    {
    wcerr << L"SaveFolder: Default save location Unavailable." << endl;
    exit(6);
    }

    PWSTR defaultSaveFolder;
    hr = shellItemResolvedSaveFolder->GetString(PKEY_ParsingPath, &defaultSaveFolder);
    wcout << L"Library " << arguments[0] << L" has folder " << defaultSaveFolder << L" as a default save location." << endl;

    shellItemResolvedSaveFolder->Release();
    CoTaskMemFree(defaultSaveFolder);
    }
    else //Set current default save folder
    {
    shellLibrary = OpenLibrary(L"SaveFolder", arguments[0], false);
    //Create shell item from folder path
    hr = SHCreateItemFromParsingName(arguments[1], 0, IID_PPV_ARGS(&shellItemSaveFolder));
    if (FAILED(hr))
    {
    wcerr << L"SaveFolder: Can't find folder: " << arguments[1] << endl;
    exit(6);
    }

    shellLibrary->SetDefaultSaveFolder(DSFT_DETECT, shellItemSaveFolder);
    if (FAILED(hr))
    {
    wcerr << L"SaveFolder: Can't set default save folder to" << arguments[1] << endl;
    exit(7);
    }

    //Commit the library changes
    shellLibrary->Commit();
    }

    if (shellItemSaveFolder != NULL)
    shellItemSaveFolder->Release();
    if (shellLibrary != NULL)
    shellLibrary->Release();
    }
    COMMAND(L"SaveFolder", L"SLUtil SaveFolder LibraryName [FolderPath]", L"Set or get the library's save folder path", L"SLUtil SaveFolder Documents C:\\Docs", 1, SaveFolder);

    //compare strings for equality for the length of the shorter string
    bool CompareStringShort(PCWSTR first, PCWSTR second)
    {
    int length = min(lstrlenW(first), lstrlenW(second));
    return CompareString(LOCALE_NEUTRAL, NORM_IGNORECASE, first, length, second, length) == CSTR_EQUAL;
    }

    //Set or get the library's pinned to navigation pane state
    void NavPanePinnedState(const CCommand &command, const vector<PCWSTR> &arguments)
    {
    IShellLibrary *shellLibrary = NULL;
    HRESULT hr = S_OK;
    //Show current pinned to navigation pane state
    if (arguments.size() == 1)
    {
    //Open library with read permissions
    IShellLibrary *shellLibrary = OpenLibrary(L"NavPanePinnedState", arguments[0]);

    LIBRARYOPTIONFLAGS optionFlags;
    hr = shellLibrary->GetOptions(&optionFlags);
    if (FAILED(hr))
    {
    wcerr << L"NavPanePinnedState: Can't get current pinned " << "to navigation pane state." << endl;
    exit(5);
    }

    wcout << L"Library " << arguments[0] << L" is" <<
    (((optionFlags & LOF_PINNEDTONAVPANE) != 0) L" " : L" not ") << L"pinned to naveigation pane." << endl;
    }
    else //Set the current pinned to navigation pane state
    {
    //Open library with read/write permissions
    IShellLibrary *shellLibrary = OpenLibrary(L"NavPanePinnedState", arguments[0], false);

    LIBRARYOPTIONFLAGS optionFlags = CompareStringShort( arguments[1], L"TRUE") ? LOF_PINNEDTONAVPANE : LOF_DEFAULT;

    hr = shellLibrary->SetOptions(LOF_MASK_ALL, optionFlags);
    if (FAILED(hr))
    {
    wcerr << L"NavPanePinnedState: Can't set pinned to " << "navigation pane state." << endl;
    exit(6);
    }
    //Commit the library changes
    shellLibrary->Commit();
    }
    if (shellLibrary != NULL)
    shellLibrary->Release();
    }
    COMMAND(L"NavPanePinnedState", L"SLUtil NavPanePinnedState LibraryName [TRUE|FALSE]", L"Set or get the library's Pinned to navigation pane state", L"SLUtil NavPanePinnedState MyLib TRUE", 1, NavPanePinnedState);


    //Set or get the library's icon
    void Icon(const CCommand &command, const vector<PCWSTR> &arguments)
    {
    IShellLibrary *shellLibrary = NULL;
    HRESULT hr = S_OK;
    //Show current icon resource name
    if (arguments.size() == 1)
    {
    //Open library with read permissions
    IShellLibrary *shellLibrary = OpenLibrary(L"Icon", arguments[0]);

    PWSTR iconName;
    hr = shellLibrary->GetIcon(&iconName);
    if (FAILED(hr))
    {
    wcerr << L"Icon: Can't get icon resource name." << endl;
    exit(5);
    }
    wcout << L"Library " << arguments[0] << L": Icon resource name: "
    << iconName << endl;

    CoTaskMemFree(iconName);
    }
    else //Set the current icon resource name
    {
    //Open library with read/write permissions
    IShellLibrary *shellLibrary = OpenLibrary(L"Icon", arguments[0], false);

    hr = shellLibrary->SetIcon(arguments[1]);
    if (FAILED(hr))
    {
    wcerr << L"Icon: Can't set icon resource name." << endl;
    exit(6);
    }
    //Commit the library changes
    shellLibrary->Commit();
    }
    if (shellLibrary != NULL)
    shellLibrary->Release();
    }
    COMMAND(L"Icon", L"SLUtil Icon LibraryName [Icon]", L"Set or get the library's icon", L"SLUtil Icon MyLib imageres.dll,-1005", 1, Icon);

  2. Compile and test:
    1. Open a Command Prompt window and change the directory (cd) to the location of the SLUtil.exe.
    2. Open the Libraries Shell Folder next to the Command Prompt Window, so you will be able to see the changes you make with the SLUtil tool.
    3. Test the SLUtil from the command line.
    4. Try to give the same icon of the Pictures library to your MyLib library.
    5. Try to pin and unpin the library to the Explorer pane.
    6. Change the default save location of a library, move this location to another place in the file-system, and test if the default save location is resolving this change.

Task 6 – Add the FolderType Command

Libraries have a folder template type. This type defines the look and feel of the library Shell Window, for example, Pictures has different template than Music. The IShellLibrarySetFolderType() method takes a GUID that represent the folder type id. The GetFolderType() method returns the current folder id. By definition, user-created libraries get the Generic folder type.

  1. To let the user use text instead of GUID, we are going to use a helper class. The CFolderTypeIdNameConverter class knows how to convert the Pictures, Music, Documents, Videos, and Generic to the corresponding FOLDERTYPEID_* and vice versa. It also knows to take only part of the name, for example, vid for Videos. This makes it easy to set a library folder type: SLUtil FolderType MyLib doc.
  2. Add the FolderTypeIdNameConverter.h and FolderTypeIdNameConverter.cpp files to the project. You can find them in the Starter folder.
  3. Add #include "FolderTypeIdNameConverter.h"
  4. Add the following code snippet to implement the FolderType command://Set or get the library's folder template
    void FolderType(const CCommand &command, const vector<PCWSTR> &arguments)
    {
    IShellLibrary *shellLibrary = NULL;
    HRESULT hr = S_OK;
    FOLDERTYPEID folderTypeId;

    CFolderTypeIdNameConverter converter;

    //Show current folder type
    if (arguments.size() == 1)
    {
    //Open library with read permissions
    IShellLibrary *shellLibrary = OpenLibrary(L"FolderType", arguments[0]);

    hr = shellLibrary->GetFolderType(&folderTypeId);
    if (FAILED(hr))
    {
    wcerr << L"FolderType: Can't get the library's folder template." << endl;
    exit(5);
    }
    wcout << L"Library " << arguments[0] << L": Folder template is: " << converter.GetFolderTypeIdName(folderTypeId) << endl;
    }
    else //Set the current folder type
    {
    //Open library with read/write permissions
    IShellLibrary *shellLibrary = OpenLibrary(L"FolderType", arguments[0], false);

    hr = converter.GetFolderTypeIdFromName(arguments[1], &folderTypeId);
    if (FAILED(hr))
    {
    wcerr << L"FolderType: Invalida folder template name." << endl;
    exit(6);
    }

    hr = shellLibrary->SetFolderType(folderTypeId);
    if (FAILED(hr))
    {
    wcerr <<
    L"FolderType: Can't set the library's folder template," << endl;
    exit(7);
    }
    //Commit the library changes
    shellLibrary->Commit();
    }
    if (shellLibrary != NULL)
    shellLibrary->Release();
    }
    COMMAND(L"FolderType", L"SLUtil FolderType LibraryName [Documents|Pictures|Music|Videos|Generic]", L"Set or get the library's folder template", L"SLUtil MyLib Documents", 1, FolderType);

  5. Compile and test:
    1. Open a Command Prompt window and change the directory (cd) to the location of the SLUtil.exe.
    2. Open the Libraries Shell Folder next to the Command Prompt Window, so you will be able to see the changes you make with the SLUtil tool.
    3. Test the SLUtil from the command line.
    4. Try to give the folder type of the Pictures library to your MyLib library.

Task 7 – Add the ListFolders Command

The library's GetFolders() method returns an array of folders in a COM object that implements the IShellItemArray. IShellItemArray has a GetCount() method that returns the length of the array and a GetItemAt() that returns a COM object that implements the IShellItem and represent a shell folder. The application can retrieve the folder path by using the ParsingPath property of the shell folder. It can read string-based properties via the GetString() method of the interface IShellItem2. This is why we need to convert the IShellItem interface that we got from the IShellItemArray to an IShellItem2 interface. Another complication that we have to take into account is that the folder may be renamed, moved, or removed. We will use the ResolveFolder() method to fix these issues.

  1. Add the following code snippet://List all library folders
    void ListFolders(const CCommand &command, const vector<PCWSTR> &arguments)
    {
    IShellLibrary *shellLibrary = OpenLibrary(L"ListFolders", arguments[0]);
    HRESULT hr = S_OK;
    IShellItemArray *shellItemArray = NULL;

    shellLibrary->GetFolders(LFF_ALLITEMS, IID_PPV_ARGS(&shellItemArray));

    if (FAILED(hr))
    {
    wcerr << L"ListFolders: Can't get the library's folder list." << endl;
    exit(5);
    }

    wcout << L"Folder list of " << arguments[0] << L" library:" << endl;
    DWORD count;
    shellItemArray->GetCount(&count);

    //Iterate through all library folders
    for (DWORD i = 0; i < count; ++i)
    {
    IShellItem *shellItem;
    IShellItem2 *shellItem2;

    hr = shellItemArray->GetItemAt(i, &shellItem);
    if (FAILED(hr))
    continue;
    //Convert IShellItem to IShellItem2
    shellItem->QueryInterface(IID_PPV_ARGS(&shellItem2));
    shellItem->Release();

    IShellItem2 *shellItemResolvedFolder = NULL;
    //Fix folder path changes
    hr = shellLibrary->ResolveFolder(shellItem2, 5000, IID_PPV_ARGS(&shellItemResolvedFolder));
    if (SUCCEEDED(hr))
    {
    //Point to the fixed folder
    shellItem2->Release();
    shellItem2 = shellItemResolvedFolder;
    }
    //else we will show the unfixed folder

    PWSTR folderPath;
    hr = shellItem2->GetString(PKEY_ParsingPath, &folderPath);
    if (SUCCEEDED(hr))
    {
    wcout << folderPath << endl;
    }
    CoTaskMemFree(folderPath);
    shellItem2->Release();
    }
    shellItemArray->Release();
    shellLibrary->Release();
    }
    COMMAND(L"ListFolders", L"SLUtil ListFolders LibraryName", L"List all library folders.", L"SLUtil ListFolders Documents", 1, ListFolders);

  2. Compile and test:
    1. Open a Command Prompt window and change the directory (cd) to the location of the SLUtil.exe.
    2. Open the Libraries Shell Folder next to the Command Prompt Window, so you will be able to see the changes you make with the SLUtil tool.
    3. Test the SLUtil from the command line.
    4. List the content of a library.
    5. Try to rename, move or remove a folder and see how the library fixes the problem.

Task 8 – Add the ManageUI Command

The final command that complete the SLUtil is the ManageUI command. This command displays the library’s shell management window:

  1. The SHShowManageLibraryUI() API gets a shell item that represents a shell library and shows a library management dialog. You provide the handle of the parent Window, the text for the title and for the instructions. You can also decide whether to allow adding of un-indexable network folders that may slow down the library operations.
  2. Add the following code://Show the Shell Library management UI
    void ManageUI(const CCommand &command, const vector<PCWSTR> &arguments)
    {
    PWSTR libraryFullPath = NULL;
    HRESULT hr = GetLibraryFromLibrariesFolder(arguments[0], NULL, false, &libraryFullPath);
    if (FAILED(hr))
    {
    wcerr << L"ManageUI: Can't get library." << endl;
    exit(4);
    }

    IShellItem *shellItem = NULL;
    hr = SHCreateItemFromParsingName(libraryFullPath, 0, IID_PPV_ARGS(&shellItem));
    if (FAILED(hr))
    {
    wcerr << L"ManageUI: Can't create COM object." << endl;
    exit(5);
    }

    PCWSTR title = arguments[0];
    PCWSTR instruction = L"Manage Library folders and settings";

    if (arguments.size() > 1)
    title = arguments[1];

    if (arguments.size() > 2)
    instruction = arguments[2];

    SHShowManageLibraryUI(shellItem, NULL, title, instruction, LMD_ALLOWUNINDEXABLENETWORKLOCATIONS);

    shellItem->Release();
    CoTaskMemFree(libraryFullPath);
    }
    COMMAND(L"ManageUI", L"SLUtil ManageUI LibraryName [Title] [Instruction]", L"Show the Shell Library management UI", L"SLUtil ManageUI Documents", 1, ManageUI);
  3. Compile and test:
    1. SLUtil ManageUI Pictures "Manage Picture Library" "Add or Remove folders from the Pictures Library"
  4. You can find the final version in the Final solution folder. Well done!