有时为了方便起见,我们会把资源文件(图片、声音等等)编译进exe程序,需要的时候释放出来。使用gcc可以实现这个东西。
1、安装TDM-GCC
TDM-GCC是推荐用的很好的编译器,基于mingw64改造,安装时会自动配置环境变量,简单方便。下载地址 备用地址
2、准备资源文件并链接
通过objcopy命令链接资源文件为o文件:
# Windows系统下(编译64位程序时)
objcopy -I binary -O pe-x86-64 -B i386:x86-64 资源文件名 输出的链接文件名
# Linux系统下(编译32位程序时)
objcopy -I binary -O elf32-i386 -B i386 资源文件名 输出的链接文件名
# Linux系统下(编译64位程序时)
objcopy -I binary -O elf64-x86-64 -B i386:x86-64 资源文件名 输出的链接文件名
注意输出的文件扩展名要是o。
比如我这里有一个jst.jar的文件,通过objcopy链接为jst.o:
objcopy -I binary -O pe-x86-64 -B i386:x86-64 jst.jar jst.o
这样,当前目录下面出现了链接的文件:
再通过nm命令查询被链接的文件指针名:
nm 被链接的文件
例如我这里:
_binary_jst_jar_end文件末指针_binary_jst_jar_size文件大小指针_binary_jst_jar_start文件头指针
3、编写C/C++源文件并合并编译
刚刚我们获取了文件各指针名,那么在C语言/C++中我们就可以使用它们了!具体先要用extern语句来声明它们为char数组类型,平时就用文件头指针和文件末指针即可。要获取文件大小,不建议直接用上面获得的大小指针,建议通过末指针减头指针来计算得到。我的代码如下:
#include <stdio.h>
#include <stdlib.h>
extern char _binary_jst_jar_start[]; //引用文件头指针
extern char _binary_jst_jar_end[]; //引用文件末指针
int main() {
int size = _binary_jst_jar_end - _binary_jst_jar_start; //始末指针相减得到文件大小
printf("文件的起始指针为:%d\n", _binary_jst_jar_start);
printf("文件的结束指针为:%d\n", _binary_jst_jar_end);
printf("文件的大小:%d\n", size);
system("pause");
}
可以看到虽然引用的时候是char数组形式,但实质上它就是一个指针。数组名即为它的地址值。直接用其数组名获取地址。
C++的话操作方式相同,把printf换成cout语句即可。
然后再把资源文件和C源文件编译到一起:
C语言:
gcc C源文件 链接的资源文件 -o 编译输出文件
C++:
g++ C++源文件 链接的资源文件 -o 编译输出文件
例如我的:
// C:
gcc Main.c jst.o -o out.exe
// C++:
g++ Main.cpp jst.o -o out.exe
注意Windows上输出文件格式是exe的,linux可以不带格式(扩展名)。
在如果需要编译32位程序或者64位程序需要使用-m指定架构(不带m参数时跟随系统架构),例如下:
# C语言编译32位应用程序:
gcc -m32 Main.c jst.o -o out.exe
# C语言编译64位应用程序:
gcc -m64 Main.c jst.o -o out.exe
# C++编译32位应用程序:
g++ -m32 Main.cpp jst.o -o out.exe
# C++编译64位应用程序:
g++ -m64 Main.cpp jst.o -o out.exe
运行结果:
如果想释放这个文件怎么办呢?既然得到了文件始末指针,那么释放它也就很方便了。
在C语言中,先通过fopen建立一个新文件,释放文件时就写入到这个新文件里面(相当于这个新文件就是你释放的文件)。再用fwrite函数释放即可。例如我要把这个文件释放到E:\中转\outFile.jar,那么代码如下:
C语言:
#include <stdio.h>
#include <stdlib.h>
extern char _binary_jst_jar_start[]; //引用文件头指针
extern char _binary_jst_jar_end[]; //引用文件末指针
int main() {
int size = _binary_jst_jar_end - _binary_jst_jar_start; //始末指针相减得到文件大小
FILE *fp = fopen("E:\\中转\\outFile.jar", "wb"); //创建文件指针,指定释放的位置并指定为二进制只写模式
fwrite(_binary_jst_jar_start, size, 1, fp); //fwrite各参数含义:文件头指针位置,单次写入区块大小,写入区块数量,文件指针
fclose(fp); //一定要fclose!否则文件可能损坏
printf("文件释放完毕\n");
system("pause");
}
文件操作方面,C++和C语言略有不同。C++需要使用ofstream创建对象并打开文件,然后使用其write()函数写入。
C++:
#include <iostream>
#include <fstream>
#include <stdlib.h>
using namespace std;
extern char _binary_jst_jar_start[];
extern char _binary_jst_jar_end[];
int main() {
int size = _binary_jst_jar_end - _binary_jst_jar_start;
ofstream fp;
fp.open("E:\\中转\\outFile.jar", ios::binary | ios::out); //打开(指定)输出文件
fp.write(_binary_jst_jar_start, size); //write函数各参数意义:待写入数据头指针,写入的数据大小
fp.close();
cout << "文件释放完毕" << endl;
system("pause");
}
结果:
它出现在了指定位置。
最后需要说明一下得是昨天通过测试,发现在Windows系统上只有64位的gcc/g++编译器才能实现这个。32位编译器或者编译32位程序这样做就无法编译通过。 Linux系统上都可以实现。