Domanda

Sto cercando un modo per creare un file ZIP da una cartella nelle API Windows C / C ++. Posso trovare il modo di farlo in VBScript usando il metodo Shell32.Application CopyHere e ho trovato un tutorial che spiega come farlo anche in C #, ma nulla per l'API C (anche C ++ va bene, il progetto usa già MFC).

Sarei molto grato se qualcuno potesse condividere un codice C di esempio che può creare correttamente un file zip su Windows XP / 2003. In caso contrario, se qualcuno può trovare documenti solidi o un tutorial sarebbe fantastico, dal momento che le ricerche MSDN non risultano molto. Spero davvero di evitare di dover spedire una libreria di terze parti per questo, perché la funzionalità è ovviamente lì, non riesco proprio a capire come accedervi. Le ricerche di Google non rivelano nulla di utile, solo stuzzicanti informazioni. Spero che qualcuno nella comunità lo abbia risolto e possa condividerlo per i posteri!

È stato utile?

Soluzione

EDIT: questa risposta è vecchia, ma non posso eliminarla perché è stata accettata. Vedi il prossimo

https://stackoverflow.com/a/121720/3937

----- RISPOSTA ORIGINALE -----

C'è un codice di esempio per farlo qui

[EDIT: il collegamento è ora interrotto]

http://www.eggheadcafe.com/software /aspnet/31056644/using-shfileoperation-to.aspx

Assicurati di leggere su come gestire il monitoraggio del completamento del thread.

Modifica: dai commenti, questo codice funziona solo su file zip esistente, ma @ Simon ha fornito questo codice per creare un file zip vuoto

FILE* f = fopen("path", "wb");
fwrite("\x50\x4B\x05\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 22, 1, f);
fclose(f);

Altri suggerimenti

Come notato altrove nei commenti, questo funzionerà solo su un file Zip già creato. Inoltre, il contenuto non deve essere già presente nel file zip, altrimenti verrà visualizzato un errore. Ecco il codice di esempio funzionante che sono stato in grado di creare in base alla risposta accettata. Devi collegare a shell32.lib e anche kernel32.lib (per CreateToolhelp32Snapshot).

#include <windows.h>
#include <shldisp.h>
#include <tlhelp32.h>
#include <stdio.h>

int main(int argc, TCHAR* argv[])
{
    DWORD strlen = 0;
    char szFrom[] = "C:\\Temp",
         szTo[] = "C:\\Sample.zip";
    HRESULT hResult;
    IShellDispatch *pISD;
    Folder *pToFolder = NULL;
    VARIANT vDir, vFile, vOpt;
    BSTR strptr1, strptr2;

    CoInitialize(NULL);

    hResult = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void **)&pISD);

    if  (SUCCEEDED(hResult) && pISD != NULL)
    {
        strlen = MultiByteToWideChar(CP_ACP, 0, szTo, -1, 0, 0);
        strptr1 = SysAllocStringLen(0, strlen);
        MultiByteToWideChar(CP_ACP, 0, szTo, -1, strptr1, strlen);

        VariantInit(&vDir);
        vDir.vt = VT_BSTR;
        vDir.bstrVal = strptr1;
        hResult = pISD->NameSpace(vDir, &pToFolder);

        if  (SUCCEEDED(hResult))
        {
            strlen = MultiByteToWideChar(CP_ACP, 0, szFrom, -1, 0, 0);
            strptr2 = SysAllocStringLen(0, strlen);
            MultiByteToWideChar(CP_ACP, 0, szFrom, -1, strptr2, strlen);

            VariantInit(&vFile);
            vFile.vt = VT_BSTR;
            vFile.bstrVal = strptr2;

            VariantInit(&vOpt);
            vOpt.vt = VT_I4;
            vOpt.lVal = 4;          // Do not display a progress dialog box

            hResult = NULL;
            printf("Copying %s to %s ...\n", szFrom, szTo);
            hResult = pToFolder->CopyHere(vFile, vOpt); //NOTE: this appears to always return S_OK even on error
            /*
             * 1) Enumerate current threads in the process using Thread32First/Thread32Next
             * 2) Start the operation
             * 3) Enumerate the threads again
             * 4) Wait for any new threads using WaitForMultipleObjects
             *
             * Of course, if the operation creates any new threads that don't exit, then you have a problem. 
             */
            if (hResult == S_OK) {
                //NOTE: hard-coded for testing - be sure not to overflow the array if > 5 threads exist
                HANDLE hThrd[5]; 
                HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPALL ,0);  //TH32CS_SNAPMODULE, 0);
                DWORD NUM_THREADS = 0;
                if (h != INVALID_HANDLE_VALUE) {
                    THREADENTRY32 te;
                    te.dwSize = sizeof(te);
                    if (Thread32First(h, &te)) {
                        do {
                            if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID)) ) {
                                //only enumerate threads that are called by this process and not the main thread
                                if((te.th32OwnerProcessID == GetCurrentProcessId()) && (te.th32ThreadID != GetCurrentThreadId()) ){
                                    //printf("Process 0x%04x Thread 0x%04x\n", te.th32OwnerProcessID, te.th32ThreadID);
                                    hThrd[NUM_THREADS] = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
                                    NUM_THREADS++;
                                }
                            }
                            te.dwSize = sizeof(te);
                        } while (Thread32Next(h, &te));
                    }
                    CloseHandle(h);

                    printf("waiting for all threads to exit...\n");
                    //Wait for all threads to exit
                    WaitForMultipleObjects(NUM_THREADS, hThrd , TRUE , INFINITE);

                    //Close All handles
                    for ( DWORD i = 0; i < NUM_THREADS ; i++ ){
                        CloseHandle( hThrd[i] );
                    }
                } //if invalid handle
            } //if CopyHere() hResult is S_OK

            SysFreeString(strptr2);
            pToFolder->Release();
        }

        SysFreeString(strptr1);
        pISD->Release();
    }

    CoUninitialize();

    printf ("Press ENTER to exit\n");
    getchar();
    return 0;

}

Ho deciso di non seguire questa strada nonostante abbia ottenuto il codice semi-funzionale, poiché dopo ulteriori indagini, sembra che il metodo Folder :: CopyHere () in realtà non rispetti le vOpzioni passate ad esso, il che significa che non puoi forzarlo a sovrascrivere i file o non visualizzare finestre di dialogo di errore per l'utente.

Alla luce di ciò, ho provato anche la libreria XZip citata da un altro poster. Questa libreria funziona bene per la creazione di un archivio Zip, ma nota che la funzione ZipAdd () chiamata con ZIP_FOLDER non è ricorsiva, ma crea semplicemente una cartella nell'archivio. Per comprimere ricorsivamente un archivio è necessario utilizzare la funzione AddFolderContent (). Ad esempio, per creare un C: \ Sample.zip e aggiungere la cartella C: \ Temp, utilizzare quanto segue:

HZIP newZip = CreateZip("C:\\Sample.zip", NULL, ZIP_FILENAME);
BOOL retval = AddFolderContent(newZip, "C:", "temp");

Nota importante: la funzione AddFolderContent () non è funzionale come inclusa nella libreria XZip. Ricorrerà nella struttura della directory ma non riuscirà ad aggiungere alcun file all'archivio zip, a causa di un bug nei percorsi passati a ZipAdd (). Per utilizzare questa funzione è necessario modificare l'origine e cambiare questa riga:

if (ZipAdd(hZip, RelativePathNewFileFound, RelativePathNewFileFound, 0, ZIP_FILENAME) != ZR_OK)

A quanto segue:

ZRESULT ret;
TCHAR real_path[MAX_PATH] = {0};
_tcscat(real_path, AbsolutePath);
_tcscat(real_path, RelativePathNewFileFound);
if (ZipAdd(hZip, RelativePathNewFileFound, real_path, 0, ZIP_FILENAME) != ZR_OK)

Usiamo XZip per questo scopo. È gratuito, viene fornito come codice sorgente C ++ e funziona bene.

http://www.codeproject.com/KB/cpp/xzipunzip.aspx

Il codice sopra per creare un file zip vuoto è rotto, come affermano i commenti, ma sono stato in grado di farlo funzionare. Ho aperto una zip vuota in un editor esadecimale e ho notato alcune differenze. Ecco il mio esempio modificato:

FILE* f = fopen("path", "wb"); 
fwrite("\x50\x4B\x05\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 22, 1, f);
fclose(f);

Questo ha funzionato per me. Sono stato quindi in grado di aprire la cartella compressa. Non testato con app di terze parti come winzip.

Una rapida ricerca su Google ha prodotto questo sito: http: //www.example -code.com/vcpp/vcUnzip.asp che ha un esempio molto breve per decomprimere un file usando una libreria scaricabile. Ci sono molte altre librerie disponibili. Un altro esempio è disponibile su Code Project intitolato Zip and Decomprimi in modo MFC che ha un intero esempio di gui. Se vuoi farlo con .NET, ci sono sempre le classi in System.Compression.

Esiste anche il libarario 7-Zip http://www.7-zip.org /sdk.html . Ciò include la fonte per diverse lingue ed esempi.

Non credo che le API MFC o C / C ++ standard di Windows forniscano un'interfaccia per la funzionalità zip integrata.

Potresti sempre collegarti staticamente alla libreria zip freeware se non vuoi spedire un'altra libreria ...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top