制作 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);
// 概述:
// 根据指定的程序和 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;
}
{
// 复制安装程序到目标路径处
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);
}
};
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// 概述:
// 基于系统函数的文件压缩解压缩包实现类
//
// 日期:
// 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);
}
};
}