// LibExtract.cpp
#include "stdafx.h"
#include "stdio.h"
#include <windows.h>
#include "LibExtract.h" // LAST IN ORDER !!
#pragma warning(disable: 4996)
// Workaround for Compiler Bug: Error C2039 when using Directory::CreateDirectory()
#undef CreateDirectory
using namespace System;
using namespace System::IO;
using namespace System::Text;
using namespace System::Reflection;
using namespace System::Runtime::InteropServices; // Marshal
namespace CabLib
{
// Constructor
CabLib::Extract::Extract()
{
mp_This = 0;
ms_SingleFile = 0;
ms_Decrypt = "";
}
void CabLib::Extract::SetDecryptionKey(String* s_Key)
{
ms_Decrypt = s_Key;
}
// Extract only one single file from the archive.
// This file MUST be located in the root folder of the CAB archive !!!
// For splitted CAB files the file MUST be in the FIRST archive!
// After setting a single file the event evBeforeCopyFile will NOT be fired !!!
// You must set the single file before calling
// ExtractFile() or ExtractResource() or ExtractStream()
// The single file will only be valid for ONE extraction process !!!
void CabLib::Extract::SetSingleFile(String* s_File)
{
ms_SingleFile = s_File;
}
// Extracts the content of a CAB file s_CabFile into the given folder s_Folder
// The subfolder structure in the CAB file will be maintained on disk
// Also file attributes and dates of the files are maintained
// Throws an exception on error
void CabLib::Extract::ExtractFile(String* s_CabFile, String* s_Folder)
{
mi_Mutex->WaitOne(); // assure thread-safety
Exception *i_Ex = 0;
ms_Folder = s_Folder;
mp_This = this;
WCHAR* u16_CabFile = (WCHAR*)Marshal::StringToHGlobalUni( s_CabFile).ToPointer();
WCHAR* u16_Folder = (WCHAR*)Marshal::StringToHGlobalUni( s_Folder).ToPointer();
WCHAR* u16_Decrypt = (WCHAR*)Marshal::StringToHGlobalUni(ms_Decrypt).ToPointer();
CExtract i_Extract;
if (!i_Extract.SetDecryptionKeyW(u16_Decrypt))
{
i_Ex = new Exception("CabLib Extract ERROR: Decryption key too long.");
goto _Exit;
}
if (!i_Extract.CreateFDIContext())
{
i_Ex = new Exception(String::Format("CabLib Extract ERROR: Could not create FDI context: {0}",
new String(i_Extract.LastErrorW())));
goto _Exit;
}
if (!i_Extract.ExtractFileW(u16_CabFile, u16_Folder))
{
i_Ex = new Exception(String::Format("CabLib Extract ERROR while extracting files from cabinet archive: {0}",
new String(i_Extract.LastErrorW())));
goto _Exit;
}
_Exit:
Marshal::FreeHGlobal(u16_CabFile);
Marshal::FreeHGlobal(u16_Folder);
Marshal::FreeHGlobal(u16_Decrypt);
mp_This = 0;
ms_SingleFile = 0;
mi_Mutex->ReleaseMutex(); // The mutex must ALWAYS be released!!
if (i_Ex) throw i_Ex;
}
// Extracts the content of a CAB file which is stored in the file s_Module (DLL or EXE)
// into the given folder s_Folder.
// If s_Module == null --> The Cab resource is expected in the file which created the process
// Otherwise s_Module must be the filename (without path) of the DLL which contains the CAB resource
// The subfolder structure in the CAB file will be maintained on disk
// Also file attributes and dates of the files are maintained
// Throws an exception on error
// To test this function with the file Test.cab included in this project
// set s_Module = "CabLib.dll" and o_RscName = ID_CAB_TEST and o_RscType = "CABFILE"
// because the file CabLib.rc contains:
// ID_CAB_TEST CABFILE "Res\\Test.cab"
void CabLib::Extract::ExtractResource(String* s_Module, // filename of DLL or null
Object* o_RscName, // Name of resource (String or integer)
Object* o_RscType, // Type of resource (String or integer)
String* s_Folder) // Where to extract the CAB to
{
mi_Mutex->WaitOne(); // assure thread-safety
Exception *i_Ex = 0;
ms_Folder = s_Folder;
mp_This = this;
WCHAR* u16_Module = 0;
WCHAR u16_RscName[1000] = L"";
WCHAR u16_RscType[1000] = L"";
if (s_Module) u16_Module = (WCHAR*)Marshal::StringToHGlobalUni(s_Module).ToPointer();
WCHAR* u16_Folder = (WCHAR*)Marshal::StringToHGlobalUni( s_Folder). ToPointer();
WCHAR* u16_Decrypt = (WCHAR*)Marshal::StringToHGlobalUni(ms_Decrypt).ToPointer();
WCHAR* u16_ObjName = (WCHAR*)Marshal::StringToHGlobalUni(o_RscName->ToString()).ToPointer();
WCHAR* u16_ObjType = (WCHAR*)Marshal::StringToHGlobalUni(o_RscType->ToString()).ToPointer();
if (o_RscName->GetType() == __typeof(Int32)) swprintf(u16_RscName, L"#%s", u16_ObjName);
else if (o_RscName->GetType() == __typeof(String)) wcscpy (u16_RscName, u16_ObjName);
if (o_RscType->GetType() == __typeof(Int32)) swprintf(u16_RscType, L"#%s", u16_ObjType);
else if (o_RscType->GetType() == __typeof(String)) wcscpy (u16_RscType, u16_ObjType);
CExtractResource i_Extract;
if (!i_Extract.SetDecryptionKeyW(u16_Decrypt))
{
i_Ex = new Exception("CabLib Extract ERROR: Decryption key too long.");
goto _Exit;
}
if (!i_Extract.CreateFDIContext())
{
i_Ex = new Exception(String::Format("CabLib Extract ERROR: Could not create FDI context: {0}",
new String(i_Extract.LastErrorW())));
goto _Exit;
}
if (!i_Extract.ExtractResourceW(u16_Module, u16_RscName, u16_RscType, u16_Folder))
{
i_Ex = new Exception(String::Format("CabLib Extract ERROR while extracting files from cabinet resource: {0}",
new String(i_Extract.LastErrorW())));
goto _Exit;
}
_Exit:
if (s_Module) Marshal::FreeHGlobal(u16_Module);
Marshal::FreeHGlobal(u16_Folder);
Marshal::FreeHGlobal(u16_Decrypt);
Marshal::FreeHGlobal(u16_ObjType);
Marshal::FreeHGlobal(u16_ObjName);
mp_This = 0;
ms_SingleFile = 0;
mi_Mutex->ReleaseMutex(); // The mutex must ALWAYS be released!!
if (i_Ex) throw i_Ex;
}
// Extracts the content of a stream, which contains a CAB file, into the given folder s_Folder
// The subfolder structure in the CAB file will be maintained on disk
// Also file attributes and dates of the files are maintained
// Throws an exception on error
void CabLib::Extract::ExtractStream(Stream* i_Stream, String* s_Folder)
{
if (!i_Stream->CanSeek || !i_Stream->CanRead)
throw new Exception("The stream specified for CAB extraction is invalid! The stream must be capable of reading and seeking.");
mi_Mutex->WaitOne(); // assure thread-safety
Exception *i_Ex = 0;
mi_Stream = i_Stream;
ms_Folder = s_Folder;
mp_This = this;
WCHAR* u16_Folder = (WCHAR*)Marshal::StringToHGlobalUni( s_Folder). ToPointer();
WCHAR* u16_Decrypt = (WCHAR*)Marshal::StringToHGlobalUni(ms_Decrypt).ToPointer();
CExtractStream i_Extract;
if (!i_Extract.SetDecryptionKeyW(u16_Decrypt))
{
i_Ex = new Exception("CabLib Extract ERROR: Decryption key too long.");
goto _Exit;
}
if (!i_Extract.CreateFDIContext())
{
i_Ex = new Exception(String::Format("CabLib Extract ERROR: Could not create FDI context: {0}",
new String(i_Extract.LastErrorW())));
goto _Exit;
}
if (!i_Extract.ExtractStreamW(u16_Folder))
{
i_Ex = new Exception(String::Format("CabLib Extract ERROR while extracting files from cabinet stream: {0}",
new String(i_Extract.LastErrorW())));
goto _Exit;
}
_Exit:
Marshal::FreeHGlobal(u16_Folder);
Marshal::FreeHGlobal(u16_Decrypt);
mi_Stream->Close();
mi_Stream = 0;
mp_This = 0;
ms_SingleFile = 0;
mi_Mutex->ReleaseMutex(); // The mutex must ALWAYS be released!!
if (i_Ex) throw i_Ex;
}
// ###############################################################################################
// ###############################################################################################
// EXTRACT EVENT HANDLER
// ###############################################################################################
// ###############################################################################################
// This function will be called for each file in a cabinet before it is extracted.
// Return TRUE to extract the file or FALSE to skip the file.
BOOL CabLib::Extract::OnBeforeCopyFile(kCabinetFileInfo *pk_Info, void* pParam)
{
// Extract single file specified by user from the root folder in the CAB file
if (mp_This && mp_This->ms_SingleFile)
return (String::Compare(mp_This->ms_SingleFile, pk_Info->s_File, true) == 0);
if (mp_This && mp_This->evBeforeCopyFile) // fire event
return (mp_This->evBeforeCopyFile(pk_Info) == true); // bool --> BOOL
return TRUE;
}
// This function will be called when a file has been succesfully extracted.
void CabLib::Extract::OnAfterCopyFile(String* s_File, void* pParam)
{
if (mp_This && mp_This->evAfterCopyFile)
mp_This->evAfterCopyFile(s_File);
}
// This function will be called exactly once for each cabinet opened by Copy,
// including continuation cabinets opened due to files which are spanning over cabinet boundaries.
void CabLib::Extract::OnCabinetInfo(kCabinetInfo *pk_Info, void* pParam)
{
if (mp_This && mp_This->evCabinetInfo)
mp_This->evCabinetInfo(pk_Info);
}
// This function will be called when the next cabinet file in the sequence needs to be opened.
// The path has to be verified and can be changed if necessary.
// If the cabinet file cannot be opened this function will be called again
// with the second parameter set to an error that describes the problem.
void CabLib::Extract::OnNextCabinet(kCabinetInfo *pk_Info, int s32_FdiError, void* pParam)
{
if (mp_This && mp_This->evNextCabinet)
mp_This->evNextCabinet(pk_Info, s32_FdiError);
}
// ###############################################################################################
// ###############################################################################################
// STREAM ACCESS FUNCTIONS
// ###############################################################################################
// ###############################################################################################
int CabLib::Extract::OnStreamRead(void* p_Buffer, long Pos, UINT u32_Count)
{
if (!mi_Stream) return 0;
mi_Stream->Position = Pos;
System::Byte u8_ReadBuf[] = new System::Byte[u32_Count];
int s32_Read = mi_Stream->Read(u8_ReadBuf, 0, u32_Count);
Marshal::Copy(u8_ReadBuf, 0, p_Buffer, s32_Read);
return s32_Read;
}
// returns the total size of bytes to read
int CabLib::Extract::OnStreamGetLength()
{
if (!mi_Stream) return 0;
return (int) mi_Stream->Length;
}
} // Namespace Cabinet