DWORD WINAPI CPowerRenameManager::s_regexWorkerThread()

in src/modules/powerrename/lib/PowerRenameManager.cpp [912:1149]


DWORD WINAPI CPowerRenameManager::s_regexWorkerThread(_In_ void* pv)
{
    try
    {
        winrt::check_hresult(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
        WorkerThreadData* pwtd = reinterpret_cast<WorkerThreadData*>(pv);
        if (pwtd)
        {
            PostMessage(pwtd->hwndManager, SRM_REGEX_STARTED, GetCurrentThreadId(), 0);

            // Wait to be told we can begin
            if (WaitForSingleObject(pwtd->startEvent, INFINITE) == WAIT_OBJECT_0)
            {
                CComPtr<IPowerRenameRegEx> spRenameRegEx;

                winrt::check_hresult(pwtd->spsrm->GetRenameRegEx(&spRenameRegEx));

                DWORD flags = 0;
                winrt::check_hresult(spRenameRegEx->GetFlags(&flags));

                PWSTR replaceTerm = nullptr;
                bool useFileTime = false;

                winrt::check_hresult(spRenameRegEx->GetReplaceTerm(&replaceTerm));

                if (isFileTimeUsed(replaceTerm))
                {
                    useFileTime = true;
                }

                UINT itemCount = 0;
                unsigned long itemEnumIndex = 1;
                winrt::check_hresult(pwtd->spsrm->GetItemCount(&itemCount));
                for (UINT u = 0; u < itemCount; u++)
                {
                    // Check if cancel event is signaled
                    if (WaitForSingleObject(pwtd->cancelEvent, 0) == WAIT_OBJECT_0)
                    {
                        // Canceled from manager
                        // Send the manager thread the canceled message
                        PostMessage(pwtd->hwndManager, SRM_REGEX_CANCELED, GetCurrentThreadId(), 0);
                        break;
                    }

                    CComPtr<IPowerRenameItem> spItem;
                    winrt::check_hresult(pwtd->spsrm->GetItemByIndex(u, &spItem));

                    int id = -1;
                    winrt::check_hresult(spItem->GetId(&id));

                    bool isFolder = false;
                    bool isSubFolderContent = false;
                    winrt::check_hresult(spItem->GetIsFolder(&isFolder));
                    winrt::check_hresult(spItem->GetIsSubFolderContent(&isSubFolderContent));
                    if ((isFolder && (flags & PowerRenameFlags::ExcludeFolders)) ||
                        (!isFolder && (flags & PowerRenameFlags::ExcludeFiles)) ||
                        (isSubFolderContent && (flags & PowerRenameFlags::ExcludeSubfolders)) ||
                        (isFolder && (flags & PowerRenameFlags::ExtensionOnly)))
                    {
                        // Exclude this item from renaming.  Ensure new name is cleared.
                        winrt::check_hresult(spItem->PutNewName(nullptr));

                        // Send the manager thread the item processed message
                        PostMessage(pwtd->hwndManager, SRM_REGEX_ITEM_UPDATED, GetCurrentThreadId(), id);

                        continue;
                    }

                    PWSTR originalName = nullptr;
                    winrt::check_hresult(spItem->GetOriginalName(&originalName));


                    PWSTR currentNewName = nullptr;
                    winrt::check_hresult(spItem->GetNewName(&currentNewName));

                    wchar_t sourceName[MAX_PATH] = { 0 };

                    if (isFolder)
                    {
                        StringCchCopy(sourceName, ARRAYSIZE(sourceName), originalName);
                    
                    }
                    else
                    {
                        if (flags & NameOnly)
                        {
                            StringCchCopy(sourceName, ARRAYSIZE(sourceName), fs::path(originalName).stem().c_str());
                        }
                        else if (flags & ExtensionOnly)
                        {
                            std::wstring extension = fs::path(originalName).extension().wstring();
                            if (!extension.empty() && extension.front() == '.')
                            {
                                extension = extension.erase(0, 1);
                            }
                            StringCchCopy(sourceName, ARRAYSIZE(sourceName), extension.c_str());
                        }
                        else
                        {
                            StringCchCopy(sourceName, ARRAYSIZE(sourceName), originalName);
                        }
                    }

                    SYSTEMTIME fileTime = { 0 };

                    if (useFileTime)
                    {
                        winrt::check_hresult(spItem->GetTime(&fileTime));
                        winrt::check_hresult(spRenameRegEx->PutFileTime(fileTime));
                    }

                    PWSTR newName = nullptr;

                    // Failure here means we didn't match anything or had nothing to match
                    // Call put_newName with null in that case to reset it
                    winrt::check_hresult(spRenameRegEx->Replace(sourceName, &newName));

                    if (useFileTime)
                    {
                        winrt::check_hresult(spRenameRegEx->ResetFileTime());
                    }

                    wchar_t resultName[MAX_PATH] = { 0 };

                    PWSTR newNameToUse = nullptr;

                    // newName == nullptr likely means we have an empty search string.  We should leave newNameToUse
                    // as nullptr so we clear the renamed column
                    // Except string transformation is selected.

                    if (newName == nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase || flags & Capitalized))
                    {
                        SHStrDup(sourceName, &newName);
                    }

                    if (newName != nullptr)
                    {
                        newNameToUse = resultName;

                        if (isFolder)
                        {
                            StringCchCopy(resultName, ARRAYSIZE(resultName), newName);
                        }
                        else
                        {
                            if (flags & NameOnly)
                            {
                                StringCchPrintf(resultName, ARRAYSIZE(resultName), L"%s%s", newName, fs::path(originalName).extension().c_str());
                            }
                            else if (flags & ExtensionOnly)
                            {
                                std::wstring extension = fs::path(originalName).extension().wstring();
                                if (!extension.empty())
                                {
                                    StringCchPrintf(resultName, ARRAYSIZE(resultName), L"%s.%s", fs::path(originalName).stem().c_str(), newName);
                                }
                                else
                                {
                                    StringCchCopy(resultName, ARRAYSIZE(resultName), originalName);
                                }
                            }
                            else
                            {
                                StringCchCopy(resultName, ARRAYSIZE(resultName), newName);
                            }
                        }
                    }

                    wchar_t trimmedName[MAX_PATH] = { 0 };
                    if (newNameToUse != nullptr)
                    {
                        winrt::check_hresult(GetTrimmedFileName(trimmedName, ARRAYSIZE(trimmedName), newNameToUse));
                        newNameToUse = trimmedName;
                    }

                    wchar_t transformedName[MAX_PATH] = { 0 };
                    if (newNameToUse != nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase || flags & Capitalized))
                    {
                        try
                        {
                            winrt::check_hresult(GetTransformedFileName(transformedName, ARRAYSIZE(transformedName), newNameToUse, flags, isFolder));
                        }
                        catch (...)
                        {
                        }
                        newNameToUse = transformedName;
                    }

                    // No change from originalName so set newName to
                    // null so we clear it from our UI as well.
                    if (lstrcmp(originalName, newNameToUse) == 0)
                    {
                        newNameToUse = nullptr;
                    }

                    wchar_t uniqueName[MAX_PATH] = { 0 };
                    if (newNameToUse != nullptr && (flags & EnumerateItems))
                    {
                        unsigned long countUsed = 0;
                        if (GetEnumeratedFileName(uniqueName, ARRAYSIZE(uniqueName), newNameToUse, nullptr, itemEnumIndex, &countUsed))
                        {
                            newNameToUse = uniqueName;
                        }
                        itemEnumIndex++;
                    }

                    winrt::check_hresult(spItem->PutNewName(newNameToUse));

                    // Was there a change?
                    if (lstrcmp(currentNewName, newNameToUse) != 0)
                    {
                        // Send the manager thread the item processed message
                        PostMessage(pwtd->hwndManager, SRM_REGEX_ITEM_UPDATED, GetCurrentThreadId(), id);
                    }
                    CoTaskMemFree(newName);
                    CoTaskMemFree(currentNewName);
                    CoTaskMemFree(originalName);
                }
                CoTaskMemFree(replaceTerm);
            }

            // Send the manager thread the completion message
            PostMessage(pwtd->hwndManager, SRM_REGEX_COMPLETE, GetCurrentThreadId(), 0);

            delete pwtd;
            
        }
        CoUninitialize();
    }
    catch (...)
    {
        // TODO: an exception can happen while typing the expression and the syntax is not correct yet,
        // we need to be more granular and raise an exception only when a real problem happened.
        // MessageBox(NULL, L"RegexWorkerThread failed to execute.\nPlease report the bug to https://aka.ms/powerToysReportBug", L"PowerRename Error", MB_OK);
    }

    return 0;
}