有时候,我们经常需要给exe可执行文件附带一些资源文件(例如,皮肤,图片等一些资源),这些资源文件只能与exe独立放在硬盘上,这就带来了一些隐患,例如用户删除了或者修改了这些资源文件,就会引发可执行程序的非正常运行,或者无法运行,所以我们有必要把这些资源写进exe中。VS自带的添加资源方式维护起来很麻烦,所以要采用其他的办法。
其实任何一个资源文件都是二进制的一组字节(Byte)流,如果我们把这些字节流写入到一个空间很大的变量中,不就可以了吗?因为一个char/unsigned char的大小就是一个byte的大小,所以我们可以将一个这些资源数据都写入到一个unsigned char[]数组中,然后调用这个数组的首地址不就可以得到资源文件在内存中的指针了吗?
因为这个资源文件可能很大,所以我们单独的用一个.h或者.cpp文件来存放这个变量。
下面就是一个可以将资源文件(可以是任何格式的文件,例如:txt,jpg,rar,zip等)中的字节流存放到一个unsigned char数组中的程序。
int _tmain(int argc, _TCHAR* argv[])
{
if (argc == 1) return 0;
HANDLE hRsrc = CreateFile(argv[1], GENERIC_WRITE | GENERIC_READ,
0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hRsrc == INVALID_HANDLE_VALUE) return 0;
TCHAR drive[_MAX_DRIVE] = {0};
TCHAR dir[_MAX_DIR] = {0};
TCHAR fname[_MAX_FNAME] = {0};
TCHAR ext[_MAX_EXT] = {0};
_tsplitpath_s(argv[1], drive, dir, fname, ext);
TCHAR output[_MAX_DIR] = {0};
if (argc == 2)
_stprintf_s(output, _T("%s%s%s%s"), drive, dir, fname, _T(".cpp"));
else
_stprintf_s(output, _T("%s%s%s"), argv[2], fname, _T(".cpp"));
HANDLE hCpp = CreateFile(output, GENERIC_WRITE|GENERIC_READ, 0,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hCpp == INVALID_HANDLE_VALUE) return 0;
printf("正在转换...\n");
unsigned char ch = 0;
DWORD dwReadSize = 0;
DWORD dwWrittenSize = 0;
DWORD dwFileSize = GetFileSize(hRsrc, NULL);
char header[1024] = {0};
sprintf_s(header, "%s%s%d%s%s%d%s","#include \"StdAfx.h\"\n", "int dataLen = ", dwFileSize + 20,
";\n", "unsigned char rsrcData[", dwFileSize + 20, "] = {\n");
WriteFile(hCpp, header, strlen(header), &dwWrittenSize, NULL);
char radihex[2] = {0};
char buff[10] = {0};
int cnt = 0;
do
{
if(!ReadFile(hRsrc, &ch, sizeof(char), &dwReadSize, NULL))
break;
memset(radihex, 0, sizeof(radihex));
memset(buff, 0, sizeof(buff));
cnt++;
if (cnt % 16 == 0 && cnt != 0)
{
sprintf_s(buff, "0x%02x,\n", ch);
WriteFile(hCpp, buff, strlen(buff), &dwWrittenSize, NULL);
}
else if (cnt == dwFileSize + 1)
{
sprintf_s(buff, "0x%02x", ch);
WriteFile(hCpp, buff, strlen(buff), &dwWrittenSize, NULL);
}
else
{
sprintf_s(buff, "0x%02x,", ch);
WriteFile(hCpp, buff, strlen(buff), &dwWrittenSize, NULL);
}
} while (dwReadSize);
char tail[1024] = {0};
sprintf_s(tail, "%s", "\n};");
WriteFile(hCpp, tail, strlen(tail), &dwWrittenSize, NULL);
FlushFileBuffers(hCpp);
CloseHandle(hRsrc);
CloseHandle(hCpp);
printf("转换完成!\n");
Sleep(1000);
return 0;
}
这是控制台版的,需要在控制台传入两个参数,前面的参数是要转换的目标文件,后一个参数是输出路径(缺省的话就会输出到同级目录下)。
输出以后类似于这样的结果:
#include "StdAfx.h"
int dataLen = 1692;
unsigned char rsrcData[1692] = {
0x50,0x4b,0x03,0x04,0x14,0x00,0x00,0x00,0x08,0x00,0x60,0x5e,0xc6,0x44,0x53,0x30,
0xc6,0xe7,0x0c,0x02,0x00,0x00,0x56,0x04,0x00,0x00,0x08,0x00,0x00,0x00,0x6d,0x61,
0x69,0x6e,0x2e,0x78,0x6d,0x6c,0x85,0x54,0x4d,0x6f,0xd3,0x40,0x10,0x3d,0x83,0xc4,
0x7f,0x58,0x96,0x43,0x2f,0x71,0xfd,0x11,0x42,0x7b,0xb0,0x5b,0x91,0x92,0x88,0x43,
0xc5,0xb1,0x1c,0xd1,0xda,0x5e,0x3b,0x16,0x9b,0xdd,0x68,0xbd,0x21,0x6d,0xcf,0x88,
0x1e,0x22,0x04,0x12,0x5......
}
然后在要使用该资源的位置声明引用这个两个变量:
// import resourse data
extern int dataLen;
extern unsigned char rsrcData[];
dataLen为数据的长度,rsrcData为资源文字的指针地址,具体怎么用要看实际的需求了,很多库和api都支持从内存中读取数据。
最后还附带一个可视化的转换工具:
下载地址: