Giter VIP home page Giter VIP logo

Comments (3)

rikyoz avatar rikyoz commented on May 20, 2024

Hello PMime,
First of all, sorry for the late reply!

originally I wanted to use your library in our software. But my company insisted that I should take the 7zip SDK.

Thank you in any case for having considered bit7z!

Because I assume that you are experienced in the SDK, I address to you.

We have the following issue: We like to integrate the functionality of the 7zip SDK to add files to an archive. Files that are already in the archive should remain.

The SDK offers a nice C++ example: \CPP\7zip\UI\Client7z. It contains all things we aim for.

But there is an important problem. If the archive already exists and has some files in it, they disappear and only the new files are compressed in the archive.

I already have exchanged outFileStreamSpec->Create(archiveName, true) with outFileStreamSpec->Open(archiveName, OPEN_ALWAYS) in the Client7z example.

It would be nice, if you could explain how to solve this problem, best with code snippets.

Sure, I'll try my best to explain how to implement the updating of archives.
Unfortunately, the documentation of 7-zip is really scarce and incomplete in this sense.
I managed to implement it in bit7z only after digging up many small pieces of information through the internet.
And now, the internal code of bit7z has some abstraction levels that make it slightly tricky to understand how it works.

Essentially, the procedure of updating an archive consists of:

  • Creating a "temporary" archive on which to copy the compressed data from the old one;
  • Adding (compressing) the new files to this temporary archive;
  • Deleting the original archive and renaming the new one with its name.

Unfortunately, I don't know of a way to do this without using a temporary file.
As far as I know, this is the same way implemented in the 7-zip File Manager.

Anyway, a "bit" of code that I hope it can help you:

/* Relevant changes to include in your UpdateCallback */
class UpdateCallback : protected CMyUnknownImp, public IArchiveUpdateCallback { 
    public:
        UpdateCallback(const vector<FSItem>& new_items)
            : mOldArc{ nullptr }, mOldItemsCount{ 0 } /* other members */ {}
    
        virtual ~UpdateCallback() {
            Finalize();
        }
    
        MY_UNKNOWN_IMP1( IArchiveUpdateCallback )
    
        void setOldArc(IInArchive* old_arc) {
            if ( old_arc ) {
                mOldArc = old_arc;
                //Note: GetNumberOfItems may fail, here I omitted the check of its HRESULT return value!
                mOldArc->GetNumberOfItems(&mOldItemsCount);
            }
        }
    
        uint32_t itemsCount() { 
            /* Total number of items is equal to the number of items in the old archive 
               plus the new items to be added */
            return mOldItemsCount + mNewItems.size();
        }
    
        COM_DECLSPEC_NOTHROW STDMETHODIMP GetProperty(UInt32 index, PROPID propID, PROPVARIANT* value) {
            PROPVARIANT prop = {};
            if (propID == kpidIsAnti) {
                prop.vt = VT_BOOL;
                prop.boolVal = VARIANT_FALSE;
            } else if (index < mOldItemsCount) { //Index refers to an item in the old archive
                // Getting the property from the old archive reader
                mOldArc->GetProperty(index, propID, &prop);
            } else {
                // Here you should put the code for getting the properties of the new item, as usual
            }
            *value = prop;
            return S_OK;
        }
    
        COM_DECLSPEC_NOTHROW STDMETHODIMP GetStream(UInt32 index, ISequentialInStream** inStream) {
            RINOK(Finalize());
    
            if (index < mOldItemsCount) { // Old item in the archive, no need to create a stream
                return S_OK;
            }
    
            // Here you should put the code for creating a stream for the new file
    
            return S_OK;
        }
    
        COM_DECLSPEC_NOTHROW STDMETHODIMP GetUpdateItemInfo(UInt32 index,
            Int32* newData,
            Int32* newProperties,
            UInt32* indexInArchive) {
    
            bool is_old_item = index < mOldItemsCount;

            //Note: 0=false, i.e. no new data (old item). 1=true, i.e. new data (new item);
            if (newData != nullptr) {
                *newData = is_old_item ? 0 : 1; 
            }
            if (newProperties != nullptr) {
                *newProperties = is_old_item ? 0 : 1;
            }
            if (indexInArchive != nullptr) {
                *indexInArchive = is_old_item ? index : static_cast<uint32_t>(-1);
            }
    
            return S_OK;
        }
    
        // Other methods from IArchiveUpdateCallback

    private:
        IInArchive* mOldArc;
        uint32_t mOldItemsCount;
        // Other private members of your update callback class
};

int main() {
    std::cout << "Loading 7-zip DLL..." << std::endl;
    auto library = LoadLibrary("./7z.dll");
    if (library == nullptr) {
        std::cerr << "Could not load 7z.dll" << std::endl;
        return -1;
    }

    std::cout << "Getting CreateObject function..." << std::endl;
    auto create_object = reinterpret_cast<CreateObjectFunc>(GetProcAddress(library, "CreateObject"));
    if (create_object == nullptr) {
        std::cerr << "Could not get CreateObject function" << std::endl;
        return -1;
    }
        
    std::cout << "Initializing old archive reader..." << std::endl;
    CMyComPtr<IInArchive> old_arc;
    HRESULT res = create_object(&format_GUID, &::IID_IInArchive, reinterpret_cast<void**>(&old_arc));
    if ( res != S_OK ) {
        std::cerr << "Could not get class object for old_arc (error " << res << ")" << std::endl;
        return -1;
    }
    
    std::cout << "Creating old archive input stream..." << std::endl;
    CMyComPtr<CInFileStream> in_file_stream = new CInFileStream;
    if ( !in_file_stream->Open( L"archive.7z" ) ) {
        std::cerr << "Could not open input archive file stream" << std::endl;
        return -1;
    }

    std::cout << "Initializing OpenCallback for the old archive..." << std::endl;
    CMyComPtr<IArchiveOpenCallback> open_callback = new OpenCallback();
    res = old_arc->Open( in_file_stream, nullptr, open_callback );
    if ( res != S_OK ) {
        std::cerr << "Could not open archive file" << std::endl;
        return -1;
    }
    
    std::cout << "Initializing the new output archive from the input old one..." << std::endl;
    CMyComPtr<IOutArchive> new_arc;
    res = old_arc->QueryInterface(::IID_IOutArchive, reinterpret_cast<void**>(&new_arc));
    if ( res != S_OK ) {
        std::cerr << "Could not initialize new archive (error " << res << ")" << std::endl;
        return -1;
    }

    std::cout << "Creating output file stream..." << std::endl;
    CMyComPtr<COutFileStream> out_file_stream = new COutFileStream();
    if (!out_file_stream->Create(L"archive.7z.tmp", true)) {
        std::cerr << "Could not create temp file" << std::endl;
        return -1;
    }

    std::cout << "Initializing UpdateCallback..." << std::endl;
    /* New item that you want to add.
       Note: FSItem is a class that I use in bit7z, it's similar to the CDirItem in Client7z.cpp */
    FSItem item{L"7z.dll"}; 
    vector<FSItem> new_items = {item}; //new items vector
    CMyComPtr<UpdateCallback> update_callback = new UpdateCallback{new_items};
    /* This is the most important part: your update callback must have access 
       to the old archive reader so that it can:
        - distinguish between requests concerning old and new data;
        - have access to the properties of the old items.
    */
    update_callback->setOldArc(old_arc);

    std::cout << "Compressing new files..." << std::endl;
    res = new_arc->UpdateItems(out_file_stream, update_callback->itemsCount(), update_callback);

    if (res == E_NOTIMPL) {
        std::cerr << "Unsupported operation" << std::endl;
        return -1;
    }

    if (res != S_OK) {
        std::cerr << "Error while compressing" << std::endl;
        return -1;
    }

    // Releasing the streams so that we can rename the temporary archive back to the original file name
    in_file_stream.Release();
    out_file_stream.Release();
        
    // Closing the old archive so that we can delete it
    old_arc->Close();

    // Renaming the temporary archive back to the original file name
    std::error_code ec;
    std::filesystem::rename("archive.7z.tmp", "archive.7z", ec);
    if (ec) {
        std::cerr << "Could not restore the old archive file name (" << ec.message() << ")" << std::endl;
        return -1;
    }
    std::cout << "Operation completed!" << std::endl;
}

from bit7z.

PMime avatar PMime commented on May 20, 2024

Thank you Riccardo! Especially the code is very helpful

from bit7z.

rikyoz avatar rikyoz commented on May 20, 2024

Glad that I could be of assistance!

from bit7z.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.