在开写解压代码之前,我们要先知道尾部数据的位置:
我本来是准备直接写入尾部一个区段,
然后在区段中设置大小或Flags来确定是否自己添加的.(和NSIS一样)
但是在这里, 为了方便, 我选择直接写入文件尾部数据,
并且假设这个文件并没有任何的没有头信息的已添加区段
但是为了兼容之后的代码,仍然选择通过PE格式来获取最后区段所在的位置来确定尾部数据位置
先定义几个尾部数据的结构 (写入时也要按此结构写入):
struct addedSector
{
DWORD verifycode; // 0
char compressType[10]; // 压缩方式
char programName[20]; // 安装包的程序名
char autorun[150]; // 自动运行程序的相对路径
int nfiles; // 总文件数量
//
// +sizeof(addedSector) 之后就是每个文件块 fileblock
};
struct fileblock
{
char relativepath[150]; // 临时文件=".tmp" or 相对路径 : aa/bb/
char filename[50]; // 文件名 : a.dll >> 那么这个文件的路径: setuppath/aa/bb/a.dll
DWORD filesize; // 大小
//
// +sizeof(fileblock) 之后就是文件内容 byte* filebuf
};
设置的数据很少,但是够用了.
addedSector结构就是刚开始时说到的'安装包信息'的结构体
这个结构的后面紧接着就是连续的每个文件块.
文件块遍历的话 直接使用 filesize+sizeof(fileblock) 即可
模板EXE就可以按照上面的结构, 找到尾部结构:
// 简单的初始化PE信息的封装类
ctPEFile::PEFile selfpe;
// 获取被添加的内存所在位置
addedSector* getAddedSector()
{
char tmp[260];
GetModuleFileNameA( NULL, tmp, 260 );
if(selfpe.loadfile( tmp ))
{
// 越过最后一个区段,就是添加数据的位置
PIMAGE_SECTION_HEADER lastsec = selfpe.secheader + selfpe.seccount - 1;
return (addedSector*)(selfpe.filebuf + lastsec->PointerToRawData + lastsec->SizeOfRawData);
}
return nullptr;
}
获取尾部并且初始化结构中的信息后, 下一步就是解压了,
而解压最重要的是遍历文件块(fileblock):
// 获取指定的文件块位置
fileblock* getFileBlock( int ifile )
{
if(ifile < addedsec->nfiles)
{
// 先到第一个
fileblock* fb = (fileblock*)((DWORD)addedsec + sizeof( addedSector ));
// 一直遍历到指定的位置
for(int i = 0; i < ifile; i++)
fb = (fileblock*)((DWORD)fb + fb->filesize + sizeof( fileblock ));
return fb;
}
return nullptr;
}
拿到文件块之后就很简单了,直接写入文件内容(filebuf)到文件中即可:
//
// if relativepath exist, create folders
//
char dir[260];
wsprintfA( dir, "%s\\%s", setuppath.c_str(), fb->relativepath );
SHCreateDirectoryExA( NULL, dir, NULL );
wsprintfA( extractPath, "%s\\%s\\%s", setuppath.c_str(), fb->relativepath, fb->filename );
//
writetoexfile ( filebuf , fb-> filesize );
再给这个解压函数加上一些额外的功能, 比如界面显示,临时文件释放等.
解压函数就可以正常运作了
//
// 释放文件到指定位置
//
void extractFile( int ifile )
{
fileblock* fb = getFileBlock( ifile );
if(fb)
{
// 创建要释放的目录/确定文件全路径
char extractPath[260] = {0};
if(strcmp( fb->relativepath, ".tmp" ) == 0)
{
// 临时文件,直接释放到临时目录
GetTempPathA( 260, extractPath );
strcat( extractPath, fb->filename );
}
else
{
if(fb->relativepath[0])
{
//if relativepath exist, create folders
char dir[260];
wsprintfA( dir, "%s\\%s", setuppath.c_str(), fb->relativepath );
SHCreateDirectoryExA( NULL, dir, NULL );
wsprintfA( extractPath, "%s\\%s\\%s", setuppath.c_str(), fb->relativepath, fb->filename );
}
else
{
wsprintfA( extractPath, "%s\\%s", setuppath.c_str(), fb->filename );
}
}
// 界面上显示当前解压文件的名称
ctd.setText( "showpath", extractPath );
// 这里直接写入文件, 并没有解压相关 , 请去完整代码中查看
byte* filebuf = (byte*)((DWORD)fb + sizeof( fileblock ));
//
FILE *f = fopen(extractPath,"wb");
if(f)
{
fwrite( filebuf, 1, fb->filesize, f );
fclose( f );
}
}
}
下一篇: 手撸一个安装包制作工具(5) --生成器/项目链接