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