Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Explorer un zip comme un dossier

8 réponses
Avatar
Sylvain SF
Une de mes applis que je teste en ce moment sous W7
utilise le dialogue de sélection de dossier.

Sous mon XP de dev. le support des ZIP est désactivé,
SHBrowseForFolder() invoqué avec BIF_RETURNONLYFSDIRS
ne m'affiche que les dossiers et la sélection est
nécessairement valide.

Sous W7, où je n'ai pas invalidé le support des ZIP,
SHBrowseForFolder() affiche les zip comme des dossiers
et permet leur sélection - certes l'explorer les voit
comme tel mais pas mon appli. qui ne peut les explorer
via FindFirst/Next/File.

Existe-t-il un moyen (transparent pour l'appli.) de
parcourir le contenu d'un zip, alternativement le
parcours est-il possible via des API système (utilisées
sur détection explicite d'un .zip en lieu et place d'un
vrai répertoire) ?

merci,
Sylvain.

8 réponses

Avatar
Christian ASTOR
Sylvain SF wrote:

Existe-t-il un moyen (transparent pour l'appli.) de
parcourir le contenu d'un zip, alternativement le
parcours est-il possible via des API système (utilisées
sur détection explicite d'un .zip en lieu et place d'un
vrai répertoire) ?



Si zipfldr.dll est enregistré, les zips sont traités par le Shell comme
des dossiers, donc on peut les énumérer de façon classique en énumérant
le Shell Namespace (IShellFolder, IEnumIDList)
Avatar
Sylvain SF
Christian ASTOR a écrit :

le parcours [d'un zip] est-il possible via des API système



Si zipfldr.dll est enregistré, les zips sont traités par le Shell comme
des dossiers, donc on peut les énumérer de façon classique en énumérant
le Shell Namespace (IShellFolder, IEnumIDList)



certes, le parcours par la face Nord est possible ...

mais même dans ce cas, ie:

(theIdList obtenu par SHBrowseForFolder)

IShellFolder* desktop = null;
::SHGetDesktopFolder(&desktop);
IShellFolder* folder = null;
desktop->BindToObject(theIdList, null, IID_IShellFolder, &folder);
IEnumIDList* files = null;
folder->EnumObjects(null, SHCONTF_NONFOLDERS, &files);
ITEMIDLIST* pidl = null;
DWORD fetched = 0;

while (files->Next(1, &pidl, &fetched) == S_OK && fetched){

que faire ici du fichier (pidl) courant ?
}

une conversion PIDL vers path donne un chemin inutilisable.
une interface IID_IDataObject peut être obtenue pour ce PIDL
mais quel flavor (clipboard format) demander ?

à ce stade, j'ai l'impression que Windows, puisse qu'il ne fournit
aucun moyen direct d'utiliser zipfldr pour zipper et dézipper, est
incapable de dézipper à la volée pour permettre l'accès au contenu
d'un des fichiers du zip.

j'ai loupé un moyen ?... en plus d'oublier de dire que je voulais
lire les fichiers, en plus de les itérer.

Sylvain.
Avatar
Christian ASTOR
Sylvain SF wrote:

une conversion PIDL vers path donne un chemin inutilisable.
une interface IID_IDataObject peut être obtenue pour ce PIDL
mais quel flavor (clipboard format) demander ?



J'obtiens bien le nom de chaque fichier avec ::GetDisplayNameOf()

à ce stade, j'ai l'impression que Windows, puisse qu'il ne fournit
aucun moyen direct d'utiliser zipfldr pour zipper et dézipper, est
incapable de dézipper à la volée pour permettre l'accès au contenu
d'un des fichiers du zip.
j'ai loupé un moyen ?... en plus d'oublier de dire que je voulais
lire les fichiers, en plus de les itérer.



Pour les extraire, il faut utiliser IShellDispatch.
Par exemple =>
(je n'ai pas supprimé les "Temporary Directory n" que ça crée à chaque
copie...)

{
WCHAR wsFolder[MAX_PATH] = L"c:zipfile.zip";
HRESULT hr;
IShellDispatch *pShellDisp = NULL;

CoInitialize(NULL);
hr = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_SERVER,
IID_IShellDispatch, (LPVOID *) &pShellDisp );
if (SUCCEEDED(hr))
{
Folder* pFolderDest;
VARIANT vVariantDest;
VariantInit(&vVariantDest);
vVariantDest.vt = VT_BSTR;
WCHAR wsTempPath[MAX_PATH];
GetTempPath(MAX_PATH, wsTempPath);
vVariantDest.bstrVal = SysAllocString(wsTempPath);
pShellDisp->NameSpace(vVariantDest, &pFolderDest);
if (SUCCEEDED(hr))
{
VARIANT vVariantSrc;
VariantInit(&vVariantSrc);
vVariantSrc.vt = VT_BSTR;
vVariantSrc.bstrVal = SysAllocString(wsFolder);
Folder* pFolderSrc;
hr = pShellDisp->NameSpace(vVariantSrc, &pFolderSrc);
if (SUCCEEDED(hr))
{
FolderItems* pFolderItems;
hr = pFolderSrc->Items(&pFolderItems);
if (SUCCEEDED(hr))
{
VARIANT vOption;
//vOption.vt = VT_EMPTY;
VariantInit(&vOption);
vOption.vt = VT_I4;
vOption.lVal = 4;
long nCount;
hr = pFolderItems->get_Count(&nCount);
for (int i = 0; i<nCount; i++)
{
FolderItem* pFolderItem;
VARIANT vIndex;
VariantInit(&vIndex);
vIndex.vt = VT_I4;
vIndex.lVal = i;
hr = pFolderItems->Item(vIndex, &pFolderItem);
if (SUCCEEDED(hr))
{
VARIANT vVariant;
VariantInit(&vVariant);
vVariant.vt = VT_DISPATCH;
vVariant.ppdispVal = (IDispatch**)pFolderItem;
hr = pFolderDest->CopyHere(vVariant, vOption);
pFolderItem->Release();
}
VariantClear(&vIndex);
}
pFolderItems->Release();
}
pFolderSrc->Release();
}
VariantClear(&vVariantSrc);
pFolderDest->Release();
}
VariantClear(&vVariantDest);
pShellDisp->Release();
}
}
Avatar
Sylvain SF
Christian ASTOR a écrit :

une conversion PIDL vers path donne un chemin inutilisable.



J'obtiens bien le nom de chaque fichier avec ::GetDisplayNameOf()



oui je voulais dire que fopen("x:...container.zipfoo.ext", "?")
ne fonctionne pas vraiment, "inutilisable" n'était pas "invalide".

Pour les extraire, il faut utiliser IShellDispatch.
Par exemple => [...]



j'étais tombé sur un sample qui utilisait une interface IShellDispatch
pour "setter" la propriété 'Items' - ce qui ne marchait pas - merci pour
cette variante.

Sylvain.
Avatar
Sylvain SF
Christian ASTOR a écrit :

Pour les extraire, il faut utiliser IShellDispatch.



et cela fonctionne très bien, merci encore.
(avec qlq get_IsFolder et get_GetFolder pour récursiver le tout).

(je n'ai pas supprimé les "Temporary Directory n" que ça crée à chaque
copie...)



ça c'est en effet très pénible, le parcours des sous-rép. ne créé pas
de dossiers temporaires (le dispatch se promène dans le zip) mais
l'extraction de foo.ziprep1bar.ext vers c:temp créé
c:tempbar.ext
et
c:tempTemporary Directory n for foo.ziprep1bar.ext

évidemment en boucle on se retrouve avec des dizaines de Temp. Dir.
cachés en lecture seule et dont la localisation du nom est inconnue.

existe-t-il un moyen de connaître le template du nom de ces dossiers?
(je ne trouve "Temporary Directory" nul part sur un OS-en, ni
"Répertoire temporaire" sur un OS-fr).

Sylvain.
Avatar
Christian ASTOR
Sylvain SF wrote:

existe-t-il un moyen de connaître le template du nom de ces dossiers?
(je ne trouve "Temporary Directory" nul part sur un OS-en, ni
"Répertoire temporaire" sur un OS-fr).



Il y a la chaîne
"Temporary Directory %1 for %2"
stockée en ressources dans zipfldr.dll (US)
Mais il faudrait vérifier sur des OS d'autres langues pour savoir si
c'est localisé selon la langue ou en dur...
Avatar
Sylvain SF
Christian ASTOR a écrit :

Il y a la chaîne
"Temporary Directory %1 for %2"
stockée en ressources dans zipfldr.dll (US)
Mais il faudrait vérifier sur des OS d'autres langues pour savoir si
c'est localisé selon la langue ou en dur...



en effet en ressources propres de la DLL, chaîne d'ID 10227.

la persistance de ces dossiers temporaires ressemble fort
à un bug de l'interface Folder de ce namespace; l'extraction
respectant la hiérarchie dans le zip est sûrement requise
mais "CopyHere" ne signifie pas "CopyHereAndElsewhere".

merci pour ton aide.
Sylvain.
Avatar
Sylvain SF
Sylvain SF a écrit :

la persistance de ces dossiers temporaires ressemble fort
à un bug de l'interface Folder de ce namespace []



qui a disparu avec Windows 7, btw.

SF.