C++ Windows packages file resources into exe and reads them out

Posted by krotkruton on Tue, 28 Dec 2021 07:40:08 +0100

Are you like me, Total number of exe files built (plus resource files) try to keep it as few as possible. It's better to have only one file, or some things don't want to put some important things outside exe. Even after encryption, you don't feel at ease. Then rc and windows api can meet your needs. If you want to dazzle you with more than 100 DLLs and more than 100 resource files in a program like adobe, it doesn't matter, Just watch it!  

catalogue

Basic knowledge

RC resource file:

Windows API functions

text

Basic knowledge

RC resource file:

RC, full name (Resource), can be called windres Exe compiles and packages the resources in exe. Its basic syntax is as follows:

        [resource_id] [Token] [Arguments]

Common keywords: DIALOG,CTEXT,LTEXT,RTEXT,MENU,ICON

Add: RC can also be used to set the icon of packaged exe. Only one line of code is required:

[any name] icon "path of icon, suffix. ico"

In addition, many people on the Internet say that the variable name of "any name" must be MAIN_ICON to make the EXE have an icon. However, in fact, as long as there is an icon file in the resource file, the icon of your exe will change. For example:

//rc.rc
THIS_IS_NOT_THE_EXE_ICON_PLEASE ICON "secret.ico" //What's the secret???

Suppose secret ICO is a picture that you don't want to appear on the exe icon, but RC doesn't care. It will use it as the program icon by default.

The RC keyword used in this article is RCDATA

Windows API functions

Needless to say, it's windows Almost all functions in H (except for version incompatibility, for example, GetConsoleWindow is incompatible with Win98, 95 and server 2000)

The main windows functions used this time: GetLastError, findresource, lockresource, sizeofresource, unlockeresource

text

If you want to package resources, first, you need to write the resource file information into rc. You can easily package resources with RCDATA. The RCDATA syntax is as follows:

[resource ID / serial number] RCDATA "resource address"

Suppose you have two texts to support Chinese and English, then the program can be written

//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"

The specific file format is as follows:

 Chinese.l the contents are as follows:

Hello world!

 English.l is

HelloWorld!

OK, with resources, how to read it? At this time, we need to develop a function to solve this problem. My function model is as follows.  

/*
 *@Summary Get resource file pointer (copied)
 *@Arguments:
 * exeInstance:The handle of this program, GetModuleHandle(NULL), and the hInstance entry parameter in WinMain can both be used
 * rcName:The name of the resource. If it is a sequence number, use makeintresource (sequence number) to obtain it
 * rcType:The type of resource has never been specified in this article_ Rcdata, or RT_ICON,RT_DIALOG et al
 * err:Error code
 *@Return:
 * The data pointer after copy is returned if successful, and NULL if failed
 */
char * GetResourceDataPtr(HMODULE exeInstance,LPCSTR rcName,LPCSTR rcType,int & err);

So how to achieve it?

It is divided into the following steps:

1. Find the resource and get the handle through FindResource

2. Obtain the HGLOBAL of the resource through LoadResource

3. Obtain the resource pointer through LockResource

4. Copy data through malloc and strncpy

5. Release HGLOBAL in Heap through UnlockResource

Careful smart people may find out why the fifth step is italicized? You'll know why it's italicized later!

OK, now that you have the idea, it's time to write the function body.

If you want the function to resist all exceptions that can be handled, it is very important to judge the exceptions and return them in the function. Here, I define a macro to handle this situation

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

First, find the resource and get the handle through FindResource, so the code is

FindResource is defined as follows

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

Then LoadResource

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

Lock resource again

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

Then malloc and strncpy copy the data

_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);

Finally, UnlockResource returns?
NO,NO,NO!

In fact, you don't need UnlockResource at all, because when you look at the place where it is defined, winbase H yes, you'll find that it's defined like this

#define UnlockResource(h) (h)

When I wrote the code, I found that the UnlockResource displayed in Codeblocks is a macro and mingw. When I was warned that this is a meaningless statement (warning: statement has no effect [- wunused value] |), I thought something was wrong. Sure enough, it was empty!

Finally, with our wrong judgment, a function can be written. Of course, in order not to debug is a waste of time, I also designed a debug version. In this way, the resource file will not be changed, and the whole exe will be rebuilt

Finally, I present all the source code:

//ctool. H fileio:: file in debug_ Size comes from it. It is a c + + toolkit. You can get it if you need it
#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;
    };

    //Link winmm. Required a. VC #pragma comment (link, "winmm. A") add "winmm" string to linker settings in CodeBlocks buildoption 
    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);
        //Jitter random number, currently in beta status
        void Shake(ShakeSt & st);
    }
    namespace vectors{
        //vector
        struct Vector{
            float x;
            float y;
            float z;
            Vector(float v0,float v1,float v2);
        };
        Vector Normalize(Vector);
    }
}

#endif // CTOOL_INC
//ctool. cpp ctool. The implementation of H needs to be taken by yourself. Most of the code is found online. It is only used as a toolkit and has no commercial use
#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);//Call stat function
    if(ret != 0) return -1;//Get failed.
    return statbuf.st_size;//Returns the file 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;
    //A separator is also added at the end of the string to intercept the last paragraph
    string strs = str + splits;
    size_t pos = strs.find(splits);
    int step = splits.size();

    // If the content is not found, the string search function returns npos
    while (pos != strs.npos)
    {
        string temp = strs.substr(0, pos);
        res.push_back(temp);
        //Remove the split string and split it in the remaining string
        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;
}

Well, there are more than 13000 words unknowingly. This is my first time to write a blog, and I haven't learned c + + for long. It's about a month (not including the time to write code). I still read it casually from the online video. Therefore, I must have misunderstood some things. If there is a mistake, please point out. Thank you very much. Thank you!

reference material:

Microsoft 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 transcoding (VC + +) _sunflover454 column - CSDN blog

c language gets the size of the file_ qq_36553031 blog - CSDN blog_ Get file size in c language

How to implement string splitting function split() in C + +_ Grey cat CSDN blog_ c++ string string segmentation

Topics: C++ Programming Windows