See the question and my original answer on StackOverflow

Here is some sample code (using ATL from Visual Studio for simplicity) that can extract files from an archive file (zip, 7z, rar, etc.) on recent versions of Windows 11.

However some remarks:

  1. It uses an undocumented interface ITransferSource2 (which IMHO should be documented). It should support IStream or IStorage or ITransferSource's MoveItem but it doesn't seem to work.
  2. It can be very slow on some formats, for example .7z and .rar. Actually, it should run more or less at the same speed as the Windows Shell UI itself (my experience shows that copy-pasting a .7z file takes very long time vs 7Zip itself). This reduces considerably the interest for this feature IMHO.
#include <windows.h>
#include <atlbase.h>
#include <shobjidl_core.h>
#include <shlguid.h>

// this is an undocumented interface
DECLARE_INTERFACE_IID_(ITransferSource2, ITransferSource, "D6B78E20-B98A-49FA-AE7E-2BA7FCE522F5")
{
public:
    virtual HRESULT WINAPI IsCopySupported(IShellItem* psiSource, IShellItem* psiDest, BOOL* fSupported) = 0;
    virtual HRESULT WINAPI CopyItem(IShellItem* psiSource, IShellItem* psiDest, PCWSTR pszNameDst, TRANSFER_SOURCE_FLAGS flags, int, IShellItem**) = 0;
    virtual HRESULT WINAPI LastCopyError(int*) = 0;
};

void ExtractItemToTarget(IShellItem* item, IShellItem* target, bool recursive)
{
    // enumerate item's children
    CComPtr<IEnumShellItems> items;
    ATLASSERT(SUCCEEDED(item->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&items))));

    do
    {
        CComPtr<IShellItem> child;
        items->Next(1, &child, 0);
        if (!child)
            break;

        // get relative name
        CComHeapPtr<wchar_t> name;
        ATLASSERT(SUCCEEDED(child->GetDisplayName(SIGDN_PARENTRELATIVEPARSING, &name)));

        // get full path for display
        CComHeapPtr<wchar_t> path;
        ATLASSERT(SUCCEEDED(child->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &path)));
        wprintf(L"Processing '%s'...\n", path.m_pData);

        // folder or item?
        SFGAOF folder = 0;
        if (SUCCEEDED(child->GetAttributes(SFGAO_FOLDER, &folder)) && folder)
        {
            if (recursive)
            {
                CComPtr<ITransferDestination> destination;
                ATLASSERT(SUCCEEDED(target->BindToHandler(nullptr, BHID_Transfer, IID_PPV_ARGS(&destination))));

                // ensure directory exists. we create a directory even for an empty folder
                CComPtr<IShellItem> childTarget;
                CComPtr<IShellItemResources> resources;
                ATLASSERT(SUCCEEDED(destination->CreateItem(name, FILE_ATTRIBUTE_DIRECTORY, 0, TSF_OVERWRITE_EXIST, IID_PPV_ARGS(&childTarget), IID_PPV_ARGS(&resources))));

                // go deep down
                ExtractItemToTarget(child, childTarget, recursive);
            }
        }
        else
        {
            // extract file (here we overwrite)
            CComPtr<ITransferSource> source;
            ATLASSERT(SUCCEEDED(item->BindToHandler(nullptr, BHID_Transfer, IID_PPV_ARGS(&source))));

            CComPtr<ITransferSource2> source2;
            ATLASSERT(SUCCEEDED(source.QueryInterface(&source2)));

            CComPtr<IShellItem> newItem;
            ATLASSERT(SUCCEEDED(source2->CopyItem(child, target, name, TSF_OVERWRITE_EXIST, 0, &newItem)));
        }
        child.Release();
    } while (true);
}

int main()
{
    ATLASSERT(SUCCEEDED(CoInitialize(nullptr)));
    {
        // should support zip 7z (slow!) gz bz2 tar rar (slow!) tgz tbz2 tzst txz zst xz
        CComPtr<IShellItem> file;
        ATLASSERT(SUCCEEDED(SHCreateItemFromParsingName(L"c:\\mypath\\my.zip", nullptr, IID_PPV_ARGS(&file))));

        // target path, create directory
        auto targetPath = L"c:\\target";
        CreateDirectory(targetPath, nullptr);

        CComPtr<IShellItem> target;
        ATLASSERT(SUCCEEDED(SHCreateItemFromParsingName(targetPath, nullptr, IID_PPV_ARGS(&target))));

        // extract file into target folder
        ExtractItemToTarget(file, target, true);
    }
    CoUninitialize();
    return 0;
}