如何将文件嵌入可执行文件?

我有两个问题,第一个已经解决了。

目前的问题
如果我嵌入了一个需要加载库的文件,例如jpeg图像或mp3音乐,我将需要使用该文件作为库的输入。 但是,每个库都是不同的,并使用一种方法来获取文件作为输入,输入可以是文件名或FILE *指针(来自libc的文件界面)。

我想知道如何访问带有名称的嵌入式文件。 如果我创建一个临时文件会有效率,还有另一种方法吗? 我可以将文件名映射到内存吗? 我的平台是Windows和Linux。

如果show_file(const char * name)是库中的函数,我将需要一个字符串来打开该文件。
我看过这些问题:
如何在内存中获取缓冲区的文件描述符?
从C中的文件描述符获取文件名

以下代码是我的解决方案。 这是一个好的解决方案吗? 它效率低下吗?

# include  # include  extern char _binary_data_txt_start; extern const void* _binary_data_txt_size; const size_t len = (size_t)&_binary_data_txt_size; void show_file(const char* name){ FILE* file = fopen(name, "r"); if (file == NULL){ printf("Error (show_file): %s\n", name); return; } while (true){ char ch = fgetc(file); if (feof(file) ) break; putchar( ch ); } printf("\n"); fclose(file); } int main(){ int fpipe[2]; pipe(fpipe); if( !fork() ){ for( int buffsize = len, done = 0; buffsize>done; ){ done += write( fpipe[1], &_binary_data_txt_start + done, buffsize-done ); } _exit(0); } close(fpipe[1]); char name[200]; sprintf(name, "/proc/self/fd/%d", fpipe[0] ); show_file(name); close(fpipe[0]); } 

另一个问题(已解决)
我试图在Linux上嵌入一个文件,使用GCC,并且它有效。 但是,我尝试在Windows上使用Mingw做同样的事情,并且它没有编译。

代码是:

 # include  extern char _binary_data_txt_start; extern char _binary_data_txt_end; int main(){ for (char* my_file = &_binary_data_txt_start; my_file <= &_binary_data_txt_end; my_file++) putchar(*my_file); printf("\n"); } 

编译命令是:

 objcopy --input-target binary --output-target elf32-i386 --binary-architecture i386 data.txt data.o g++ main.cpp data.o -o test.exe 

在Windows上,我收到以下编译器错误:

 undefined reference to `_binary_data_txt_start' undefined reference to `_binary_data_txt_end' 

我试图用i386-pc-mingw32替换elf32-i386 ,但我仍然得到同样的错误。

我认为为了与MinGW一起使用,您需要从.c文件中的名称中删除前导下划线。 有关详细信息,请参阅使用gcc mingw嵌入二进制blob 。

看看是否使用以下帮助:

 extern char binary_data_txt_start; extern char binary_data_txt_end; 

如果需要相同的源来使用Linux或MinGW版本,则可能需要使用预处理器在不同的环境中使用正确的名称。

如果您正在使用需要FILE*的库来读取数据,那么您可以使用fmemopen(3)从内存blob中创建伪文件。 这样可以避免在磁盘上创建临时文件。 不幸的是,它是一个GNU扩展,所以我不知道它是否可用于MinGW(可能不是)。

但是,大多数编写良好的库(例如libpng和IJG的JPEG库 )都提供了从内存而不是从磁盘打开文件的例程。 特别是libpng甚至提供了一个流接口,您可以在PNG文件完全读入内存之前对其进行递增解码。 例如,如果您从网络流式传输隔行扫描的PNG,并且希望在加载时显示隔行数据以获得更好的用户体验,则此function非常有用。

在Windows上,您可以将自定义资源嵌入到可执行文件中。 您需要一个.RC文件和一个资源编译器。 使用Visual Studio IDE,您可以毫不费力地完成它。

在您的代码中,您将使用FindResourceLoadResourceLockResource函数在运行时将内容加载到内存中。 以长字符串forms读取资源的示例代码:

 void GetResourceAsString(int nResourceID, CStringA &strResourceString) { HRSRC hResource = FindResource(NULL, MAKEINTRESOURCE(nResourceID), L"DATA"); HGLOBAL hResHandle = LoadResource(NULL, hResource); const char* lpData = static_cast ( LockResource(hResHandle) ); strResourceString.SetString(lpData, SizeofResource(NULL, hResource)); FreeResource(hResource); } 

其中nResourceID是自定义资源类型DATA下的资源ID。 DATA只是一个名称,您可以选择其他名称。 其他内置资源是游标,对话框,字符串表等。

我创建了一个名为elfdataembed的小型库,它提供了一个简单的界面,用于提取/引用使用objcopy嵌入的部分。 这允许您将偏移量/大小传递给另一个工具,或使用文件描述符直接从运行时引用它。 希望这将有助于未来的人。

值得一提的是,这种方法比编译符号更有效,因为它允许外部工具引用数据而无需提取,并且它也不需要将整个二进制文件加载到内存中以便提取/引用它。

使用nm data.o查看它命名为符号的内容。 它可能与文件系统差异一样简单,导致文件名派生符号不同(例如文件名大写)。

编辑:刚看到你的第二个问题。 如果您正在使用线程,您可以创建一个管道并将其传递给库(如果它想要一个FILE *首先使用fdopen() )。 如果您对API有更具体的了解,我可以添加更具体的建议。