制作 MSI 安装程序少不了 CAB 文件,CAB 文件可以对需要安装程序进行压缩,以达到快速分发新程序的目的,通过参考高人的代码和 MSDN 整理了一套 Cabinet 文件的操作实现类,使用这些类可以:
  1、将多个文件制作为一个 CAB 文件
  2、将一个目录直接打包为 CAB 文件
  3、将磁盘上的 CAB 文件中解包出文件
  4、从内存中的 CAB 数据流中解包出文件 
  5、从程序的 CAB 资源中解包出文件
  6、通过策略模式可以对 CAB 文件中的数据进行加解密

通过该套封装类,已经实现了制作单文件的安装程序 Setup.exe ,该程序使用 /Create 参数启动时根据指定的目录打包为一个 CAB 文件并将该文件加入到特定的资源中生成一个用于安装的 Setup.exe(同一个程序,如果没有指定参数那么根据有无 CAB 资源来决定是进行“安装”还是“卸载”),在程序中添加资源很简单,参考下面的例子代码:

     // --------------------------------------------------------------------------
     //     概述:
     //         根据指定的程序和 CAB 文件创建安装程序
     //     参数:
     //         szInstall    - [ IN ]    生产的目标安装程序路径
     //         szPackage    - [ IN ]    安装程序使用的 CAB 文件路径
     //         szProgram    - [ IN ]    原始的安装程序路径
    BOOL CreateSetup(LPTSTR szInstall, LPTSTR szPackage, LPTSTR szProgram);
下面是该函数的实现:

BOOL CPagePackage::CreateSetup(LPTSTR szInstall, LPTSTR szPackage, LPTSTR szProgram)
{
     //     复制安装程序到目标路径处
    BOOL bCopy  =  CopyFile(szProgram, szInstall, FALSE);
    CHK_EXP_RET( FALSE  ==  bCopy , ShowSystemError() );

//     打开 CAB 文件
    HANDLE hFile  =  CreateFile(szPackage, GENERIC_READ,  0 ,  0 , OPEN_ALWAYS,  0 ,  0 );
    CHK_EXP_RET( hFile  ==  INVALID_HANDLE_VALUE , ShowSystemError() );
    BasicHelper::CHandleManager xAutoFileManager( hFile );

//     创建 CAB 文件到共享内存映射的句柄
    HANDLE hMapping  =  CreateFileMapping(hFile, NULL, PAGE_READONLY,  0 ,  0 , NULL );
    CHK_EXP_RET( hMapping  ==  NULL , ShowSystemError() );
    BasicHelper::CHandleManager xMapping( hMapping );

CONST LPCTSTR szResName  =  MAKEINTRESOURCE(  1  );
    CONST WORD wLanguage  =  MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED);

//     获取 CAB 文件大小
    DWORD dwSize  =  GetFileSize( hFile, ERROR_SUCCESS  +  NO_ERROR );
    CHK_EXP_RET( dwSize  ==  INVALID_FILE_SIZE , ShowSystemError() );

//     将 CAB 文件映射到共享内存中
    LPVOID lpBuffer  =  MapViewOfFile(hMapping, FILE_MAP_READ,  0 ,  0 ,  0 );
    CHK_EXP_RET( lpBuffer  ==  NULL , ShowSystemError() );

//     准备将 CAB 文件内容添加到安装程序中
    HANDLE hUpdate  =  BeginUpdateResource(szInstall, FALSE);
    CHK_EXP_RET( hUpdate  ==  NULL , ShowSystemError() );

//     将 CAB 文件内容更新到目标安装程序的资源中
    UpdateResource(hUpdate, RT_RCDATA, szResName, wLanguage, lpBuffer, dwSize);
    EndUpdateResource(hUpdate, FALSE);
    UnmapViewOfFile(lpBuffer);
     return  NO_ERROR  +  TRUE;
}

呵呵,最后附上 CAB 文件完整的实现文件:

CabinetImpl.h
////
//    概述:
//        基于系统函数的文件压缩解压缩包实现类
//
//    日期:
//        2008 年 1 月 12 日
//
//    作者:
//        王志科
//
//    内容:
//        <a>    CEncryptImpl<T>        对数据进行加密
//        <b> CDecryptImpl<T>        对数据进行解密
//
//        <1>    CPackageImpl<T>        文件打包实现类
//        <2> CExtractImpl<T>        文件解包实现类
//        <3> CExpressImpl<T>        内存解包实现类
//        <4> CStorageImpl<T>        资源解包实现类
//
//        <5>    CFilePackage        文件打包包装类
//        <6> CFileExtract        文件解包包装类
//        <7> CFileExpress        内存解包包装类
//        <8>    CFileStorage        资源解包包装类
//        
//    备注:
//        <1>    由于 FCI 需要的都是 ANSI 格式的参数,所以使用了大量 ANSI API,一些调用时需要转换参数
//        <2> 对加解密的支持采用策略的方式提供,可以参考系统的 CryptGraphics 服务提供的函数
////

#pragma once

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

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

#include <Windows.h>

#pragma warning( push )
#pragma comment( lib , "Cabinet" )

//==================================================================================================
#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[MAX_PATH] = { "" };
            strncpy(szPathFile, lpszPathFile, MAX_PATH);
            LPCSTR lpszFile = PathFindFileNameA(szPathFile);
            CHK_EXP_RET(lpszFile == szPathFile, ERROR_PATH_NOT_FOUND);

// 处理数据并调用函数
            szPathFile[lpszFile - szPathFile - 1] = 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()
        {
            ATLASSERT( m_hContext != NULL );
            CHK_EXP_RET( m_hContext == NULL , FALSE );
            return FCIFlushFolder(m_hContext, FCI_GetNextCabinet, FCI_UpdateStatus);
        }

BOOL FlushCabinet(BOOL bCreateNewCabinetFile = FALSE)
        {
            ATLASSERT( 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)
        {
            ATLASSERT(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)
        {
            ATLASSERT(m_hContext != NULL);
            CHK_EXP_RET( m_hContext == NULL , FALSE );

CHAR szPath[MAX_PATH] = { "" };
            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[0] == '.')
                {
                    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)
        {
            ATLASSERT(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; ATLASSERT(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; ATLASSERT(lpszPath!=NULL); ATLASSERT(lpszFile!=NULL);
            return CreateContext(W2A_EX(lpszPath,0),W2A_EX(lpszFile,0),nSplitSize,wCabinetID);
        }

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

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

BOOL SetTempDirectory(LPCWSTR szTempDir)
        {
            USES_CONVERSION_EX; ATLASSERT(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[MAX_PATH];

CHAR    m_szCabFileFormat[CB_MAX_CABINET_NAME];

// 下面是状态信息,可以用来处理用户界面
        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)
        {
            // 检查参数
            ATLASSERT(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[MAX_PATH] = { "" };
            strncpy(szPath, lpszCabinet, MAX_PATH);
            LPSTR lpszName = PathFindFileNameA(szPath);

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

// 截取路径,注意保留结束的‘\’
            if(lpszName != szPath) lpszName[0] = 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; ATLASSERT(lpszCabinet != NULL);
            return Extract(W2A_EX(lpszCabinet,0), lpszTarget ? W2A_EX(lpszTarget,0) : NULL);
        }

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

BOOL ExtractFile(LPCWSTR szCabinet, LPCWSTR szTarget = NULL)
        {
            USES_CONVERSION_EX; ATLASSERT(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[MAX_PATH] = { "" };
            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[MAX_PATH] = { "" };
                strncpy(szFolder, szPath, nLength);
                szFolder[nLength] = 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[MAX_PATH] = { "" };
            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[MAX_PATH];    //    临时存储解压缩的目标目录
    };

//==============================================================================================
    //    直接从 CAB 文件进行解压缩
    class CFileExtract : public CExtractImpl<CFileExtract>
    {

};

//==============================================================================================
    //    从内存缓冲中解压缩 CAB 文件
    template <typename T> class CExpressImpl : public CExtractImpl<T>
    {
    public:
        BOOL ExtractMemory(LPCTSTR szTargetDir, LPVOID lpMemory, DWORD dwLength)
        {
            TCHAR szMemory[MAX_PATH] = { TEXT("") };
            CHK_EXP_RET( !MakeMemoryName(szMemory, MAX_PATH, lpMemory, dwLength) , FALSE);
            return ExtractFile(szMemory, szTargetDir);
        }

public:
        // 重载该函数实现不同的资源访问
        static LPCTSTR GetIdentifier()
        {
            return TEXT("MEM://");
        }

static BOOL MakeCabinetName(LPTSTR szCabinet, DWORD dwLength, LPCTSTR szInput)
        {
            // 检查输入的参数
            CHK_EXP_RET(szInput == NULL, FALSE);
            CHK_EXP_RET(dwLength == NULL, FALSE);
            CHK_EXP_RET(szCabinet == NULL, FALSE);

// 检查缓冲区大小
            SIZE_T dwInput = lstrlen(szInput);
            LPCTSTR szIdentifier = T::GetIdentifier();
            SIZE_T dwIdentifier = lstrlen(szIdentifier);
            CHK_EXP_RET(dwIdentifier + dwInput >= dwLength , FALSE);

// 构筑压缩包的名称
            lstrcpy(szCabinet, szIdentifier); 
            lstrcpy(szCabinet + dwIdentifier, szInput);
            return TRUE;
        }

// 使用该函数构筑一个存储于内存中的压缩包的名称
        static BOOL MakeMemoryName(LPTSTR szCabinet, DWORD dwLength, LPVOID lpData, SIZE_T dwSize)
        {
            CHK_EXP_RET(dwSize == 0, FALSE);
            CHK_EXP_RET(lpData == NULL, FALSE);

CONST TCHAR szFormat[] = { TEXT("%p/%IX") };
            TCHAR szMemory[MAX_PATH] = { TEXT("") }; // Addr/Size
            wsprintf(szMemory, szFormat, lpData, dwSize);
            return MakeCabinetName(szCabinet, dwLength, szMemory);
        }

static BOOL IsCabinetFileName(LPCSTR szFileName)
        {
            USES_CONVERSION_EX;
            LPCSTR szIdentifier = T2A_EX(T::GetIdentifier(),0);
            SIZE_T dwIdentifier = strlen(szIdentifier);
            return StrCmpNIA(szIdentifier, szFileName, dwIdentifier) == 0;
        }

static BOOL IsCabinetFileName(LPCWSTR szFileName)
        {
            USES_CONVERSION_EX;
            LPWSTR szIdentifier = T2W_EX(T::GetIdentifier(),0);
            SIZE_T dwIdentifier = lstrlenW(szIdentifier);
            return StrCmpNIW(szIdentifier, szFileName, dwIdentifier) == 0;
        }

public:
        struct MemoryInfo
        {
            LPBYTE lpBuffer;
            SIZE_T dwBuffer;
            SIZE_T dwOffset;
            SIZE_T dwSymbol;
        };

static INT_PTR InterOpenMemory(LPVOID lpBuffer, SIZE_T dwBuffer, SIZE_T dwSymbol = ULONG_MAX)
        {
            MemoryInfo* lpInfo = new MemoryInfo;
            CHK_EXP_RET(lpInfo == NULL , -1);

lpInfo->lpBuffer = (LPBYTE)lpBuffer;
            lpInfo->dwBuffer = (SIZE_T)dwBuffer;
            lpInfo->dwSymbol = (SIZE_T)dwSymbol;
            lpInfo->dwOffset = (SIZE_T)NO_ERROR;
            
            return (INT_PTR)(lpInfo);
        }

public:
        // 下面这四个函数都是访问内存 CAB 文件数据的实现函数
        static INT_PTR InterOpen(LPSTR pszFile, INT nOpenFlag, INT nMode)
        {
            USES_CONVERSION_EX;
            LPCSTR szIdentifier = T2A_EX(T::GetIdentifier(),0);
            SIZE_T dwIdentifier = strlen(szIdentifier);
            LPSTR szAddress = pszFile + dwIdentifier;
            
            CONST CHAR szFormat[] = { "%p/%IX" };
            LPVOID lpAddr = NULL; SIZE_T dwSize = 0;
            sscanf(szAddress, szFormat, &lpAddr, &dwSize);
            CHK_EXP_RET( IsBadReadPtr(lpAddr, dwSize) , (INT_PTR)-1);
            CHK_EXP_RET( dwSize <= FALSE , (INT_PTR)-1);
            return InterOpenMemory(lpAddr, dwSize);
        }

static UINT InterRead(INT_PTR hFile, LPVOID lpMemory, UINT nLength)
        {
            MemoryInfo* lpInfo = (MemoryInfo*)(hFile);
            SIZE_T nMemory = lpInfo->dwBuffer - lpInfo->dwOffset;
            SIZE_T dwCopy = nMemory < nLength ? nMemory : nLength;
            LPBYTE lpCopy = lpInfo->lpBuffer + lpInfo->dwOffset;
            CopyMemory(lpMemory, lpCopy, dwCopy);
            lpInfo->dwOffset += dwCopy;
            return dwCopy;
        }

static UINT InterWrite(INT_PTR hFile, LPVOID lpMemory, UINT nSize)
        {
            return UINT_MAX; // errno = ENOSPC;
        }

static LONG InterSeek(INT_PTR hFile, LONG nDistance, INT nSeekType)
        {
            MemoryInfo* lpMemInfo = (MemoryInfo*)(hFile);
            
            LONG_PTR dwOffset = (LONG_PTR)lpMemInfo->dwOffset;
            LONG_PTR dwBuffer = (LONG_PTR)lpMemInfo->dwBuffer;

if(nSeekType == SEEK_END) dwOffset = dwBuffer + nDistance;
            else if(nSeekType == SEEK_CUR) dwOffset += nDistance;
            else if(nSeekType == SEEK_SET) dwOffset = nDistance;

CHK_EXP_RET(dwOffset > dwBuffer ,(errno = EBADF, -1));
            CHK_EXP_RET(dwOffset < 0 ,(errno = EBADF, -1));

lpMemInfo->dwOffset = (SIZE_T)dwOffset;
            return dwOffset ;
        }

static INT InterClose(INT_PTR hFile)
        {
            MemoryInfo* lpMemInfo = (MemoryInfo*)(hFile);
            delete lpMemInfo;
            return 0 ;
        }

public:
        // 因为只存储 CABINET 文件指针,所以使用简单数组方式的映射
        // 映射的键是文件指针,值可以由继承类自己决定如何使用
        typedef ATL::CSimpleMap< INT_PTR , INT_PTR > CMapFilePtr;

static BOOL IsCabinetFilePtr(INT_PTR hFile)
        {
            CMapFilePtr& rMapper = GetFileMapper();
            return rMapper.FindKey(hFile) != -1;
        }

static CMapFilePtr& GetFileMapper()
        {
            static CMapFilePtr sxMapFilePtr;
            return sxMapFilePtr;
        }

public:
        static INT_PTR Open(LPSTR pszFile, INT nOpenFlag, INT nMode)
        {
            if( IsCabinetFileName(pszFile) )
            {
                INT_PTR hFile = T::InterOpen(pszFile, nOpenFlag, nMode);
                GetFileMapper().Add(hFile, 0);
                return hFile;
            }

return CExtractImpl<T>::Open(pszFile, nOpenFlag, nMode);
        }

static UINT Read(INT_PTR hFile, LPVOID lpMemory, UINT nSize)
        {
            CHK_EXP_RET( IsCabinetFilePtr(hFile) , T::InterRead(hFile, lpMemory, nSize) );
            return CExtractImpl<T>::Read(hFile, lpMemory, nSize);
        }

static UINT Write(INT_PTR hFile, LPVOID lpMemory, UINT nSize)
        {
            CHK_EXP_RET( IsCabinetFilePtr(hFile) , T::InterWrite(hFile, lpMemory, nSize) );
            return CExtractImpl<T>::Write(hFile, lpMemory, nSize);
        }

static LONG Seek(INT_PTR hFile, LONG nDistance, INT nSeekType)
        {
            CHK_EXP_RET( IsCabinetFilePtr(hFile) , T::InterSeek(hFile, nDistance, nSeekType) );
            return CExtractImpl<T>::Seek(hFile, nDistance, nSeekType);
        }

static INT Close(INT_PTR hFile)
        {
            if( IsCabinetFilePtr(hFile) )
            {
                INT nClose = T::InterClose(hFile);
                GetFileMapper().Remove(hFile);
                return nClose;
            }
            return CExtractImpl<T>::Close(hFile);
        }
    };

//==============================================================================================
    //    从资源中解压缩 CAB 文件
    template <typename T> class CStorageImpl : public CExpressImpl<T>
    {
    public:
        BOOL ExtractResource(LPCTSTR szTargetDir, LPCTSTR szResName, LPCTSTR szResType = RT_RCDATA, LPCTSTR szModule = NULL)
        {
            TCHAR szResource[MAX_PATH] = { TEXT("") };
            CHK_EXP_RET( !MakeResourceName(szResource,MAX_PATH,szResName,szResType,szModule) , FALSE);
            return ExtractFile(szResource, szTargetDir);
        }

BOOL ExtractResource(LPCTSTR szTargetDir, UINT nResID = 1, LPCTSTR szResType = RT_RCDATA, LPCTSTR szModule = NULL)
        {
            return ExtractResource(szTargetDir, MAKEINTRESOURCE(nResID), szResType, szModule);
        }

public:
        static LPCTSTR GetIdentifier()
        {
            return TEXT("RES://");
        }

// 使用该函数构筑一个存储于资源中的压缩包的名称
        static BOOL MakeResourceName(LPTSTR szCabinet, DWORD dwLength, LPCTSTR lpszResName, LPCTSTR lpszResType = RT_RCDATA, LPCTSTR lpszModule = NULL)
        {
            TCHAR szResource[MAX_PATH] = { TEXT("") }; // HRES:\\Type\Name\Module

LPTSTR lpStr = szResource;
            if( IS_INTRESOURCE(lpszResType) )
            {
                wsprintf(lpStr, TEXT("#%d/"), PtrToInt(lpszResType));
            }
            else
            {
                lstrcpy(lpStr, lpszResType);
                lstrcat(lpStr, TEXT("/"));
            }

lpStr = lpStr + lstrlen(lpStr);
            if( IS_INTRESOURCE(lpszResName) )
            {
                wsprintf(lpStr, TEXT("#%d"), PtrToInt(lpszResName));
            }
            else
            {
                lstrcpy(lpStr, lpszResName);
            }
            
            lpStr = lpStr + lstrlen(lpStr);
            if( lpszModule != NULL )
            {
                lstrcpy(lpStr, TEXT("/"));
                lstrcat(lpStr, lpszModule);
            }

return MakeCabinetName(szCabinet, dwLength, szResource);
        }

public:
        static INT_PTR InterOpenResource(LPCSTR szName, LPCSTR szType, LPCSTR szModule = NULL)
        {
            HMODULE hModule = GetModuleHandleA(szModule);
            HRSRC hResource = FindResourceA(hModule, szName, szType);
            CHK_EXP_RET( hResource == NULL , (INT_PTR)-1 );

HGLOBAL hGlobal = LoadResource(hModule, hResource);
            CHK_EXP_RET( hGlobal == NULL , (INT_PTR)-1 );

LPVOID lpBuffer = LockResource(hGlobal);
            SIZE_T dwBuffer = SizeofResource(hModule, hResource);
            return InterOpenMemory(lpBuffer, dwBuffer, ULONG_MAX);
        }

public:
        static INT_PTR InterOpen(LPSTR pszFile, INT nOpenFlag, INT nMode)
        {
            USES_CONVERSION_EX;
            LPCSTR szIdentifier = T2A_EX(T::GetIdentifier(),0);
            SIZE_T dwIdentifier = strlen(szIdentifier);

CHAR szFile[MAX_PATH] = { "" };
            strcpy(szFile, pszFile + dwIdentifier);

LPSTR szType = szFile;
            LPSTR szName = strchr(szType, '/');
            CHK_EXP_RET(szName == NULL, -1);
            szName[0] = 0; szName ++;

LPSTR szModule = strchr(szName, '/');
            if(szModule != NULL){ *szModule = 0; szModule++; }
            return InterOpenResource(szName, szType, szModule);
        }
    };

//==============================================================================================
    class CFileExpress : public CExpressImpl< CFileExpress >
    {
    public:
        // 从内存中进行解压缩
        BOOL ExtractFromMemory(LPCTSTR szTarget, LPVOID lpBuffer, SIZE_T dwBuffer)
        {
            TCHAR szCabinet[MAX_PATH] = { TEXT("") };
            MakeMemoryName(szCabinet, MAX_PATH, lpBuffer, dwBuffer);
            return ExtractFile(szCabinet, szTarget);
        }
    };

//==============================================================================================
    class CFileStorage : public CStorageImpl< CFileStorage >
    {
    public:
        // 从资源中进行解压缩
        BOOL ExtractFromResource(LPCTSTR szTarget, LPCTSTR szResName, LPCTSTR szResType = RT_RCDATA, LPCTSTR szModule = NULL)
        {
            TCHAR szCabinet[MAX_PATH] = { TEXT("") };
            MakeResourceName(szCabinet, MAX_PATH, szResName, szResType, szModule);
            return ExtractFile(szCabinet, szTarget);
        }
    };
}

转载于:https://www.cnblogs.com/WonKerr/archive/2009/03/17/CabinetImpl.html

如何创建 CAB 文件和如何从文件、内存和资源中解压缩 CAB 文件相关推荐

  1. R语言write.xlsx函数将数据写入Excel文件:写入Excel文件并自定义表单的名称、将数据写入Excel文件新的表单(sheet)中、将文件保存为xls文件格式(而不是xlsx)

    R语言write.xlsx函数将数据写入Excel文件:写入Excel文件并自定义表单的名称.将数据写入Excel文件新的表单(sheet)中.将文件保存为xls文件格式(而不是xlsx) 目录

  2. sd卡有多个android文件夹,android - 如何adb拉出SD卡中存在的文件夹的所有文件

    android - 如何adb拉出SD卡中存在的文件夹的所有文件 我的SD卡中有一个文件夹:/mnt/sdcard/Folder1/Folder2/Folder3/*.jpg Folder1和Fold ...

  3. vscode中如何创新建php文件,php – 如何在Visual Studio Code,UNIX中的所有文件中创建所有行结尾(EOL)?...

    我使用Windows 10 home,我通常使用Visual Studio Code(VSCODE)来编辑Linux Bash脚本以及PHP和JavaScript. 我没有开发任何专门用于Window ...

  4. 黄聪:C# MP3操作类,能播放指定的mp3文件,或播放嵌入的资源中的Mp3文件

    以下为我写的Mp3操作类源代码: using System; using System.Collections.Generic; using System.Linq; using System.Tex ...

  5. java 删除压缩zip文件_从ZIP存档中删除文件,而无需在Java或Python中解压缩 - java...

    从ZIP存档中删除文件,而无需使用Java(首选)或Python解压缩 你好 我使用包含数百个高度压缩的文本文件的大型ZIP文件.解压缩ZIP文件时,可能要花一些时间,并且很容易消耗多达20 GB的磁 ...

  6. git 还原文件到其他版本_如何在Git中还原旧文件版本

    git 还原文件到其他版本 读: 第1部分:什么是Git? 第2部分:Git入门 第3部分:创建第一个Git存储库 第4部分:如何在Git中还原旧文件版本 第5部分:3个用于Git的图形工具 第6部分 ...

  7. java递归删除文件夹_如何使用递归删除Java中的目录/文件夹

    java递归删除文件夹 Earlier we learned how to create a file in java and how to delete a file in java. Here w ...

  8. abaqus dat文件 matlab_工程师联盟科普系列|ABAQUS中的各种文件类型详解-dat文件如何打开...

    ABAQUS产生几类文件:有些是在运行是产生,运行后自动删除:其它一些用于分析.重启.后处理.结果转换或其它软件的文件则被保留,详细如下: 1. model_database_name.cae 模型信 ...

  9. Android中从assets资源中读取图片文件并保存到内部存储器并加载显示在ImageView中

    场景 Android系统为每个新设计的程序提供了/assets目录,这个目录保存的文件可以打包在程序里./res和/assets的不同点是,android不为/assets下的文件生成ID.如果使用/ ...

最新文章

  1. CentOS7系统下bower 命令权限问题
  2. 重磅!2020年度人类社会发展十大科学问题发布
  3. 中国海上风力发电行业战略调研与投资风险分析报告2022-2028年
  4. Python有哪些是必须学的运算符?
  5. logback 的 access 模块
  6. csp-s模拟测试41「夜莺与玫瑰·玫瑰花精·影子」
  7. 【APICloud系列|19】上架APPStore需要准备哪些材料?
  8. 如何实现把固定内容自动写入excel_Excel批量查找指定内容并导出
  9. excel html modify,Modify excel cell
  10. cacti+nagios整合(未成)
  11. Ajax开发框架(下)[整理]
  12. JAVA—字符串怎么转换成整数
  13. 电子政务互联互通软件平台的体系架构
  14. 《动森》为何流行:“我就是想找个地方待着”
  15. toft 测试用例rat_测试案例如何区分RAT,FAST,TOFT,FET | 学步园
  16. 计算机连接未识别的网络,电脑网络连接处出现未识别的网络无Internet访问的解决办法...
  17. 产品隐私政策与使用条款
  18. 计算机网络有哪些分类方式,计算机网络有哪些分类?
  19. 2017年武昌工学院计算机基础,武昌理工学院2017.doc
  20. tekton task资源

热门文章

  1. svn服务端工具VisualSVN Server
  2. 【智能卡】智能卡之SWP
  3. 公众号的发文和留言都会显示城市了!来试试!
  4. tcpdump arping nsenter
  5. CSI笔记【2】:正交频分多路复用技术/OFDM
  6. 10个SaaS的常见问题解答告诉你SaaS是什么
  7. Thonny链接pico报错Device is busy or does not respond. Your options: wait until it completes current work
  8. Linux 内核构建
  9. 单片机实现PT2262解码原理
  10. WINCE 注册表修改