20011010wo 发表于 2016-8-3 21:59:56

CAB压缩/解压缩文件库


#pragma once

#include <FCI.h>
#include <FDI.h>

#include <io.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <crtdefs.h>

#include <Windows.h>

#pragma warning( push )
#pragma comment( lib , "Cabinet" )
//==================================================================================================
#define ASSERTEXPR(expr)
//==================================================================================================
#define FOLDER_THRESHOLD    900000

//==================================================================================================
#ifndef CHK_EXP_RET
#define CHK_EXP_RET( exp , ret ) if( exp ) { return ret ; }
#endif

//==================================================================================================
namespace Cabinet
{
        //==============================================================================================
        template< typename T > class CEncryptImpl
        {
        public:
                BOOL SetEncryptKey(LPVOID lpKey, DWORD dwSize)
                {
                        return TRUE;
                }

                BOOL EncryptData(LPVOID lpData, DWORD dwData)
                {
                        return TRUE;
                }
        };

        template< typename T > class CDecryptImpl
        {
        public:
                BOOL SetDecryptKey(LPVOID lpKey, DWORD dwSize)
                {
                        return TRUE;
                }

                BOOL DecryptData(LPVOID lpData, DWORD dwData)
                {
                        return TRUE;
                }
        };

        //==============================================================================================
        template< typename T, typename EncryptT = CEncryptImpl<T> > class CPackageImpl : public EncryptT
        {
        public:
                CPackageImpl()
                {
                        Initialize();
                }

                ~CPackageImpl()
                {
                        DestroyContext();
                }

        public:
                BOOL CreateContext(LPCSTR lpszPathFile, UINT nSplitSize = INT_MAX, WORD wCabinetID = 0)
                {
                        // 校验输入参数
                        CHK_EXP_RET(lpszPathFile == NULL, FALSE);
                        CHK_EXP_RET(strlen(lpszPathFile) < 6, FALSE);
                        CHK_EXP_RET(IsBadReadPtr(lpszPathFile, 6), FALSE);

                        // 准备参数
                        CHAR szPathFile = { "" };
                        strncpy(szPathFile, lpszPathFile, MAX_PATH);
                        LPCSTR lpszFile = PathFindFileNameA(szPathFile);
                        CHK_EXP_RET(lpszFile == szPathFile, ERROR_PATH_NOT_FOUND);

                        // 处理数据并调用函数
                        szPathFile = 0;
                        return CreateContext(szPathFile, lpszFile, nSplitSize, wCabinetID);

                }

                BOOL CreateContext(LPCSTR lpszPath, LPCSTR lpszFile, UINT nSplitSize = INT_MAX, WORD wCabinetID = 0)
                {
                        // 校验输入参数
                        CHK_EXP_RET(lpszPath == NULL, FALSE);
                        CHK_EXP_RET(lpszFile == NULL, FALSE);

                        CHK_EXP_RET(strlen(lpszPath) < 2, FALSE);
                        CHK_EXP_RET(strlen(lpszFile) < 2, FALSE);

                        CHK_EXP_RET(IsBadReadPtr(lpszPath, 2), FALSE);
                        CHK_EXP_RET(IsBadReadPtr(lpszFile, 2), FALSE);

                        m_bAutoFlush = TRUE;
                        m_bOperAbort = FALSE;

                        T* pT = static_cast<T*>(this);
                        CHK_EXP_RET(!pT->OnPrepareCreate(m_xPackage, lpszPath, lpszFile, nSplitSize, wCabinetID), FALSE);
                        m_hContext = FCICreate(&m_xError, FCI_FilePlaced, FCI_Alloc, FCI_Free, FCI_Open, FCI_Read, FCI_Write,
                                FCI_Close, FCI_Seek, FCI_Delete, FCI_GetTempFile, &m_xPackage, this);
                        return (m_hContext != NULL);
                }

                BOOL DestroyContext()
                {
                        CHK_EXP_RET(m_hContext == NULL, TRUE);
                        CHK_EXP_RET(m_bAutoFlush && !FlushCabinet(FALSE), FALSE);
                        CHK_EXP_RET(!FCIDestroy(m_hContext), FALSE);
                        m_hContext = NULL;
                        return TRUE;
                }

                VOID AbortPackage()
                {
                        m_bOperAbort = TRUE;
                }

                BOOL FlushFolder()
                {
                        ASSERTEXPR(m_hContext != NULL);
                        CHK_EXP_RET(m_hContext == NULL, FALSE);
                        return FCIFlushFolder(m_hContext, FCI_GetNextCabinet, FCI_UpdateStatus);
                }

                BOOL FlushCabinet(BOOL bCreateNewCabinetFile = FALSE)
                {
                        ASSERTEXPR(m_hContext != NULL);
                        CHK_EXP_RET(m_hContext == NULL, FALSE);
                        return FCIFlushCabinet(m_hContext, bCreateNewCabinetFile, FCI_GetNextCabinet, FCI_UpdateStatus);
                }

                BOOL AddFile(LPSTR szFileToAdd, LPSTR szNameInCab = NULL)
                {
                        ASSERTEXPR(m_hContext != NULL);
                        CHK_EXP_RET(m_hContext == NULL, FALSE);

                        m_bAutoFlush = TRUE; // 必须刷新才行
                        szNameInCab = szNameInCab ? szNameInCab : PathFindFileNameA(szFileToAdd);
                        return FCIAddFile(m_hContext, szFileToAdd, szNameInCab, FALSE, FCI_GetNextCabinet, FCI_UpdateStatus, FCI_GetOpenInfo, tcompTYPE_MSZIP);
                }

                // nFolder 是压缩目录的根目录长度用来产生目录结构,外面调用不要设置该参数
                BOOL AddFolder(LPCSTR szFolder, LPCSTR szFilter = NULL, UINT nFolder = 0)
                {
                        ASSERTEXPR(m_hContext != NULL);
                        CHK_EXP_RET(m_hContext == NULL, FALSE);

                        CHAR szPath = { "" };
                        strncpy(szPath, szFolder, MAX_PATH);
                        PathAddBackslashA(szPath);

                        // 计算根目录的长度并设置搜索路径
                        UINT nSearch = strlen(szPath);
                        nFolder = nFolder ? nFolder : nSearch;
                        szFilter = szFilter ? szFilter : "*.*";
                        strcat(szPath, szFilter);

                        WIN32_FIND_DATAA datFinder;
                        HANDLE hFinder = FindFirstFileA(szPath, &datFinder);
                        CHK_EXP_RET(hFinder == INVALID_HANDLE_VALUE, FALSE);

                        BOOL bFindFile = (hFinder != INVALID_HANDLE_VALUE);
                        while (bFindFile)
                        {
                                if (datFinder.cFileName == '.')
                                {
                                        bFindFile = FindNextFileA(hFinder, &datFinder);
                                        continue;
                                }

                                strcpy(szPath + nSearch, datFinder.cFileName);

                                if (datFinder.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                                {
                                        if (!AddFolder(szPath, szFilter, nFolder))
                                        {
                                                FindClose(hFinder);
                                                return FALSE;
                                        }
                                }
                                else
                                {
                                        if (!AddFile(szPath, szPath + nFolder))
                                        {
                                                FindClose(hFinder);
                                                return FALSE;
                                        }
                                }

                                // 搜索下一个文件
                                bFindFile = FindNextFileA(hFinder, &datFinder);
                        }

                        FindClose(hFinder);
                        return TRUE;
                }

                BOOL SetTempDirectory(LPCSTR szTempDir)
                {
                        ASSERTEXPR(szTempDir != NULL);

                        INT nLength = strlen(szTempDir);
                        CHK_EXP_RET(3 < nLength, FALSE);
                        CHK_EXP_RET(nLength > MAX_PATH, FALSE);

                        strncpy(m_szTempDir, szTempDir, MAX_PATH);
                        PathAddBackslashA(m_szTempDir);
                        return TRUE;
                }

        public:
                // 使用下面的函数可以直接对一个目录下的特定文件或者一个文件进行打包
                BOOL PackageFolder(LPSTR szCabinet, LPSTR szFolder, LPSTR szFilter = NULL, UINT nSplitSize = INT_MAX, WORD wCabinetID = 0)
                {
                        CHK_EXP_RET(!CreateContext(szCabinet, nSplitSize, wCabinetID), FALSE);
                        CHK_EXP_RET(!AddFolder(szFolder, szFilter), (DestroyContext(), FALSE));
                        return DestroyContext();
                }

                BOOL PackageFile(LPSTR szCabinet, LPSTR szFile, LPSTR szName = NULL, UINT nSplitSize = INT_MAX, WORD wCabinetID = 0)
                {
                        CHK_EXP_RET(!CreateContext(szCabinet, nSplitSize, wCabinetID), FALSE);
                        CHK_EXP_RET(!AddFile(szFile, szName), (DestroyContext(), FALSE));
                        return DestroyContext();
                }

        public:
                // 下面这些函数是为了在 UNICODE 版本中方便使用而做的参数类型转换
                BOOL CreateContext(LPCWSTR lpszPathFile, UINT nSplitSize = INT_MAX, WORD wCabinetID = 0)
                {
                        USES_CONVERSION_EX; ASSERTEXPR(lpszPathFile != NULL);
                        return CreateContext(W2A_EX(lpszPathFile, 0), nSplitSize, wCabinetID);
                }

                BOOL CreateContext(LPCWSTR lpszPath, LPCWSTR lpszFile, UINT nSplitSize = INT_MAX, WORD wCabinetID = 0)
                {
                        USES_CONVERSION_EX; ASSERTEXPR(lpszPath != NULL); ASSERTEXPR(lpszFile != NULL);
                        return CreateContext(W2A_EX(lpszPath, 0), W2A_EX(lpszFile, 0), nSplitSize, wCabinetID);
                }

                BOOL AddFile(LPWSTR szFileToAdd, LPWSTR szNameInCab = NULL)
                {
                        USES_CONVERSION_EX; ASSERTEXPR(szFileToAdd != NULL);
                        return AddFile(W2A_EX(szFileToAdd, 0), szNameInCab ? W2A_EX(szNameInCab, 0) : NULL);
                }

                BOOL AddFolder(LPCWSTR szFolder, LPCWSTR szFilter = NULL)
                {
                        USES_CONVERSION_EX; ASSERTEXPR(szFolder != NULL);
                        return AddFolder(W2A_EX(szFolder, 0), szFilter ? W2A_EX(szFilter, 0) : NULL);
                }

                BOOL SetTempDirectory(LPCWSTR szTempDir)
                {
                        USES_CONVERSION_EX; ASSERTEXPR(szTempDir != NULL)
                                return SetTempDirectory(W2A_EX(szTempDir, 0));
                }

                BOOL PackageFolder(LPCWSTR szCabinet, LPCWSTR szFolder, LPCWSTR szFilter = NULL, UINT nSplitSize = INT_MAX, WORD wCabinetID = 0)
                {
                        USES_CONVERSION_EX;
                        return PackageFolder(W2A_EX(szCabinet, 0), W2A_EX(szFolder, 0), szFilter ? W2A_EX(szFilter, 0) : NULL, nSplitSize, wCabinetID);
                }

                BOOL PackageFile(LPCWSTR szCabinet, LPCWSTR szFile, LPCWSTR szName = NULL, UINT nSplitSize = INT_MAX, WORD wCabinetID = 0)
                {
                        USES_CONVERSION_EX;
                        return PackageFile(W2A_EX(szCabinet, 0), W2A_EX(szFile, 0), szName ? W2A_EX(szName, 0) : NULL, nSplitSize, wCabinetID);
                }

        public:
                BOOL OnPrepareCreate(CCAB& rCab, LPCSTR lpszPath, LPCSTR lpszFile, UINT nSplitSize, WORD wCabinetID)
                {
                        // 设置产生
                        ZeroMemory(&rCab, sizeof(CCAB));
                        strcpy(rCab.szCabPath, lpszPath);
                        PathAddBackslashA(rCab.szCabPath);

                        // FCI 内部处理是有符号的
                        rCab.cb = nSplitSize & INT_MAX;
                        rCab.cbFolderThresh = FOLDER_THRESHOLD;
                        rCab.iCab = rCab.iDisk = 1;
                        rCab.setID = wCabinetID;

                        // 不需要任何的扩展空间
                        rCab.cbReserveCFHeader = 0;
                        rCab.cbReserveCFFolder = 0;
                        rCab.cbReserveCFData = 0;

                        // 格式化一个文件名称出来
                        strcpy(m_szCabFileFormat, lpszFile);
                        FCI_GetNextCabinet(&rCab, 0, this);
                        return TRUE;
                }

        public:
                // 在新的类中重新定义这两个静态函数可以使用不同的内存策略
                static LPVOID Alloc(ULONG nSize) { return malloc(nSize); }
                static void Free(LPVOID lpMemory) { free(lpMemory); }

        public:
                // 下面的函数可以重新实现以实现不同的文件管理
                INT FilePlaced(PCCAB lpCabinet, LPSTR pszFile, LONG nFile, BOOL fContinuation, LPVOID pData = NULL)
                {
                        return ERROR_SUCCESS;
                }

                INT_PTR Open(LPSTR pszFile, INT nOpenFlag, INT nMode, LPINT pError, LPVOID pData = NULL)
                {
                        INT_PTR xResult = _open(pszFile, nOpenFlag, nMode);
                        if (xResult == -1) *pError = errno;
                        return xResult;
                }

                UINT Read(INT_PTR hFile, LPVOID lpMemory, UINT nSize, LPINT pError, LPVOID pData = NULL)
                {
                        UINT result = (UINT)_read(hFile, lpMemory, nSize);
                        if (result != nSize) *pError = errno;
                        return result;
                }

                UINT Write(INT_PTR hFile, LPVOID lpMemory, UINT nSize, LPINT pError, LPVOID pData = NULL)
                {
                        UINT result = (UINT)_write(hFile, lpMemory, nSize);
                        if (result != nSize) *pError = errno;
                        return result;
                }

                INT Close(INT_PTR hFile, LPINT pError, LPVOID pData = NULL)
                {
                        INT result = _close(hFile);
                        if (result != 0) *pError = errno;
                        return result;
                }

                LONG Seek(INT_PTR hFile, LONG nDistance, INT nSeekType, LPINT pError, LPVOID pData = NULL)
                {
                        LONG result = _lseek(hFile, nDistance, nSeekType);
                        if (result == -1) *pError = errno;
                        return result;
                }

                INT Delete(LPSTR pszFile, LPINT pError, LPVOID pData = NULL)
                {
                        INT result = remove(pszFile);
                        if (result != 0) *pError = errno;
                        return result;
                }

                BOOL GetTempFile(LPSTR pszTempName, INT nTempName, LPVOID pData = NULL)
                {
                        while (true)
                        {
                                wsprintfA(pszTempName, "%sCab%05u", m_szTempDir, m_nTempCounter++);
                                CHK_EXP_RET(GetFileAttributesA(pszTempName) == INVALID_FILE_ATTRIBUTES, TRUE);
                        }
                        return FALSE;
                }

        public:
                BOOL GetNextCabinet(PCCAB pCab, ULONG nPrevCab, LPVOID pData = NULL)
                {
                        wsprintfA(pCab->szCab, m_szCabFileFormat, pCab->iCab);
                        wsprintfA(pCab->szDisk, "Disk %d", pCab->iDisk++);

                        // 如果需要修改其他参数
                        // pCab->cbFolderThresh = xyz;
                        // pCab->setID = xyz;
                        // pCab->cb    = xyz;

                        return TRUE;
                }

                INT_PTR GetOpenInfo(LPSTR pszName, USHORT *pDate, USHORT *pTime, USHORT *pAttribs, LPINT pError, LPVOID pData = NULL)
                {
                        BY_HANDLE_FILE_INFORMATION    xFileInfo;
                        FILETIME                  xFileTime;

                        DWORD dwFileAttrib = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN;
                        HANDLE handle = CreateFileA(pszName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, dwFileAttrib, NULL);
                        if (handle == INVALID_HANDLE_VALUE)
                        {
                                *pError = GetLastError();
                                return UINT_MAX;
                        }

                        if (!GetFileInformationByHandle(handle, &xFileInfo))
                        {
                                *pError = GetLastError();
                                CloseHandle(handle);
                                return UINT_MAX;
                        }

                        FileTimeToLocalFileTime(&xFileInfo.ftLastWriteTime, &xFileTime);
                        FileTimeToDosDateTime(&xFileTime, pDate, pTime);

                        DWORD dwAttribs = GetFileAttributesA(pszName);
                        if (dwAttribs == ULONG_MAX) *pAttribs = 0; // 失败了不要打断工作,设置空属性
                        else *pAttribs = (INT)(dwAttribs & (_A_RDONLY | _A_SYSTEM | _A_HIDDEN | _A_ARCH));
                        CloseHandle(handle);

                        // 返回一个 _open 打开的文件句柄
                        return _open(pszName, _O_RDONLY | _O_BINARY);

                }

                LONG UpdateStatus(UINT nTypes, ULONG nParam1, ULONG nParam2, LPVOID pData = NULL)
                {
                        // 可以使用 ParseStatus 函数分析状态后直接使用结果
                        return ERROR_SUCCESS;
                }

        protected:
                // 下面的这些保护函数实现系统 FCI 回调
                static LPVOID DIAMONDAPI FCI_Alloc(ULONG nSize)
                {
                        return T::Alloc(nSize);
                }

                static void DIAMONDAPI FCI_Free(LPVOID lpMemory)
                {
                        return T::Free(lpMemory);
                }

                static int DIAMONDAPI FCI_FilePlaced(PCCAB lpCabinet, LPSTR pszFile, LONG nFile, BOOL fContinuation, LPVOID pData)
                {
                        return static_cast<T*>((CPackageImpl<T>*)pData)->FilePlaced(lpCabinet, pszFile, nFile, fContinuation);
                }

                static INT_PTR DIAMONDAPI FCI_Open(LPSTR pszFile, INT nOpenFlag, INT nMode, LPINT pError, LPVOID pData)
                {
                        return static_cast<T*>((CPackageImpl<T>*)pData)->Open(pszFile, nOpenFlag, nMode, pError);
                }

                static UINT DIAMONDAPI FCI_Read(INT_PTR hFile, LPVOID lpMemory, UINT nSize, LPINT pError, LPVOID pData)
                {
                        return static_cast<T*>((CPackageImpl<T>*)pData)->Read(hFile, lpMemory, nSize, pError);
                }

                static UINT DIAMONDAPI FCI_Write(INT_PTR hFile, LPVOID lpMemory, UINT nSize, LPINT pError, LPVOID pData)
                {
                        return static_cast<T*>((CPackageImpl<T>*)pData)->Write(hFile, lpMemory, nSize, pError);
                }

                static INT DIAMONDAPI FCI_Close(INT_PTR hFile, LPINT pError, LPVOID pData)
                {
                        return static_cast<T*>((CPackageImpl<T>*)pData)->Close(hFile, pError);
                }

                static long DIAMONDAPI FCI_Seek(INT_PTR hFile, LONG nDistance, INT nSeekType, LPINT pError, LPVOID pData)
                {
                        return static_cast<T*>((CPackageImpl<T>*)pData)->Seek(hFile, nDistance, nSeekType, pError);
                }

                static int DIAMONDAPI FCI_Delete(LPSTR pszFile, LPINT pError, LPVOID pData)
                {
                        return static_cast<T*>((CPackageImpl<T>*)pData)->Delete(pszFile, pError);
                }

                static BOOL DIAMONDAPI FCI_GetTempFile(LPSTR pszTempName, INT nTempName, LPVOID pData)
                {
                        return static_cast<T*>((CPackageImpl<T>*)pData)->GetTempFile(pszTempName, nTempName);
                }

                static BOOL DIAMONDAPI FCI_GetNextCabinet(PCCAB pCab, ULONG nPrevCab, LPVOID pData)
                {
                        CPackageImpl<T>* pThis = (CPackageImpl<T>*)(pData);
                        CHK_EXP_RET(pThis->m_bOperAbort, (TRUE == FALSE));
                        return static_cast<T*>(pThis)->GetNextCabinet(pCab, nPrevCab);
                }

                static INT_PTR DIAMONDAPI FCI_GetOpenInfo(LPSTR pszName, USHORT *pDate, USHORT *pTime, USHORT *pAttribs, LPINT pError, LPVOID pData)
                {
                        CPackageImpl<T>* pThis = (CPackageImpl<T>*)(pData);
                        CHK_EXP_RET(pThis->m_bOperAbort, (INT_PTR)(UINT_MAX));
                        return static_cast<T*>(pThis)->GetOpenInfo(pszName, pDate, pTime, pAttribs, pError);
                }

                static LONG DIAMONDAPI FCI_UpdateStatus(UINT nTypes, ULONG nParam1, ULONG nParam2, LPVOID pData)
                {
                        CPackageImpl<T>* pThis = (CPackageImpl<T>*)(pData);
                        CHK_EXP_RET(pThis->m_bOperAbort, (LONG)(ULONG_MAX));
                        return static_cast<T*>(pThis)->UpdateStatus(nTypes, nParam1, nParam2);
                }

        protected:
                VOID ParseStatus(UINT nTypes, ULONG nParam1, ULONG nParam2)
                {
                        m_nParam1 = nParam1;
                        m_nParam2 = nParam2;
                        m_nFolderPercent = 0;

                        if (statusFile == nTypes)
                        {
                                m_nTotalCompressSize += nParam1;
                                m_nTotalUncompressSize += nParam2;
                        }
                        else if (statusFolder == nTypes)
                        {
                                double nPercent = 100.0 * nParam1 / nParam2;
                                m_nFolderPercent = (ULONG)(nPercent);
                        }
                }

        protected:
                BOOL Initialize()
                {
                        m_hContext = 0;
                        m_xError.fError = FALSE;
                        GetTempPathA(MAX_PATH, m_szTempDir);
                        m_nTotalUncompressSize = 0;
                        m_nTotalCompressSize = 0;
                        m_nTempCounter = 1;
                        return TRUE;
                }

        protected:
                HFCI    m_hContext;
                CCAB    m_xPackage;
                ERF      m_xError;

                BOOL    m_bAutoFlush;
                BOOL    m_bOperAbort;

                LONG    m_nTempCounter;
                CHAR    m_szTempDir;

                CHAR    m_szCabFileFormat;

                // 下面是状态信息,可以用来处理用户界面
                ULONG    m_nTotalUncompressSize;
                ULONG    m_nTotalCompressSize;
                ULONG    m_nParam1, m_nParam2;
                ULONG    m_nFolderPercent;
        };

        //==============================================================================================
        // 文件打包类,附加消息通知功能
        class CFilePackage : public CPackageImpl< CFilePackage >
        {
        public:
                CFilePackage(HWND hWnd = NULL, UINT uMsg = WM_NULL) : m_hWnd(hWnd), m_uMsg(uMsg)
                {

                }

        public:
                struct S_GetNextCabinet
                {
                        PCCAB pCab;
                        ULONG nPrevCab;
                        LPVOID pData;
                };

                struct S_GetOpenInfo
                {
                        LPSTR pszName;
                        USHORT *pDate;
                        USHORT *pTime;
                        USHORT *pAttribs;
                        LPINT pError;
                        LPVOID pData;
                };

                struct S_UpdateStatus
                {
                        ULONG    nTypes, nParam1, nParam2;
                        ULONG    nTotalUncompressSize;
                        ULONG    nTotalCompressSize;
                        ULONG    nFolderPercent;
                };

                enum E_MessageNotify
                {
                        EM_GET_NEXT_CAIBNET = 1,
                        EM_GET_OPEN_INFO = 2,
                        EM_UPDATE_STATUS = 3,
                };

        public:
                // 重载回调函数,提供消息通知功能
                BOOL GetNextCabinet(PCCAB pCab, ULONG nPrevCab, LPVOID pData = NULL)
                {
                        S_GetNextCabinet xParam = { pCab, nPrevCab, pData };
                        SendMessage(m_hWnd, m_uMsg, EM_GET_NEXT_CAIBNET, reinterpret_cast<LPARAM>(&xParam));
                        return CPackageImpl<CFilePackage>::GetNextCabinet(pCab, nPrevCab, pData);
                }

                INT_PTR GetOpenInfo(LPSTR pszName, USHORT *pDate, USHORT *pTime, USHORT *pAttribs, LPINT pError, LPVOID pData = NULL)
                {
                        S_GetOpenInfo xParam = { pszName, pDate, pTime, pAttribs, pError, pData };
                        SendMessage(m_hWnd, m_uMsg, EM_GET_OPEN_INFO, reinterpret_cast<LPARAM>(&xParam));
                        return CPackageImpl<CFilePackage>::GetOpenInfo(pszName, pDate, pTime, pAttribs, pError, pData);
                }

                LONG UpdateStatus(UINT nTypes, ULONG nParam1, ULONG nParam2, LPVOID pData = NULL)
                {
                        ParseStatus(nTypes, nParam1, nParam2);
                        S_UpdateStatus xParam = { nTypes, nParam1, nParam2, m_nTotalUncompressSize, m_nTotalCompressSize, m_nFolderPercent };
                        SendMessage(m_hWnd, m_uMsg, EM_UPDATE_STATUS, reinterpret_cast<LPARAM>(&xParam));
                        return CPackageImpl<CFilePackage>::UpdateStatus(nTypes, nParam1, nParam2);
                }

        protected:
                HWND m_hWnd;
                UINT m_uMsg;
        };

        //==============================================================================================
        template <typename T, typename DecryptT = CDecryptImpl<T> > class CExtractImpl : public DecryptT
        {
        public:
                CExtractImpl()
                {
                        Initialize();
                }

                ~CExtractImpl()
                {
                        DestroyContext();
                }

        public:
                BOOL CreateContext(INT nCpuType = cpu80386)
                {
                        CHK_EXP_RET(m_hContext != NULL, FALSE);
                        m_hContext = FDICreate(FDI_Alloc, FDI_Free, FDI_Open, FDI_Read, FDI_Write, FDI_Close, FDI_Seek, -1, &m_xError);
                        return (m_hContext != NULL);
                }

                BOOL DestroyContext()
                {
                        CHK_EXP_RET(m_hContext == NULL, TRUE);
                        CHK_EXP_RET(!FDIDestroy(m_hContext), FALSE);
                        m_hContext = NULL;
                        return TRUE;
                }

                void AbortExtract()
                {
                        m_bOperAbort = TRUE;
                }

                BOOL Extract(LPCSTR lpszCabinet, LPCSTR lpszTarget = NULL)
                {
                        // 检查参数
                        ASSERTEXPR(m_hContext != NULL);
                        CHK_EXP_RET(m_hContext == NULL, FALSE);
                        CHK_EXP_RET(strlen(lpszCabinet) > MAX_PATH, FALSE);
                        CHK_EXP_RET(!T::IsCabinetFileName(lpszCabinet), FALSE);

                        // 分析路径
                        CHAR szPath = { "" };
                        strncpy(szPath, lpszCabinet, MAX_PATH);
                        LPSTR lpszName = PathFindFileNameA(szPath);

                        // 复制文件名
                        CHAR szName = { "" };
                        strncpy(szName, lpszName, MAX_PATH);

                        // 截取路径,注意保留结束的‘\’
                        if (lpszName != szPath) lpszName = 0;

                        // 缓存目标路径
                        ZeroMemory(m_szTargetDir, MAX_PATH);
                        if (!IsBadReadPtr(lpszTarget, 3))
                        {
                                strcpy(m_szTargetDir, lpszTarget);
                                PathAddBackslashA(m_szTargetDir);
                        }
                        else
                        {
                                PSTR szFileName = PathFindFileNameA(lpszCabinet);
                                strncpy(m_szTargetDir, lpszCabinet, szFileName - lpszCabinet);
                                PathAddBackslashA(m_szTargetDir);
                        }

                        m_bOperAbort = FALSE;
                        return FDICopy(m_hContext, szName, szPath, 0, FDI_Callback, NULL, this);
                }

                BOOL IsCabinet(INT hFile, PFDICABINETINFO lpCabInfo = NULL)
                {
                        CHK_EXP_RET(m_hContext == NULL, FALSE);

                        FDICABINETINFO xCabinetInfo;
                        lpCabInfo = lpCabInfo ? lpCabInfo : &xCabinetInfo;
                        return FDIIsCabinet(m_hContext, hFile, lpCabInfo);
                }

                BOOL IsCabinet(LPSTR szFileName, PFDICABINETINFO lpCabInfo = NULL)
                {
                        INT nAttr = _O_BINARY | _O_RDONLY | _O_SEQUENTIAL;
                        INT hCabinetFile = FDI_Open(szFileName, nAttr, 0);
                        CHK_EXP_RET(hCabinetFile == -1, FALSE);

                        BOOL bCabinet = IsCabinet(hCabinetFile, lpCabInfo);
                        FDI_Close(hCabinetFile);
                        return bCabinet;
                }

                BOOL ExtractFile(LPCSTR szCabinet, LPCSTR szTarget = NULL)
                {
                        CHK_EXP_RET(!CreateContext(cpu80386), FALSE);
                        BOOL bExtract = Extract(szCabinet, szTarget);
                        return DestroyContext() && bExtract;
                }

        public:
                // 下面的函数是为了 UNICODE 使用而做的类型转换
                BOOL Extract(LPCWSTR lpszCabinet, LPCWSTR lpszTarget = NULL)
                {
                        USES_CONVERSION_EX; ASSERTEXPR(lpszCabinet != NULL);
                        return Extract(W2A_EX(lpszCabinet, 0), lpszTarget ? W2A_EX(lpszTarget, 0) : NULL);
                }

                BOOL IsCabinet(LPCWSTR szFileName, PFDICABINETINFO lpCabInfo = NULL)
                {
                        USES_CONVERSION_EX; ASSERTEXPR(szFileName != NULL);
                        return IsCabinet(W2A_EX(szFileName, 0), lpCabInfo);
                }

                BOOL ExtractFile(LPCWSTR szCabinet, LPCWSTR szTarget = NULL)
                {
                        USES_CONVERSION_EX; ASSERTEXPR(szCabinet != NULL);
                        return ExtractFile(W2A_EX(szCabinet, 0), szTarget ? W2A_EX(szTarget, 0) : NULL);
                }

        public:
                //    下面四个函数用来处理解压缩时的回调信息
                INT_PTR OnCabinetInfo(PFDINOTIFICATION pNotify, LPVOID lpData = NULL)
                {
                        // pNotify->psz1 : szNextCabinet
                        // pNotify->psz2 : szNextDisk
                        // pNotify->psz3 : szCabPath
                        return ERROR_SUCCESS;
                }

                INT_PTR OnNextCabinet(PFDINOTIFICATION pNotify, LPVOID lpData = NULL)
                {
                        // pNotify->psz1 : szNextCabinet
                        // pNotify->psz2 : szNextDisk
                        // pNotify->psz3 : szCabPath
                        return ERROR_SUCCESS;
                }

                INT_PTR OnCopyFile(PFDINOTIFICATION pNotify, LPVOID lpData = NULL)
                {
                        char szPath = { "" };
                        strncpy(szPath, m_szTargetDir, MAX_PATH);
                        strncat(szPath, pNotify->psz1, MAX_PATH);

                        // 确保目录存在
                        LPSTR lpszPath = strchr(szPath, '\\');
                        while (lpszPath != NULL)
                        {
                                // 构筑目录名称
                                INT nLength = lpszPath - szPath;
                                CHAR szFolder = { "" };
                                strncpy(szFolder, szPath, nLength);
                                szFolder = 0;

                                // 创建目录,并搜索下一个目录
                                CreateDirectoryA(szFolder, NULL);
                                lpszPath = strchr(lpszPath + 1, '\\');
                        }

                        WORD wAttr = _O_BINARY | _O_CREAT | _O_WRONLY | _O_SEQUENTIAL;
                        return FDI_Open(szPath, wAttr, _S_IREAD | _S_IWRITE);
                }

                INT_PTR OnFileCopyComplete(PFDINOTIFICATION pNotify, LPVOID lpData = NULL)
                {
                        FDI_Close(pNotify->hf);

                        CHAR szPath = { "" };
                        strncpy(szPath, m_szTargetDir, MAX_PATH);
                        strncat(szPath, pNotify->psz1, MAX_PATH);
                        SetFileAttrDate(szPath, pNotify->date, pNotify->time, pNotify->attribs);
                        return TRUE; // 返回该值让系统继续处理,否则系统停止处理下一个
                }

        public:
                static BOOL IsCabinetFileName(LPCSTR szCabinetFile)
                {
                        return PathFileExistsA(szCabinetFile);
                }

        public:
                static LPVOID Alloc(ULONG nSize)
                {
                        return malloc(nSize);
                }

                static void Free(LPVOID lpMemory)
                {
                        return free(lpMemory);
                }

        public:
                static INT_PTR Open(LPSTR pszFile, INT nOpenFlag, INT nMode)
                {
                        return _open(pszFile, nOpenFlag, nMode);
                }

                static UINT Read(INT_PTR hFile, LPVOID lpMemory, UINT nSize)
                {
                        return _read(hFile, lpMemory, nSize);
                }

                static UINT Write(INT_PTR hFile, LPVOID lpMemory, UINT nSize)
                {
                        return _write(hFile, lpMemory, nSize);
                }

                static LONG Seek(INT_PTR hFile, LONG nDistance, INT nSeekType)
                {
                        return _lseek(hFile, nDistance, nSeekType);
                }

                static INT Close(INT_PTR hFile)
                {
                        return _close(hFile);
                }

        protected:
                // 下面的这些保护函数实现系统 FDI 回调
                static LPVOID DIAMONDAPI FDI_Alloc(ULONG nSize)
                {
                        return T::Alloc(nSize);
                }

                static void DIAMONDAPI FDI_Free(LPVOID lpMemory)
                {
                        return T::Free(lpMemory);
                }

        protected:
                static INT_PTR DIAMONDAPI FDI_Open(LPSTR pszFile, INT nOpenFlag, INT nMode)
                {
                        return T::Open(pszFile, nOpenFlag, nMode);
                }

                static UINT DIAMONDAPI FDI_Read(INT_PTR hFile, LPVOID lpMemory, UINT nSize)
                {
                        return T::Read(hFile, lpMemory, nSize);
                }

                static UINT DIAMONDAPI FDI_Write(INT_PTR hFile, LPVOID lpMemory, UINT nSize)
                {
                        return T::Write(hFile, lpMemory, nSize);
                }

                static long DIAMONDAPI FDI_Seek(INT_PTR hFile, LONG nDistance, INT nSeekType)
                {
                        return T::Seek(hFile, nDistance, nSeekType);
                }

                static INT DIAMONDAPI FDI_Close(INT_PTR hFile)
                {
                        return T::Close(hFile);
                }

        protected:
                static INT_PTR FDI_Callback(FDINOTIFICATIONTYPE eNotifyType, PFDINOTIFICATION pNotify)
                {
                        CExtractImpl<T> * pThis = (CExtractImpl<T> *)(pNotify->pv);
                        CHK_EXP_RET(pThis->m_bOperAbort, (INT_PTR)(UINT_MAX));

                        T* pT = static_cast<T *>(pThis);
                        CHK_EXP_RET(eNotifyType == fdintCOPY_FILE, pT->OnCopyFile(pNotify));
                        CHK_EXP_RET(eNotifyType == fdintCABINET_INFO, pT->OnCabinetInfo(pNotify));
                        CHK_EXP_RET(eNotifyType == fdintNEXT_CABINET, pT->OnNextCabinet(pNotify));
                        CHK_EXP_RET(eNotifyType == fdintCLOSE_FILE_INFO, pT->OnFileCopyComplete(pNotify));
                        return ERROR_SUCCESS; // 允许不支持的通知继续进行
                }

                static BOOL SetFileAttrDate(LPCSTR lpszFileName, WORD wDate, WORD wTime, WORD wAttribs)
                {
                        SetFileAttributesA(lpszFileName, wAttribs & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_ARCHIVE));
                        HANDLE hFile = CreateFileA(lpszFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
                        CHK_EXP_RET(hFile == INVALID_HANDLE_VALUE, FALSE);

                        FILETIME ftFile = { 0 , 0 }, ftLocal = { 0 , 0 };
                        CHK_EXP_RET(!DosDateTimeToFileTime(wDate, wTime, &ftFile), (CloseHandle(hFile), FALSE));
                        CHK_EXP_RET(!LocalFileTimeToFileTime(&ftFile, &ftLocal), (CloseHandle(hFile), FALSE));
                        SetFileTime(hFile, &ftLocal, NULL, &ftLocal);
                        CloseHandle(hFile);
                        return TRUE;
                }

        protected:
                VOID Initialize()
                {
                        m_hContext = NULL;
                        m_xError.fError = FALSE;
                }

        protected:
                HFDI    m_hContext;
                ERF      m_xError;

                BOOL    m_bOperAbort;
                CHAR    m_szTargetDir;    //    临时存储解压缩的目标目录
        };

}

页: [1]
查看完整版本: CAB压缩/解压缩文件库