C++ Windows将文件资源打包到exe中并读取出来

        你是否像我一样,喜欢构建出来的exe的总文件数(加上资源文件)尽量少一点,最好只有一个文件,或者有些东西不希望把一些重要的东西放到exe外面,即使加密后也不放心,那么rc与windows api就可以满足你的需求。如果你想像adobe那样,一个程序100多个dll与100以上的资源文件,让你眼花缭乱的话,也没有关系,就当看个乐吧! 

 

目录

基础知识

RC资源文件:

Windows API 函数

正文


基础知识

RC资源文件:

        RC,全称(Resource),它可以被windres.exe编译并将里面的资源打包到exe中,其基本语法如下:

        [resource_id] [Token] [Arguments]

常见的关键字:DIALOG,CTEXT,LTEXT,RTEXT,MENU,ICON

补充:RC还可用于设置打包出来的exe的图标,只需要一行代码:

        [随便一个名字] ICON "icon的路径,后缀为.ico"

另外,网上很多人都说,”随便一个名字“这个变量名字必须是MAIN_ICON才会让exe有图标,但是,实际上只要资源文件中有一个icon文件,那么你的exe的图标必定会改变,就比如这样:

//rc.rc
THIS_IS_NOT_THE_EXE_ICON_PLEASE ICON "secret.ico" //什么秘密呢???

假设secret.ico是一个你不希望出现在exe图标上的图片,但是RC才不管,它会把它默认作为程序图标。(其实这是错的 June 7 2023)

本篇文章主要用到的RC关键字:RCDATA

Windows API 函数

这个不用讲了吧,就是windows.h中的几乎所有函数(除去版本不兼容,如GetConsoleWindow就不兼容win98,95,server 2000)

本次主要用到的windows函数:GetLastError,FindResource,LockResource,SizeofResource,UnlockResource

正文

如果你要打包resource,那么首先,你需要将资源文件信息写入rc中,利用RCDATA你就可以很方便地打包资源,RCDATA语法如下:

        [资源ID/序号] RCDATA "资源地址"

假设你有两个文本来支持中英文,那么程序便可以写成

//res.h
#ifndef RES_H
#define RES_H

#define STEP 11451
#define STEPN(X) ((STEP) + (X))
#define RES_LAN_ENG STEPN(0)
#define RES_LAN_CHI STEPN(1)

#endif
#include "res.h"

RES_LAN_ENG RCDATA "English.l"
RES_LAN_CHI RCDATA "Chinese.l"

具体文件格式如下:

 Chinese.l内容如下

你好世界!

 English.l则为

HelloWorld!

好的,资源有了那么要怎么读出来呢,这时,就需要我们去开发个函数来解决这个问题了,我的函数模型如下。 

/*
 *@Summary 获取资源文件指针(拷贝了)
 *@Arguments:
 * exeInstance:这个程序的句柄GetModuleHandle(NULL)与WinMain中的hInstance入口参数都可以
 * rcName:资源的名字,如果是序号,用 MAKEINTRESOURCE(序号)获取
 * rcType:资源的类型,在这篇文章始终未RT_RCDATA,还可以为RT_ICON,RT_DIALOG等
 * err:错误码
 *@Return:
 * 成功的话返回copy后的数据指针,失败返回NULL
 */
char * GetResourceDataPtr(HMODULE exeInstance,LPCSTR rcName,LPCSTR rcType,int & err);

 那么如何实现呢?

分为以下几步:

1.通过FindResource查找资源并获取句柄

2.通过LoadResource获取资源的HGLOBAL

3.通过LockResource获取资源的指针

4.通过malloc与strncpy复制数据

5.通过UnlockResource释放在Heap中的HGLOBAL

细心的聪明人可能发现了,为什么第五步是斜体呢?等会你就知道为什么它是斜体了!

好的,思路有了,该写函数体了。

如果你想让函数可以抵抗一切可以处理的异常,那么在函数内判断异常并返回十分重要,这里,我定义了一个宏来处理这个情况

//C:Condition
//S:Statements
#define MASSERT(C,S) if((C)){S;}

那么首先,通过FindResource查找资源并获取句柄,所以代码为

FindResource定义如下 

HRSRC FindResourceA(
  HMODULE hModule,
  LPCSTR  lpName,
  LPCSTR  lpType
);
HRSRC rs = FindResource(exeInstance,rcName,rcType);

然后LoadResource

HGLOBAL LoadResource(
  HMODULE hModule,
  HRSRC   hResInfo
);
HGLOBAL glb = LoadResource(exeInstance,rs);

再LockResource

LPVOID LockResource(
  HGLOBAL hResData
);
char * sdata = (char*)LockResource(glb)

然后malloc与strncpy复制数据

_CRTIMP void* __cdecl __MINGW_NOTHROW	malloc	(size_t) __MINGW_ATTRIB_MALLOC;
_CRTIMP char* __cdecl __MINGW_NOTHROW	strncpy (char*, const char*, size_t);
DWORD size = SizeofResource(exeInstance,rs);//Get Resource size
size_t sz = sizeof(BYTE) * (size + 1);
char * data = (char *)malloc(sz);
ZeroMemory(data,sz);
strncpy(data,sdata,sz);

最后,UnlockResource,返回?
NO,NO,NO!

其实根本就不需要UnlockResource,因为当你查看它被定义的地方winbase.h是,你会发现它是这么定义的

#define UnlockResource(h) (h)

当我写代码时发现UnlockResource在Codeblocks中显示是一个宏和mingw编译时警告我这是个没有意义的语句(warning: statement has no effect [-Wunused-value]|)时我就觉得有点不对劲,果然,它是个空包弹!

最后,加上我们的错误判断,一个函数就写好了,当然,为了不要再debug是浪费时间,我还设计了个debug版,这样,就不要资源文件改了,整个exe又要重新构建了

最后的最后,奉上全部源代码:

//ctool.h debug中的fileIO::file_size出自其中,是个c++工具包,需要的话自取
#ifndef CTOOL_INC
#define CTOOL_INC
#include <windows.h>
#include <string>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <sys/stat.h>
#include <math.h>

using namespace std;

#ifdef BUILD_DEBUG
#define O(x) cout << x
#define Oln(x) cout << x << endl
#endif // BUILD_DEBUG

namespace ctk{

    struct TMST0{
        DWORD all;
        DWORD offset;
    };

    //需要链接winmm.a,VC #pragma comment(link,"Winmm.a") CodeBlocks BuildOpition中的linker settings中添加 "winmm" 字符串 
    class Clock{
    public:
        Clock(bool start = true);
        void Start();
        TMST0 Stop();
        DWORD GetALLTime();//Do not set pre time
        DWORD GetOffset();//Set Pre Time
        TMST0 Now();//Do not reset preTime
    private:
        DWORD m_StartTime;
        DWORD m_PreTime;
        bool m_start;
    };
}

namespace strps{
    string GetTranslateString(string in);
    void split(vector<string> & vct,const string & line,const char sep);
    string Trim(string & str);
    void Stringsplit(string str, string splits, vector<string>& res);
    namespace encoding{
        string GBKToUTF8(const string &strGBK);
        string UTF8ToGBK(const string &strUTF8);
    }
}

namespace fileIO{
    int file_size(char* filename);
    bool check_exists(char* filename);
}

namespace num{
    namespace random{
        typedef struct RandomShakeStruct{
            float shakeValue;
            float time;
            float timePerRound;
            float mn;
            float mx;
            float start;
            int stDirection;
            ctk::Clock insideClock;
        } ShakeSt;
        void ShakeInit(ShakeSt & st,float timePerRound,float minv,float maxv,float start,int startDiection = 1);
        //抖动式随机数,目前为beta状态
        void Shake(ShakeSt & st);
    }
    namespace vectors{
        //向量
        struct Vector{
            float x;
            float y;
            float z;
            Vector(float v0,float v1,float v2);
        };
        Vector Normalize(Vector);
    }
}

#endif // CTOOL_INC
//ctool.cpp ctool.h的实现,需要自取,大部分代码为网上找的,只作为一个工具包,没有商业用途
#include "./ctool.h"
#include <iostream>


using namespace std;
using namespace ctk;
using namespace strps;
using namespace fileIO;
using namespace strps::encoding;
using namespace num::random;
using namespace num::vectors;

Clock::Clock(bool start){
    this->m_StartTime = this->m_PreTime = 0;
    this->m_start = false;
    if(start){
        this->Start();
    }
}

void Clock::Start(){
    if(m_start)return;
    this->m_start = true;
    this->m_StartTime = timeGetTime();
}

TMST0 Clock::Now(){
    if(!m_start)return {0,0};
    TMST0 t;
    t.all = timeGetTime() - this->m_StartTime;
    t.offset = timeGetTime() - this->m_PreTime;
    return t;
}

DWORD Clock::GetALLTime(){
    if(!m_start)return 0;
    return Now().all;
}

DWORD Clock::GetOffset(){
    if(!m_start)return 0;
    DWORD off = Now().offset;
    this->m_PreTime = timeGetTime();
    return off;
}

TMST0 Clock::Stop(){
    if(!m_start)return {0,0};
    TMST0 rt = Now();
    this->m_StartTime = 0;
    this->m_start = false;
    return rt;
}

string strps::GetTranslateString(string in){
    string out = "";
    for(size_t i = 0;i < in.length();i++){
        if(in[i] == '\\'){
            switch(in[++i]){
            case 'n'://New Line
                out += '\n';
                break;
            case '\\'://Backslash
                out += '\\';
                break;
            case 'v'://vertical
                out += '\v';
                break;
            case 't'://tab
                out += '\t';
                break;
            case 'r'://return
                out += '\r';
                break;
            case 'a'://alll
                out += '\007';
                break;
            default:
                i--;
                out += in[i];
                break;
            }
        }else{
            out += in[i];
        }
    }
    return out;
}


int fileIO::file_size(char* filename){
    struct stat statbuf;
    int ret;
    ret = stat(filename,&statbuf);//调用stat函数
    if(ret != 0) return -1;//获取失败。
    return statbuf.st_size;//返回文件大小。
}

string strps::Trim(string & str){
    string blanks("\f\v\r\t\n ");
    string temp;
    for(int i = 0;i < (int)str.length();i++){
        if(str[i] == '\0')
            str[i] = '\t';
    }
    str.erase(0,str.find_first_not_of(blanks));
    str.erase(str.find_last_not_of(blanks) + 1);
    temp = str;
    return temp;
}

void strps::split(vector<string> & vct,const string & line,const char sep){
    const size_t size = line.size();
    const char* str = line.c_str();
    int start = 0,end = 0;
    for(int i = 0;i < (int)size;i++){
        if(str[i] == sep){
            string st = line.substr(start,end);
            Trim(st);
            vct.push_back(st);
            start = i + 1;
            end = 0;
        }else
            end++;
    }
    if(end > 0){
        string st = line.substr(start,end);
        Trim(st);
        vct.push_back(st);
    }
}

void strps::Stringsplit(string str, string splits, vector<string>& res){
    if (str == "")		return;
    //在字符串末尾也加入分隔符,方便截取最后一段
    string strs = str + splits;
    size_t pos = strs.find(splits);
    int step = splits.size();

    // 若找不到内容则字符串搜索函数返回 npos
    while (pos != strs.npos)
    {
        string temp = strs.substr(0, pos);
        res.push_back(temp);
        //去掉已分割的字符串,在剩下的字符串中进行分割
        strs = strs.substr(pos + step, strs.size());
        pos = strs.find(splits);
    }
}

inline bool fileIO::check_exists(char* name) {
  struct stat buffer;
  return (stat (name, &buffer) == 0);
}

string strps::encoding::GBKToUTF8(const string &strGBK){
	string strOutUTF8 = "";
	WCHAR *str1;
	int n = MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(),  - 1, NULL, 0);
	str1 = new WCHAR[n];
	MultiByteToWideChar(CP_ACP, 0, strGBK.c_str(),  - 1, str1, n);
	n = WideCharToMultiByte(CP_UTF8, 0, str1,  - 1, NULL, 0, NULL, NULL);
	char *str2 = new char[n];
	WideCharToMultiByte(CP_UTF8, 0, str1,  - 1, str2, n, NULL, NULL);
	strOutUTF8 = str2;
	delete [] str1;
	str1 = NULL;
	delete [] str2;
	str2 = NULL;
	return strOutUTF8;
}

string strps::encoding::UTF8ToGBK(const string &strUTF8){
	int len = MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(),  - 1, NULL, 0);
	WCHAR *wszGBK = new WCHAR[len + 1];
	memset(wszGBK, 0, (len+1)*sizeof(WCHAR));
	MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)strUTF8.c_str(),  - 1, wszGBK, len);
	len = WideCharToMultiByte(CP_ACP, 0, wszGBK,  - 1, NULL, 0, NULL, NULL);
	char *szGBK = new char[len + 1];
	memset(szGBK, 0, len + 1);
	WideCharToMultiByte(CP_ACP, 0, wszGBK,  - 1, szGBK, len, NULL, NULL);
	//strUTF8 = szGBK;
	string strTemp(szGBK);
	delete [] szGBK;
	szGBK = NULL;
	delete [] wszGBK;
	wszGBK = NULL;
	return strTemp;
}

void num::random::ShakeInit(num::random::ShakeSt & st,float timePerRound,float minv,float maxv,float start,int startDiection){
    st.insideClock.Stop();
    st.shakeValue = start;
    if(maxv > minv){
        st.mn = minv;
        st.mx = maxv;
    }else{
        st.mn = maxv;
        st.mx = minv;
    }
    st.stDirection = startDiection;
    st.timePerRound = timePerRound;
    st.time = 0;
    st.insideClock.Start();
}

void num::random::Shake(num::random::ShakeSt & st){
    float nowOff =  ((float)st.insideClock.GetOffset())/((float)1000);
    //cout << nowOff << endl;
    float off = (float)(st.mx - st.mn) * ((float)nowOff / (float)st.timePerRound);
    float off1 = st.shakeValue + off * st.stDirection;
    if(off1 >= st.mx){
        off1 = st.mx;
        st.stDirection = -1;
    }else if(off1 <= st.mn){
        off1 = st.mn;
        st.stDirection = 1;
    }
    st.shakeValue = off1;
}

num::vectors::Vector num::vectors::Normalize(num::vectors::Vector v){
    float sum = v.x*v.x + v.y*v.y + v.z*v.z;
    float len = sqrt(sum);
    return Vector(v.x / len,v.y / len,v.z / len);
}

num::vectors::Vector::Vector(float v0,float v1,float v2){
    this->x = v0;
    this->y = v1;
    this->z = v2;
}
//ResProcessor.h
#ifndef RESPROCESSOR_H_INCLUDED
#define RESPROCESSOR_H_INCLUDED
#include <malloc.h>
#include <windows.h>
#include <stdio.h>
#include "ctool.h"
#define MASSERT(C,S) if(C){S;}

char * GetResourceDataPtrDebug(HMODULE,LPCSTR,LPCSTR,int & err,LPCSTR useForDebug = "");
char * GetThisResourceDataPtrDebug(LPCSTR,LPCSTR,int & err,LPCSTR useForDebug = "");
char * GetThisResourceDataPtrRelease(LPCSTR,LPCSTR,int & err);
char * GetResourceDataPtrRelease(HMODULE,LPCSTR,LPCSTR,int & err);
char * GetResourceDataPtr(HMODULE,LPCSTR,LPCSTR,int & err,LPCSTR useForDebug = "");//handle res type,data is created by malloc
char * GetThisResourceDataPtr(LPCSTR,LPCSTR,int & err,LPCSTR useForDebug = "");

#endif // RESPROCESSOR_H_INCLUDED
//ResProcessor.cpp
#include "ResProcessor.h"

char * GetResourceDataPtr(HMODULE h,LPCSTR s,LPCSTR s1,int & err,LPCSTR useForDebug){
    #ifndef BUILD_DEBUG
        return GetResourceDataPtrRelease(h,s,s1,err);
    #else
        return GetResourceDataPtrDebug(h,s,s1,err,useForDebug);
    #endif // BUILD_DEBUG
}

char * GetThisResourceDataPtr(LPCSTR s,LPCSTR s1,int & err,LPCSTR useForDebug){
    return GetResourceDataPtr(GetModuleHandle(NULL),s,s1,err,useForDebug);
}

char * GetResourceDataPtrDebug(HMODULE h,LPCSTR s,LPCSTR s1,int & err,LPCSTR useForDebug){
    int fileSz = fileIO::file_size((char *)useForDebug);
    MASSERT(fileSz == -1,err = ERR_GETING_RES_SIZE;return NULL);
    FILE * filed = fopen(useForDebug,"r");
    MASSERT(!filed,err = ERR_GET_RES_DATA;return NULL);
    char * data = (char *)malloc(sizeof(char) * (fileSz+1));
    MASSERT(!data,err = ERR_COPY_DATA;fclose(filed);return NULL);
    ZeroMemory(data,sizeof(char) * (fileSz+1));
    MASSERT(GetLastError(),err = ERR_COPY_DATA;fclose(filed);return NULL);
    fread(data,sizeof(char),fileSz,filed);
    return data;
}

char * GetThisResourceDataPtrDebug(LPCSTR s,LPCSTR s1,int & err,LPCSTR useForDebug){
    return GetResourceDataPtrDebug(GetModuleHandle(NULL),s,s1,err,useForDebug);
}

char * GetThisResourceDataPtrRelease(LPCSTR s,LPCSTR s1,int & err){
    return GetResourceDataPtrRelease(GetModuleHandle(NULL),s,s1,err);
}

char * GetResourceDataPtrRelease(HMODULE h,LPCSTR s,LPCSTR s1,int & err){
    typedef char T;
    HRSRC rs = FindResource(h,s,s1);//Check Resource
    MASSERT(GetLastError() || !rs,err = ERR_FIND_RES;return NULL);
    HGLOBAL glb = LoadResource(h,rs);//Load Resource
    MASSERT(GetLastError() || !glb,err = ERR_LOAD_RES;return NULL);
    T * sdata = (T*)LockResource(glb);//Do not need to use UnlockResource to Free it
    MASSERT(GetLastError() || !sdata,err = ERR_GET_RES_DATA;return NULL);
    DWORD size = SizeofResource(h,rs);//Get Resource size
    MASSERT(GetLastError() || !size,err = ERR_COPY_DATA;return NULL);
    size_t sz = sizeof(BYTE) * (size + 1);
    T * data = (T *)malloc(sz);//CopyData
    MASSERT(!data,err = ERR_COPY_DATA;return NULL);
    ZeroMemory(data,sz);
    strncpy(data,sdata,sz);
    MASSERT(GetLastError(),err = ERR_COPY_DATA;return NULL);
    return data;
}

好了,不知不觉就有了1万三千多个字了,这是本人第一次写博客,而且自己c++也没学多久,大概就一个月吧(不加上写代码的时间),还是从网上的视频里随便看了看的,因此对于一些东西肯定是有理解错误的,如果有错,请指出,感激不尽,谢谢!

参考资料:

微软MSDN:

LockResource function (libloaderapi.h) - Win32 apps | Microsoft Docs

LoadResource function (libloaderapi.h) - Win32 apps | Microsoft Docs

FindResourceA function (winbase.h) - Win32 apps | Microsoft Docs

UNICODE GBK UTF-8 编码互转(VC++)_sunflover454的专栏-CSDN博客

C语言获取文件的大小_qq_36553031的博客-CSDN博客_c语言获取文件大小

C++中string如何实现字符串分割函数split()_灰猫-CSDN博客_c++ string字符串分割

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值