前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >7z 自解压读取 config.txt 配置的代码实现

7z 自解压读取 config.txt 配置的代码实现

作者头像
我与梦想有个约会
发布于 2020-01-04 09:42:24
发布于 2020-01-04 09:42:24
2.3K00
代码可运行
举报
文章被收录于专栏:jiajia_dengjiajia_deng
运行总次数:0
代码可运行

7z 自解压功能,实际是将三个文件连接在一起,第一个文件是 7z 的自解压模块(实际是一个通用的包含了界面界面的应用程序)+ config.txt(配置文件)+ 实际要解压的 7z 压缩包文件。三个文件通过 Windows 的 copy 命令拼接在一起,你也可以自己实现代码,将三个文件拼接在一起,因为第一个文件的首地址 PE 结构不变,所以当程序运行时相当于运行了 7z 的自解压模块。他们的组成如下图:

图中可以看的出来,使用 copy /b 将三个文件连接在了一起,我们需要在自解压的模块程序 7z_sfx.exe 中实现读取查找 config.txt 文件的位置和内容,从而也就可以得到自解压文件的起始位置。这样在解压文件的时候将包装在我们程序中的自解压程序起始地址传递进去就可以了。

前提条件

首先要在代码中找到被追加进自己程序的 config.txt 文件内容,config.txt 必须要有一个标识来记录文件的开头和结束,这样我们才知道这个文件中间的内容,参考 7z 自解压模块的代码,其在 config.txt 头部和尾部分别设计了两个标识,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
;!@Install@!UTF-8!
Title="SOFTWARE v1.0.0.0"
BeginPrompt="Do you want to install SOFTWARE v1.0.0.0?"
RunProgram="setup.exe"
;!@InstallEnd@!

在程序中只要将程序一块一块的读取到内存,对比每一个字节如果存在 ;!@Install@!UTF-8! 就是 config 文件的开头,存在 ;!@InstallEnd@! 就是 config 文件的结尾。这样中间的内容也就确定了,文件结尾的位置就是 7z 压缩包文件的开头。

实现代码

代码实现起来要考虑的内容还是比较多的,我参考了 7z 的代码从头实现了一遍,对每一个变量都做了作用注释,因为 7z 官方的代码一个注释都没有,看起来很难懂,索性就参考他的思路一点一点重写了一遍。调用 FindSignature 方法就可以查找到 config.txt 中的内容了,用 strOutput 参数将内容传出。

程序编译完成后,使用 copy /b 程序名 + 带有标记的 config.txt 就可以测试出效果,自己再加上解压的代码你就可以实现一个属于自己的自解压模块了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
#include <fstream>
#include <windows.h>

static char kBeginSignature[] = { ';','!','@','I','n','s','t','a','l','l','@','!','U','T','F','-','8','!', 0 };
static char kEndSignature[] = { ';','!','@','I','n','s','t','a','l','l','E','n','d','@','!', 0 };

bool FindSignature(const std::string& strBeginSignature, const std::string& strEndSignature, std::string& strOutput)
{
    const size_t nFixedBufferSize = (1 << 12);
    char szApplication[MAX_PATH] = { 0 };
    GetModuleFileNameA(NULL, szApplication, MAX_PATH);

    FILE* hFile = NULL;
    fopen_s(&hFile, szApplication, "rb");

    // 标记是否找到头部
    bool bFoundBegin = false;

    // 记录需要跳过多少个字节(上一次读取长度不足的内容会被填充到当前 buffer 中)
    size_t nBytesPrev = 0;

    BYTE szBuffer[nFixedBufferSize] = { 0 };

    for (;;)
    {
        size_t nReadSize = nFixedBufferSize - nBytesPrev;
        size_t nProcessedSize = fread(szBuffer + nBytesPrev, 1, nReadSize, hFile);
        if (nProcessedSize == 0)
            return false;

        // 上一次读取剩余的字节 + 本次读取到的字节总数
        size_t nTotalSize = nBytesPrev + nProcessedSize;

        // 标记读取出来的内存块中已经对比的数据位置
        size_t nPos = 0;

        for (;;)
        {
            if (!bFoundBegin)
            {
                // 剩余长度不足头部内容,直接跳出
                if (nPos > nTotalSize - strBeginSignature.size())
                    break;

                // 标记已经找到头部,找到头部后将读取指针移动到头部关键字末尾
                if (memcmp(szBuffer + nPos, strBeginSignature.c_str(), strBeginSignature.size()) == 0)
                {
                    bFoundBegin = true;
                    nPos += strBeginSignature.size();
                }
                else
                {
                    nPos++;
                }
            }
            else
            {
                // 剩余长度不足尾部内容,直接跳出
                if (nPos > nTotalSize - strEndSignature.size())
                    break;

                // 如果找到末尾则直接返回
                if (memcmp(szBuffer + nPos, strEndSignature.c_str(), strEndSignature.size()) == 0)
                    return true;

                // 将不是末尾标记的数据追加给传出参数
                BYTE pByte = szBuffer[nPos];

                // 程序中常量字符串末尾是 0,但文件中不是 0,如果读到 0 证明是程序中的常量,而不是文件中的
                if (pByte == 0)
                {
                    bFoundBegin = false;
                    break;
                }
                strOutput += pByte;

                // 向后步进
                nPos++;
            }
        }

        // 记录下次需要跳过的字节数量
        nBytesPrev = nTotalSize - nPos;

        // 将不足以对比的剩余内容拷贝到 buffer 的首位,下次读取的新数据衔接在该数据后面
        memmove(szBuffer, szBuffer + nPos, nBytesPrev);
    }

    if (hFile)
    {
        fclose(hFile);
    }

    return false;
}

int main()
{
    std::string strOutput;
    if (FindSignature(kBeginSignature, kEndSignature, strOutput))
        std::cout << strOutput.c_str() << std::endl;
    else
        std::cout << "Not found..." << std::endl;

}

相关

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019年4月6日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
String的长度有限,而我对你的思念却无限延伸
C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。
Undoom
2025/05/29
780
String的长度有限,而我对你的思念却无限延伸
C/C++ Zlib实现文件压缩与解压
在软件开发和数据处理中,对数据进行高效的压缩和解压缩是一项重要的任务。这不仅有助于减小数据在网络传输和存储中的占用空间,还能提高系统的性能和响应速度。本文将介绍如何使用 zlib 库进行数据的压缩和解压缩,以及如何保存和读取压缩后的文件。zlib 是一个开源的数据压缩库,旨在提供高效、轻量级的压缩和解压缩算法。其核心压缩算法基于 DEFLATE,这是一种无损数据压缩算法,通常能够提供相当高的压缩比。zlib 库广泛应用于多个领域,包括网络通信、文件压缩、数据库系统等。
王 瑞
2023/11/29
3K0
C/C++ Zlib实现文件压缩与解压
【C++】模拟实现string类
https://blog.csdn.net/weixin_72357342/article/details/136852268?spm=1001.2014.3001.5502 而在本次项目中我们的目标是模拟实现一个string类: 该string包含四个成员变量,分别是:
修修修也
2024/05/30
2810
【C++】模拟实现string类
PaddleOCR C++动态库编译及调用识别(一)
本篇就来看看如何把PaddleOCR的源码重新编译成动态库,供OpenCV的Demo调用。
Vaccae
2021/07/30
4.9K1
PaddleOCR C++动态库编译及调用识别(一)
string的模拟全实现
前面我们学习了string的用法,本节我们将实现string的模拟实现,话不多说,直接上手,因此我们先了解我们是多文件进行编写,因此需要注意命名空间的控制,这是文件分布图:OK,我们开始~
学习起来吧
2024/05/16
1460
string的模拟全实现
C++ MiniZip实现目录压缩与解压
Zlib是一个开源的数据压缩库,提供了一种通用的数据压缩和解压缩算法。它最初由Jean-Loup Gailly和Mark Adler开发,旨在成为一个高效、轻量级的压缩库,其被广泛应用于许多领域,包括网络通信、文件压缩、数据库系统等。其压缩算法是基于DEFLATE算法,这是一种无损数据压缩算法,通常能够提供相当高的压缩比。
王 瑞
2023/11/23
1.5K0
C++ MiniZip实现目录压缩与解压
C/C++ Zlib库封装MyZip压缩类
Zlib是一个开源的数据压缩库,提供了一种通用的数据压缩和解压缩算法。它最初由Jean-Loup Gailly和Mark Adler开发,旨在成为一个高效、轻量级的压缩库,其被广泛应用于许多领域,包括网络通信、文件压缩、数据库系统等。其压缩算法是基于DEFLATE算法,这是一种无损数据压缩算法,通常能够提供相当高的压缩比。
王 瑞
2023/11/30
5540
C/C++ Zlib库封装MyZip压缩类
C++在线五子棋对战(网页版)项目:实用工具类模块代码实现
#ifndef _M_LOGGER_H_  和 #define _M_LOGGER_H_是预处理器指令,用于防止头文件被重复包含。如果头文件已经被包含了,那么这对指令将会被跳过。
二肥是只大懒蓝猫
2023/10/13
3880
C++在线五子棋对战(网页版)项目:实用工具类模块代码实现
【c++】string类模拟实现
我们stl库中的string类实在std命名空间的,这里我们自定义一个命名空间own,包含string类和简单的成员变量:
用户11029103
2024/04/20
990
【c++】string类模拟实现
Linux C++简单实现一个批量插入的sql脚本生成工具
之前项目上隔三差五地要手工批量导入一些数据,于是自己用C++写了这个sql脚本文件的生成工具。
typecodes
2024/03/29
1410
Linux C++简单实现一个批量插入的sql脚本生成工具
STL&string&模拟实现
STL(standard template library-标准模板库):是C++标准库的重要组成部分,不仅是一个可以复用的库,而且是一个包罗数据结构与算法的软件框架。
用户10551528
2024/03/18
920
STL&string&模拟实现
【c++丨STL】string模拟实现(附源码)
之前我们学习了STL的第一个容器--string及其常用接口的使用方法:
ephemerals__
2024/10/24
1390
【c++丨STL】string模拟实现(附源码)
【c++】string类常见接口函数
字符串类是basic_string类模板的一个实例化,它使用char(即字节)作为其字符类型,并使用默认的char_traits和分配器类型
用户11029103
2024/04/16
1650
【c++】string类常见接口函数
基于protobuf的代码生成
前段时间我用 Python 和 Mako 模板引擎重新梳理了我们项目中的一些重复的流程。重构了所有的RPC系统。这个工作其实完成了挺久了,但是迫于懒一直拖着没写完这篇记录,就一直没发。
owent
2021/02/24
1.6K0
基于protobuf的代码生成
C++中string的底层实现,关于string的一切在你面前轻松拿捏
C++ 中的 std::string 是最常用的数据结构之一。然而,深入了解它的底层实现机制,可以显著提升你对内存管理和数据操作的理解。
用户11286421
2024/10/15
1310
【C++】string类的模拟实现
1. 库里面的构造函数实现了多个版本,我们这里就实现最常用的参数为const char *的版本,为了同时支持无参的默认构造,这里就不在多写一个无参的默认构造,而是用全缺省的const char *参数来替代无参和const char *参数的两个构造函数版本。
举杯邀明月
2023/04/12
6730
【C++】string类的模拟实现
string类(下)(模拟实现string类,深度剖析其底层)
断言(Assertion)是编程中一种常用的调试辅助手段,用于在代码执行期间验证某个条件是否为真。如果条件为真(即满足预期),则程序继续执行;如果条件为假(即不满足预期),则断言失败,通常会导致程序抛出一个错误、输出一条错误信息,甚至直接终止程序。断言主要用于开发和测试阶段,以确保代码的正确性和健壮性。
suye
2024/10/16
1270
HTTP协议代码实现
本篇博客我们来讲如何实现一个HTTP,此篇涉及前端和后端。快快准备好小板凳来听吧!!!
小灵蛇
2024/10/01
1230
HTTP协议代码实现
7z软件指南(压缩解压工具)
在 Linux 系统下,可通过输入特定命令轻松安装 7-Zip,以下针对不同发行版给出具体指令:
是山河呀
2025/02/02
4150
【C++之STL】一文学会使用 string
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
fhvyxyci
2024/11/11
2280
【C++之STL】一文学会使用 string
相关推荐
String的长度有限,而我对你的思念却无限延伸
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验