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

发布于 2024-12-02 14:45:54 字数 1384 浏览 5 评论 0 原文

我有一个用 C++ 编写的小型演示可执行文件,它仅依赖于加载一个 5kb PNG 图像才能运行,该图像用于我制作的像素文本。由于这个文件,我需要提供一个 ZIP 存档,而不仅仅是一个可执行文件,这会在下载和“播放”之间产生足够的摩擦,我相信这会阻止一些人尝试它。

是,是否可以将 PNG 文件(以及任何其他文件)嵌入到可执行文件或源代码中,使其成为单个文件,并且可执行文件可以使用它?

我的问题 能够将 PNG 解析为字节流,因此不需要转换为像素数据。

提前致谢! (存在与此类似标题的其他问题,但它们及其答案似乎涉及更具体的问题并且不是很有帮助)

编辑:编译器是 Visual C++ 2010,这是在 Windows 上(尽管我想避免使用 Windows 特定的实用程序)

edit2:Alf 的答案似乎是最便携的方法,所以我很快编写了一个函数来将 PNG 文件解析为 TXT 或头文件,该函数可以被读作无符号字符数组。它的形式似乎与 PNG 文件本身相同,但我的 png 加载器不接受该数组。当从内存加载它时,PNG 解析器会获取一个 (void * buffer, size_t length) (如果有的话)。

如果您想查看代码,但如果您认为其他答案比此方法更好,我仍然会接受:

void compileImagePNGtoBinary(char * filename, char * output){

    FILE * file = fopen(filename, "rb");
    FILE * out = fopen(output, "w");

    unsigned char buffer[32];
    size_t count;
    fprintf(out, "#pragma once \n\n static unsigned char TEXT_PNG_BYTES[] = { ");
    while(!feof(file)){
            count = fread(buffer, 1, 32, file);

            for(int n = 0; n < count; ++n){
                    fprintf(out, "0x%02X, ", buffer[n]);
            };
    };
    fprintf(out, "};");
    fclose(file);
    fclose(out);

};

最终编辑Alf 也提到的 ImageMagick 正是我所需要的,谢谢!

I have a small demo executable wrote in C++ that depends only on one 5kb PNG image being loaded before it can run, which is used for a pixel text I made. Because of this one file, I would need to give out a ZIP archive instead of just one executable file, which creates enough friction between download and 'play' that I believe would dissuade some from trying it out.

My question is, is there anyway to embed the PNG file (and any other file really) into the Executable or source code so that it is a single file, and the executable can use it?

I have the ability to parse the PNG as a byte stream, so it does not need converted to pixel data.

Thanks in advance! (Other questions with a similar title to this exist, but they and their answers seem to get into more specific issues and weren't very helpful)

edit:The compiler is Visual C++ 2010 and this is on Windows (though I would want to avoid windows specific utilities for this)

edit2: Alf's answer seemed like the most portable method, so I quickly wrote a function to parse the PNG file into a TXT or header file that could be read as a unsigned char array. It appears to be identical in this form to the PNG file itself, but my png loader won't accept the array. When loading it from memory, the PNG parser takes a (void * buffer, size_t length) if it matters.

The code if you wanted to see, but I'll still accept other answers if you think they're better than this method:

void compileImagePNGtoBinary(char * filename, char * output){

    FILE * file = fopen(filename, "rb");
    FILE * out = fopen(output, "w");

    unsigned char buffer[32];
    size_t count;
    fprintf(out, "#pragma once \n\n static unsigned char TEXT_PNG_BYTES[] = { ");
    while(!feof(file)){
            count = fread(buffer, 1, 32, file);

            for(int n = 0; n < count; ++n){
                    fprintf(out, "0x%02X, ", buffer[n]);
            };
    };
    fprintf(out, "};");
    fclose(file);
    fclose(out);

};

Final Edit: ImageMagick which Alf also mentioned did exactly what I needed of it, thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

把时间冻结 2024-12-09 14:45:54

一种可移植的方法是定义一个像

typedef unsigned char Byte;

Byte const* pngFileData()
{
    static Byte const data =
    {
        // Byte data generated by a helper program.
    };
    return data;
}

“Then”这样的函数,您所要做的就是编写一个小帮助程序,该程序将 PNG 文件读取为二进制文件并生成 C++ 花括号初始值设定项文本。 编辑:@awoodland 在对该问题的评论中指出ImageMagick 有这样一个小帮助程序...

当然,对于 Windows 特定的程序,请改用普通的 Windows 资源方案。

干杯&呵呵,,

A portable way is to define a function like

typedef unsigned char Byte;

Byte const* pngFileData()
{
    static Byte const data =
    {
        // Byte data generated by a helper program.
    };
    return data;
}

Then all you have to do is to write a little helper program that reads the PNG file as binary and generates the C++ curly braces initializer text. Edit: @awoodland has pointed out in comment to the question, that ImageMagick has such a little helper program…

Of course, for a Windows-specific program, instead use the ordinary Windows resource scheme.

Cheers & hth.,

原谅我要高飞 2024-12-09 14:45:54

看看 XD:

http://www.fourmilab.ch/xd/

最后,xd 可以读取二进制文件并发出 C 语言数据
包含文件中数据的声明。当
您希望在 C 程序中嵌入二进制数据。

就我个人而言,我会使用 Windows 资源,但如果您需要一种真正可移植的方式,而不涉及可执行格式的知识,那么这就是正确的方法。 PNG、JPG 等等...

Look at XD:

http://www.fourmilab.ch/xd/

Finally, xd can read a binary file and emit a C language data
declaration which contains the data from the file. This is handy when
you wish to embed binary data within C programs.

Personally, I'd use resources for windows, but if you require a truly portable way that doesn't involve knowledge of the executable format, this is the way to go. PNG, JPG, whatever...

如果没有 2024-12-09 14:45:54

Base64 对文件进行编码并将其放入代码中某处的字符串中;)

Base64 encode the file and put it in a string somewhere in your code ;)

他夏了夏天 2024-12-09 14:45:54

您可以将任意文件嵌入到程序资源中: (MSDN) 用户定义的资源

用户定义的资源定义语句定义包含应用程序特定数据的资源。数据可以具有任何格式,并且可以定义为给定文件的内容(如果给定了文件名参数)或一系列数字和字符串(如果指定了原始数据块)。

nameID typeID 文件名

文件名指定包含资源二进制数据的文件的名称。文件的内容作为资源包含在内。 RC 不以任何方式解释二进制数据。程序员有责任确保数据针对目标计算机体系结构正确对齐。

完成后,您可以使用 LoadResource函数来访问文件中包含的字节。

You can embed any arbitrary file into your program resources: (MSDN) User-Defined Resource.

A user-defined resource-definition statement defines a resource that contains application-specific data. The data can have any format and can be defined either as the content of a given file (if the filename parameter is given) or as a series of numbers and strings (if the raw-data block is specified).

nameID typeID filename

The filename specifies the name of a file containing the binary data of the resource. The contents of the file are included as the resource. RC does not interpret the binary data in any way. It is the programmer's responsibility to ensure that the data is properly aligned for the target computer architecture.

Once you've done that you can use the LoadResource function to access the bytes contained in the file.

百变从容 2024-12-09 14:45:54

为了完整起见,我将提到#embed,并希望这将“很快”成为(标准)现实。

C23(C,而不是 C++ 编程语言)已经合并了这样的功能,例如 语言功能允许您编写:


int main (int, char*[]) {
    static const char sound_signature[] = {
#embed <sdk/jump.wav>
// ^^^^^^^^^^^^^^^^^^
    };
    static_assert((sizeof(sound_signature) / sizeof(*sound_signature)) >= 4,
        "There should be at least 4 elements in this array.");

    return 0;
}

对于 C++ 项目,仅针对此用例拥有一个 C 子组件可能有点困难,除非您的应用程序严重需要嵌入数据以某种方式。我的直觉是,即使在它进入标准之前,实现也会“悄悄地”让 C++ 可以使用它。您可以在功能作者博客(其中上面的示例是借自)。

请记住,C++ 更早就追求完全相同的功能,但首先将其纳入 C 标准。一篇论文(可能针对 c++26)可用 这里建议以下语法:

#include <embed>

constexpr std::span<const std::byte> fxaa_binary = std::embed("fxaa.spirv");

即完全避免预处理器。

I'll mention #embed for completeness and hoping this will "soon" be a (standard) reality.

C23 (the C, not C++ programming language) has already incorporated such a functionality as a language feature allowing you to write:


int main (int, char*[]) {
    static const char sound_signature[] = {
#embed <sdk/jump.wav>
// ^^^^^^^^^^^^^^^^^^
    };
    static_assert((sizeof(sound_signature) / sizeof(*sound_signature)) >= 4,
        "There should be at least 4 elements in this array.");

    return 0;
}

For a C++ project, having a C subcomponent just for this use case might be a stretch, unless your application heavily requires embedding data in some way. My intuition is that implementations will "quietly" make this available to C++ even before it's in the standard. You can read more on the topic at the feature's author blog (where the above example was borrowed from).

Bear in mind that the exact same feature was being pursued even earlier for C++ but made it into the C standard first. A paper (for c++26 probably) is available here suggesting the following sytnax:

#include <embed>

constexpr std::span<const std::byte> fxaa_binary = std::embed("fxaa.spirv");

i.e. avoiding the preprocessor altogether.

少女净妖师 2024-12-09 14:45:54

这是依赖于可执行文件格式的,这意味着本质上依赖于操作系统/编译器。 Windows为此提供了资源系统,如此问题中所述。

This is executable-format dependent, which means inherently operating system/compiler dependent. Windows offers the Resources system for this as mentioned in this question.

不…忘初心 2024-12-09 14:45:54

如果我想将静态数据嵌入到可执行文件中,我会将其作为无符号字符数组打包到 .lib/.a 文件或头文件中。如果您正在寻找一种便携式方法。
我创建了一个命令行工具,实际上可以在此处执行这两项操作。您所要做的就是列出文件,然后选择选项 -l64 来输出 64 位库文件以及包含指向每个数据的所有指针的标头。

您还可以探索更多选项。例如,此选项:

>BinPack image.png -j -hx

将 image.png 的数据输出到头文件中,因为十六进制和行将根据 -j 选项进行调整。

const unsigned char BP_icon[] = { 
0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
0x00,0x00,0x01,0xed,0x00,0x00,0x01,0xed,0x08,0x06,0x00,0x00,0x00,0x34,0xb4,0x26,
0xfb,0x00,0x00,0x02,0xf1,0x7a,0x54,0x58,0x74,0x52,0x61,0x77,0x20,0x70,0x72,0x6f,
0x66,0x69,0x6c,0x65,0x20,0x74,0x79,0x70,0x65,0x20,0x65,0x78,0x69,0x66,0x00,0x00,
0x78,0xda,0xed,0x96,0x5d,0x92,0xe3,0x2a,0x0c,0x85,0xdf,0x59,0xc5,0x2c,0x01,0x49,
0x08,0x89,0xe5,0x60,0x7e,0xaa,0xee,0x0e,0xee,0xf2,0xef,0x01,0x3b,0x9e,0x4e,0xba,
0xbb,0x6a,0xa6,0x66,0x5e,0x6e,0x55,0x4c,0x8c,0x88,0x0c,0x07,0xd0,0x27,0x93,0x84,
0xf1,0xef,0x3f,0x33,0xfc,0xc0,0x45,0xc5,0x52,0x48,0x6a,0x9e,0x4b,0xce,0x11,0x57,
0x2a,0xa9,0x70,0x45,0xc3,0xe3,0x79,0xd5,0x5d,0x53,0x4c,0xbb,0xde,0xd7,0xe8,0x57,
0x8b,0x9e,0xfd,0xe1,0x7e,0xc0,0xb0,0x02,0x2b,0xe7,0x03,0xcf,0xa7,0xa5,0x87,0xff,
0x1a,0xf0,0xb0,0x54,0xd1,0xd2,0x0f,0x42,0xde,0xae,0x07,0xc7,0xf3,0x83,0x92,0x4e,
0xcb,0xfe,0x22,0xc4,0xa7,0x91,0xb5,0xa2,0xd5,0xee,0x97,0x50,0xb9,0x84,0x84,0xcf,
0x07,0x74,0x09,0xd4,0x73,0x5b,0x31,0x17,0xb7,0x8f,0x5b,0x38,0xc6,0x69,0xaf}

If I want to embed static data into an executable, I would package it into a .lib/.a file or a header file as an array of unsigned chars. That's if you are looking for a portable approach.
I have created a command line tool that does both actually here. All you have to do is list files, and pick option -l64 to output a 64bit library file along with a header that includes all pointers to each data.

You can explore more options as well.for example, this option:

>BinPack image.png -j -hx

will output the data of image.png into a header file, as hexadecimal and lines will be justified per -j option.

const unsigned char BP_icon[] = { 
0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52,
0x00,0x00,0x01,0xed,0x00,0x00,0x01,0xed,0x08,0x06,0x00,0x00,0x00,0x34,0xb4,0x26,
0xfb,0x00,0x00,0x02,0xf1,0x7a,0x54,0x58,0x74,0x52,0x61,0x77,0x20,0x70,0x72,0x6f,
0x66,0x69,0x6c,0x65,0x20,0x74,0x79,0x70,0x65,0x20,0x65,0x78,0x69,0x66,0x00,0x00,
0x78,0xda,0xed,0x96,0x5d,0x92,0xe3,0x2a,0x0c,0x85,0xdf,0x59,0xc5,0x2c,0x01,0x49,
0x08,0x89,0xe5,0x60,0x7e,0xaa,0xee,0x0e,0xee,0xf2,0xef,0x01,0x3b,0x9e,0x4e,0xba,
0xbb,0x6a,0xa6,0x66,0x5e,0x6e,0x55,0x4c,0x8c,0x88,0x0c,0x07,0xd0,0x27,0x93,0x84,
0xf1,0xef,0x3f,0x33,0xfc,0xc0,0x45,0xc5,0x52,0x48,0x6a,0x9e,0x4b,0xce,0x11,0x57,
0x2a,0xa9,0x70,0x45,0xc3,0xe3,0x79,0xd5,0x5d,0x53,0x4c,0xbb,0xde,0xd7,0xe8,0x57,
0x8b,0x9e,0xfd,0xe1,0x7e,0xc0,0xb0,0x02,0x2b,0xe7,0x03,0xcf,0xa7,0xa5,0x87,0xff,
0x1a,0xf0,0xb0,0x54,0xd1,0xd2,0x0f,0x42,0xde,0xae,0x07,0xc7,0xf3,0x83,0x92,0x4e,
0xcb,0xfe,0x22,0xc4,0xa7,0x91,0xb5,0xa2,0xd5,0xee,0x97,0x50,0xb9,0x84,0x84,0xcf,
0x07,0x74,0x09,0xd4,0x73,0x5b,0x31,0x17,0xb7,0x8f,0x5b,0x38,0xc6,0x69,0xaf}
骄傲 2024-12-09 14:45:54

在linux上我用这个。它基于我在尝试做一些 4k 演示时发现的一些示例,尽管做了一些修改。我相信它也可以在 Windows 上工作,但不能使用默认的 VS 内联汇编。我的解决方法是 #defining 一个宏来使用此代码或 @MarkRansom 建议的 Windows 资源系统(很难得到工作,但最终确实工作)。

//USAGE: call BINDATA(name, file.txt) and access the char array &name.

#ifndef EMBED_DATA_H
#define EMBED_DATA_H

#ifdef _WIN32
//#error The VS ASM compiler won't work with this, but you can get external ones to do the trick
#define BINDATA #error BINDATA requires nasm
#else

__asm__(
".altmacro\n" \
".macro binfile p q\n" \
"   .global \\p\n" \
"\\p:\n" \
"   .incbin \\q\n" \
"\\p&_end:\n" \
"   .byte 0\n" \
"   .global \\p&_len\n" \
"\\p&_len:\n" \
"   .int(\\p&_end - \\p)\n" \
".endm\n\t"
);

#ifdef __cplusplus
    extern "C" {
#endif

#define BINDATA(n, s) \
__asm__("\n\n.data\n\tbinfile " #n " \"" #s "\"\n"); \
extern char n; \
extern int n##_len;

#ifdef __cplusplus
    }
#endif

#endif

#endif

On linux I use this. It's based off a few examples I found when trying to do some 4k demos, albeit modified a bit. I believe it can work on windows too, but not with the default VS inline assembly. My workaround is #defining a macro to either use this code or the windows resource system that @MarkRansom suggests (quite painful to get working, but does work eventually).

//USAGE: call BINDATA(name, file.txt) and access the char array &name.

#ifndef EMBED_DATA_H
#define EMBED_DATA_H

#ifdef _WIN32
//#error The VS ASM compiler won't work with this, but you can get external ones to do the trick
#define BINDATA #error BINDATA requires nasm
#else

__asm__(
".altmacro\n" \
".macro binfile p q\n" \
"   .global \\p\n" \
"\\p:\n" \
"   .incbin \\q\n" \
"\\p&_end:\n" \
"   .byte 0\n" \
"   .global \\p&_len\n" \
"\\p&_len:\n" \
"   .int(\\p&_end - \\p)\n" \
".endm\n\t"
);

#ifdef __cplusplus
    extern "C" {
#endif

#define BINDATA(n, s) \
__asm__("\n\n.data\n\tbinfile " #n " \"" #s "\"\n"); \
extern char n; \
extern int n##_len;

#ifdef __cplusplus
    }
#endif

#endif

#endif
浊酒尽余欢 2024-12-09 14:45:54

我来这里寻找 bash 脚本,这样我就可以以跨平台兼容的方式生成 C 字节数组(无论如何,我的 Windows 构建都依赖 mingw bash),而无需编译辅助工具或依赖任何不符合普通 bash shell 标准的工具。这是我的看法:

#!/bin/sh
set -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
OUT_FILE="$SCRIPT_DIR/src/alloverse_binary_schema.h"
BINARY_FILE="$SCRIPT_DIR/include/allonet/schema/alloverse.bfbs"
VAR_NAME="alloverse_schema"

echo "static const unsigned char ${VAR_NAME}_bytes[] = {" > "$OUT_FILE"
hexdump -ve '1/1 "0x%02x, "' "$BINARY_FILE" >> "$OUT_FILE"
echo "0x00}; static const int ${VAR_NAME}_size = sizeof(${VAR_NAME}_bytes); " >> "$OUT_FILE"

然后我可以从使用它的 C 文件中 #include ,并根据需要使用 foo_bytesfoo_size

#include "alloverse_binary_schema.h"
bool allo_initialize(void)
{
    g_alloschema = reflection_Schema_as_root(alloverse_schema_bytes);
}

这个脚本应该适合您的需求通过调整 OUT_FILEBINARY_FILEVAR_NAME(也许将它们作为脚本的参数)。

I came here looking for a bash script, so that I can generate the C array of bytes in a mostly-cross-platform compatible way (I depend on mingw bash for my windows builds anyway) without having to compile a helper tool or depend on any tools that don't come standard with a normal bash shell. Here's my take:

#!/bin/sh
set -e

SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
OUT_FILE="$SCRIPT_DIR/src/alloverse_binary_schema.h"
BINARY_FILE="$SCRIPT_DIR/include/allonet/schema/alloverse.bfbs"
VAR_NAME="alloverse_schema"

echo "static const unsigned char ${VAR_NAME}_bytes[] = {" > "$OUT_FILE"
hexdump -ve '1/1 "0x%02x, "' "$BINARY_FILE" >> "$OUT_FILE"
echo "0x00}; static const int ${VAR_NAME}_size = sizeof(${VAR_NAME}_bytes); " >> "$OUT_FILE"

I can then just #include this from the C file where I use it, and use foo_bytes and foo_size as needed:

#include "alloverse_binary_schema.h"
bool allo_initialize(void)
{
    g_alloschema = reflection_Schema_as_root(alloverse_schema_bytes);
}

This script should be adaptable to your needs by adjusting OUT_FILE, BINARY_FILE and VAR_NAME (perhaps taking them as arguments to the script).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文