// main function - sets up a 'meta command' to contain each of the library commands, and executes it on the given arguments
int wmain(int argc, wchar_t *argv[])
{
    // initialize COM before doing anything else, since the IShellLibrary API depends on COM
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (SUCCEEDED(hr))
    {
        CMetaCommand::PFNCREATECOMMAND rgMainCmds[] =
        {
            CMetaCommand::Create<CCreateCommand>,
            CMetaCommand::Create<CInfoCommand>,
            CMetaCommand::Create<CEnumCommand>,
            CMetaCommand::Create<CSetAttributeCommand>,
            CMetaCommand::Create<CAddCommand>,
            CMetaCommand::Create<CRemoveCommand>,
            CMetaCommand::Create<CSetDefaultSaveFolderCommand>,
            CMetaCommand::Create<CResolveCommand>,
            CMetaCommand::Create<CResolveAllCommand>,
            CMetaCommand::Create<CManageCommand>,
        };

        {
            PCWSTR pszExeName = PathFindFileNameW(CONSUME_NEXT_ARG(argv, argc));
            CMetaCommand main(pszExeName, L"Displays and modifies the attributes of Shell Libraries.", rgMainCmds, ARRAYSIZE(rgMainCmds));
            main.Execute(const_cast<PCWSTR*>(argv), argc);
        }
        CoUninitialize();
    }

    return 0;
}
    // Processes a single argument which identifies the library to operate on; passes any remaining arguments to the derived class.
    HRESULT v_ProcessArguments(PCWSTR *ppszArgs, int cArgs)
    {
        PCWSTR pszLibPath = CONSUME_NEXT_ARG(ppszArgs, cArgs);
        HRESULT hr = pszLibPath ? S_OK : E_INVALIDARG;
        if (SUCCEEDED(hr))
        {
            if (_fCreate)
            {
                // When creating a new library, interpret the argument as the file system path to save the library to.
                WCHAR szAbsPath[MAX_PATH];
                hr = SHStrDupW(_wfullpath(szAbsPath, pszLibPath, ARRAYSIZE(szAbsPath)), &_pszSavePath);
            }
            else
            {
                // Check for the 'FOLDERID_' prefix, which indicates that the argument should be interpreted as a KNOWNFOLDERID.
                const WCHAR szPrefix[] = L"FOLDERID_";
                const UINT cchPrefix = ARRAYSIZE(szPrefix) - 1;
                if (StrCmpNCW(pszLibPath, szPrefix, cchPrefix) == 0)
                {
                    IKnownFolderManager *pkfm;
                    hr = CoCreateInstance(CLSID_KnownFolderManager, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pkfm));
                    if (SUCCEEDED(hr))
                    {
                        // KNOWNFOLDERIDs are GUIDs, but they have a corresponding canonical name which is a string.
                        // By convention, the canonical name is the same as the name of the KNOWNFOLDERID #define.
                        // That is, FOLDERID_DocumentsLibrary => "DocumentsLibrary".  So, skip the prefix and pass
                        // the remainder to GetFolderByName to retrieve the known folder.
                        IKnownFolder *pkf;
                        hr = pkfm->GetFolderByName(pszLibPath + cchPrefix, &pkf);
                        if (SUCCEEDED(hr))
                        {
                            hr = pkf->GetShellItem(KF_FLAG_INIT, IID_PPV_ARGS(&_psiLibrary));
                            pkf->Release();
                        }
                        pkfm->Release();
                    }
                }
                else
                {
                    // Default - interpret the argument as a file system path, and create a shell item for it.
                    WCHAR szAbsPath[MAX_PATH];
                    hr = SHCreateItemFromParsingName(_wfullpath(szAbsPath, pszLibPath, ARRAYSIZE(szAbsPath)), NULL, IID_PPV_ARGS(&_psiLibrary));
                }
            }
        }
        else
        {
            ParseError(L"Missing library path argument.\n");
        }

        if (SUCCEEDED(hr))
        {
            // Allow derived command to process any remaining arguments.
            hr = v_ProcessLibArguments(ppszArgs, cArgs);
        }
        return hr;
    }
 // Interpret the next argument as a path, and obtain the IShellItem for it to be consumed by the derived class.
 HRESULT v_ProcessLibArguments(PCWSTR *ppszArgs, int cArgs)
 {
     PCWSTR pszFolderPath = CONSUME_NEXT_ARG(ppszArgs, cArgs);
     HRESULT hr = pszFolderPath ? S_OK : E_INVALIDARG;
     if (SUCCEEDED(hr))
     {
         hr = SHStrDupW(pszFolderPath, &_pszFolderPath);
         if (SUCCEEDED(hr))
         {
             hr = SHCreateItemFromParsingName(pszFolderPath, NULL, IID_PPV_ARGS(&_psiFolder));
         }
     }
     else
     {
         ParseError(L"Missing folder path argument.\n");
     }
     // On success, pass any remaining arguments on to the derived class.
     return SUCCEEDED(hr) ? v_ProcessFolderArguments(ppszArgs, cArgs) : hr;
 }
int wmain(int argc,  wchar_t *argv[])
{
    // initialize COM before doing anything else, since the IShellLibrary API depends on COM
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (SUCCEEDED(hr))
    {
        CMetaCommand::PFNCREATECOMMAND rgMainCmds[] =
        {
            CMetaCommand::Create<CReindexMatchingFileTypeCommand>,
            CMetaCommand::Create<CReindexMatchingMimeTypeCommand>,
            CMetaCommand::Create<CReindexMatchingWhereClauseCommand>,
        };
        {
            PCWSTR pszExeName = PathFindFileNameW(CONSUME_NEXT_ARG(argv, argc));
            CMetaCommand main(pszExeName, L"Reindexes the files that match the requirements specified. \nreindex <command> -? will give you the usage for each command", rgMainCmds, ARRAYSIZE(rgMainCmds));
            main.Execute(const_cast<PCWSTR *>(argv), argc);
        }
        CoUninitialize();
    }

    return 0;
}
 // Processes the filetypes that we are reindexing.
 HRESULT v_ProcessArguments(PCWSTR* ppszArgs, int cArgs)
 {
     HRESULT hr = cArgs ? S_OK : E_FAIL;
     if (FAILED(hr))
     {
         ParseError(L"Invalid number of arguments.");
     }
     else
     {
         // We are going to create a copy of ppszArgs to make an array of filetypes.
         m_ppszFileTypes = (PWSTR*) CoTaskMemAlloc(sizeof(PWSTR*) * cArgs);
         hr = m_ppszFileTypes ? S_OK : E_FAIL;
         if (SUCCEEDED(hr))
         {
             m_cArgs = cArgs;
             for (int i = 0; (i < m_cArgs) && SUCCEEDED(hr); i++)
             {
                 PCWSTR pszArg = CONSUME_NEXT_ARG(ppszArgs, cArgs);
                 hr = pszArg[0] == L'.' ? S_OK : E_FAIL;
                 if (FAILED(hr))
                 {
                     ParseError(L"'%s' is not a valid filetype.", pszArg);
                 }
                 else
                 {
                     hr = SHStrDup(pszArg, &(m_ppszFileTypes[i]));
                 }
             }
             if (FAILED(hr))
             {
                 FreeAll();
             }
         }
     }
     return hr;
 }
int wmain(int argc, wchar_t* argv[])
{
    if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)))
    {
        PCWSTR pszAppName = CONSUME_NEXT_ARG(argv, argc);
        PCWSTR pszOp = CONSUME_NEXT_ARG(argv, argc);
        if (pszOp && ((pszOp[0] == L'-') || (pszOp[0] == L'/')))
        {
            /* skip - or / */
            pszOp++;
            if (!_wcsicmp(pszOp, L"?"))
            {
                Usage(pszAppName);
            }
            else if (!_wcsicmp(pszOp, L"get"))
            {
                PCWSTR pszPropertyName = CONSUME_NEXT_ARG(argv, argc);
                if (pszPropertyName)
                {
                    PCWSTR pszFileName = CONSUME_NEXT_ARG(argv, argc);
                    if (pszFileName)
                    {
                        GetPropertyValue(pszFileName, pszPropertyName);
                    }
                    else
                    {
                        wprintf(L"No file name specified.\n");
                    }
                }
                else
                {
                    wprintf(L"No property canonical name specified.\n");
                }
            }
            else if (!_wcsicmp(pszOp, L"enum"))
            {
                PCWSTR pszFileName = CONSUME_NEXT_ARG(argv, argc);
                if (pszFileName)
                {
                    EnumerateProperties(pszFileName);
                }
                else
                {
                    wprintf(L"No file name specified.\n");
                }
            }
            else if (!_wcsicmp(pszOp, L"set"))
            {
                PCWSTR pszPropertyName = CONSUME_NEXT_ARG(argv, argc);
                if (pszPropertyName)
                {
                    PCWSTR pszPropertyValue = CONSUME_NEXT_ARG(argv, argc);
                    if (pszPropertyValue)
                    {
                        PCWSTR pszFileName = CONSUME_NEXT_ARG(argv, argc);
                        if (pszFileName)
                        {
                            SetPropertyValue(pszFileName, pszPropertyName, pszPropertyValue);
                        }
                        else
                        {
                            wprintf(L"No file name specified.\n");
                        }
                    }
                    else
                    {
                        wprintf(L"No property value specified.\n");
                    }
                }
                else
                {
                    wprintf(L"No property canonical name specified.\n");
                }
            }
            else if (!_wcsicmp(pszOp, L"info"))
            {
                PCWSTR pszPropertyName = CONSUME_NEXT_ARG(argv, argc);
                if (pszPropertyName)
                {
                    GetPropertyDescription(pszPropertyName);
                }
                else
                {
                    wprintf(L"No property canonical name specified.\n");
                }
            }
            else
            {
                wprintf(L"Unrecognized operation specified: -%s\n", pszOp);
                Usage(pszAppName);
            }
        }
        else
        {
            wprintf(L"No operation specified.\n");
            Usage(pszAppName);
        }
        CoUninitialize();
    }
    return 0;
}