Reputation: 63
Since win11 recently (in 23H2) added native support for rar/7zip/etc. is there any API to use those from c++ ? (Or it's just explorer thingy, that cannot be used by app) I need this for demoscene related purposes.
Upvotes: 1
Views: 301
Reputation: 139075
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:
ITransferSource2
(which IMHO should be documented). It should support IStream
or IStorage
or ITransferSource
's MoveItem
but it doesn't seem to work.#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;
}
Upvotes: 4