在GCC中实现VC中的资源

什么是资源呢?

用过VC的人都知道VC的图片资源可以导进exe可执行文件中,也就是说发布程序的时候可以不用带着那些可恶的图片了,这也可以减少误删图片造成程序出问题的几率。但是在Linux下的GCC中则没有这个功能,因此在这里给出了几种可行的方案。

1.方案1:基于NASM的资源实现

NASM是x86汇编器,我们用它来定义数据段。

由于代码的是可模板化,图片多了就是重复性工作了,其次是图片的内容一般很大,如果手动定义图片的数据是不现实的,(太累了^-^)。下面给出python脚本来自动生成源代码,脚本名字script.py。

import struct
def read_config():
    f = open('configure')
    config = []
    for line in f:
        c = line.split()
        config.append (c)
    return config
        
def write_source (f):
    b = f.read(1)
    while(b):
        byte = struct.unpack("B", b)
        print "0x0%x," %(byte),
        b = f.read(1)
    print "0x0" 
    
def make_source(config):
    for c in config:
        name = "source"+c[0]
        print "global " + name+":"
    print("[section .data]")
    for c in config:
        name = "source"+c[0]
        print name+":"
        image = open(c[1])
        print "db ",
        write_source(image)
        print 
if __name__ == '__main__':
    config = read_config()
    make_source(config)

 上述脚本的功能是根据配置文件configure中的内容来生成NASM汇编代码,首先看一下configure的写法:

0 root.png
1 collection.png

 configure的写法很简单,ID-imagename的格式,先写ID号(注意一定要从0开始连续的编号),然后是要作为资源的图片名字,同时将这些图片放在相同的目录下。然后就可以执行下面的命令:

python script.py>source.asm

 执行完该命令后,可以得到一个source.asm的NASM的汇编源代码,我们分析一下生成的文件内容:

global source0:
global source1:
[section .data]
source0:
db  0x089, 0x050, 0x04e, 0x047, 0x0d, 0x0a, 0x01a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x049
db  0x048, 0x044, 0x052, 0x00, 0x00, 0x00, 0x014, 0x00, 0x00, 0x00, 0x010, 0x08, 0x02
db  0x00, 0x00, 0x00, 0x099, 0x07a, 0x0c8, 0x04c, 0x00, 0x00, 0x00, 0x03, 0x073, 0x042
db  0x049, 0x054, 0x08, 0x08, 0x08, 0x0db, 0x0e1, 0x04f, 0x0e0, 0x00, 0x00, 0x01, 0x0da
db  0x049, 0x044, 0x041, 0x054, 0x028, 0x053, 0x09d, 0x092, 0x0bf, 0x04b, 0x09b, 0x041
db  0x018, 0x0c7, 0x0ef, 0x0b5, 0x011, 0x0c4, 0x086, 0x04, 0x024, 0x0b6, 0x0a6, 0x06, 0x05a
db  0x0ab, 0x014, 0x042, 0x087, 0x08a, 0x0d0, 0x045, 0x084, 0x066, 0x068, 0x017, 0x0b7
db 0x02c, 0x0ba, 0x0b5, 0x05b, 0x0e7, 0x04e, 0x015, 0x0a5, 0x0ff, 0x08a, 0x0e, 0x082, 0x076
db 0x070, 0x0cb, 0x02a, 0x0e, 0x01d, 0x0da, 0x082, 0x09b, 0x0a5, 0x083, 0x082, 0x0d6, 0x049
db 0x074, 0x089, 0x015, 0x062, 0x07c, 0x02f, 0x0ef, 0x0dd, 0x0f3, 0x05c, 0x0bf, 0x0cf, 0x0bd
db 0x097, 0x0e4, 0x035, 0x05d, 0x0c4, 0x0cb, 0x0bd, 0x097, 0x0fb, 0x0f5, 0x0b9, 0x0ef, 0x0f7
db 0x09e, 0x07b, 0x022, 0x0e7, 0x09c, 0x0ba, 0x06f, 0x089, 0x00, 0x082, 0x037, 0x0ba, 0x0fd
db 0x06d, 0x0f3, 0x0cb, 0x0e9, 0x0c1, 0x0ae, 0x063, 0x0f6, 0x033, 0x08a, 0x031, 0x0eb, 0x064
db 0x029, 0x0ed, 0x0e7, 0x046, 0x01e, 0x0ce, 0x0be, 0x07d, 0x03f, 0x05f, 0x0ff, 0x094, 0x015
db 0x08a, 0x0b0, 0x0a8, 0x0af, 0x0af, 0x0b6, 0x0d7, 0x06a, 0x0a3, 0x0f9, 0x062, 0x0a9, 0x03c
db 0x0a5, 0x0a2, 0x048, 0x079, 0x086, 0x085, 0x04f, 0x0ab, 0x023, 0x056, 0x0c6, 0x09a, 0x0e6
db 0x0d9, 0x09f, 0x0ea, 0x042, 0x0fd, 0x0cd, 0x0f2, 0x04a, 0x08f, 0x017, 0x078, 0x06b, 0x0b5
db  0x036, 0x0fc, 0x020, 0x02a, 0x03d, 0x099, 0x0be, 0x038, 0x0fa, 0x069, 0x03a, 0x06d, 0x026
db 0x045, 0x04e, 0x011, 0x03b, 0x0cb, 0x0ca, 0x0e2, 0x014, 0x021, 0x05d, 0x0e1, 0x0f1, 0x0f3
db 0x042, 0x0a9, 0x072, 0x079, 0x07e, 0x0f2, 0x0e8, 0x069, 0x075, 0x0e9, 0x0f3, 0x086, 0x052
db 0x062, 0x039, 0x087, 0x02f, 0x06e, 0x035, 0x05f, 0x0bf, 0x05b, 0x0dc, 0x0df, 0x06b, 0x0c4
db 0x0ad, 0x04b, 0x021, 0x03d, 0x063, 0x0d9, 0x011, 0x029, 0x0b4, 0x06, 0x02d, 0x0b9, 0x0ab
db 0x0e3, 0x0df, 0x063, 0x03a, 0x099, 0x079, 0x0f1, 0x0ea, 0x0d7, 0x08f, 0x06, 0x0ac, 0x079
db 0x0d6, 0x0c3, 0x0b8, 0x0d5, 0x078, 0x065, 0x0a2, 0x0fe, 0x0e1, 0x023, 0x06b, 0x0ed, 0x0d8
db 0x032, 0x0f6, 0x012, 0x011, 0x05a, 0x06b, 0x0d8, 0x05a, 0x036, 0x069, 0x087, 0x0d8, 0x0d8
db 0x0a1, 0x062, 0x0f9, 0x0e0, 0x07b, 0x03f, 0x0c0, 0x01, 0x086, 0x050, 0x0fc, 0x0f7, 0x090
db 0x093, 0x01b, 0x0b0, 0x06c, 0x058, 0x078, 0x043, 0x060, 0x041, 0x0fa, 0x08a, 0x08e, 0x09c
db 0x092, 0x01f, 0x0ad, 0x020, 0x010, 0x05d, 0x061, 0x0af, 0x08c, 0x0c0, 0x012, 0x0ae, 0x068
db 0x088, 0x034, 0x01, 0x093, 0x09a, 0x040, 0x099, 0x0d1, 0x0a, 0x0e9, 0x087, 0x038, 0x014
db  0x05e, 0x0c4, 0x045, 0x0e6, 0x069, 0x083, 0x0b2, 0x06c, 0x0ea, 0x058, 0x0ea, 0x0e0, 0x078
db  0x04b, 0x041, 0x04a, 0x07c, 0x092, 0x0c1, 0x0a1, 0x029, 0x0e6, 0x0ed, 0x080, 0x0cf, 0x0a4
db  0x045, 0x050, 0x0c6, 0x02, 0x069, 0x063, 0x0b4, 0x019, 0x0f0, 0x029, 0x030, 0x0c1,
... ....
... ....
... ....

 我们发现生成的文件内容很简单,首先是导出变量,以使C语言可是调用,这些变量是各个资源在内存中的指针。然后是数据段的定义,这里是将整个文件都复制进数据段,所以前面说这个文件要是人写的话,肯定会疯掉的,太多了,不过幸好我们可以用脚本自动生成该文件。

 

接下来我们又提供了两个脚本用来生成source.h source.c 文件,这样的话在C语言中引用该文件就能使用资源了,首先看一下这两个脚本:

script_source_head.py

def head(config):
    print "#ifndef SOURCE_H"
    print "#define SOURCE_H"
    for c in config:
        print "#define SOURCE_IMAGE_" + (c[1]).split(".")[0].upper(),c[0]
    print "void *get_source(int sourceID);"
    print "int *get_source_length(int sourceID);"
    print "#endif"
    
def read_config():
    f = open('configure')
    config = []
    for line in f:
        c = line.split()
        config.append (c)
    return config
if __name__ == '__main__':
    config = read_config()
    head(config)

 和script_source.py

import os
def source(config):
    print "#include \"source.h\""
    for c in config:
        print "extern void *source"+c[0]+";"
    
    i = 0
    print "static void *Image[] = {"
    for c in config:
        print "\t&source"+c[0],
        i = i + 1
        if (i != len(config)):
            print ","
        else:
            print
    print "};"
    
    i = 0
    print "static void *ImageLength[] = {"
    for c in config:
        statinfo = os.stat(c[1])
        print "\t%s" %(statinfo.st_size),
        i = i + 1
        if (i != len(config)):
            print ","
        else:
            print
    print "};"
    
    print '''void *
get_source(int sourceID)
{
        return Image[sourceID];
}

int *
get_source_length(int sourceID)
{
        return ImageLength[sourceID];
}'''
  
def read_config():
    f = open('configure')
    config = []
    for line in f:
        c = line.split()
        config.append (c)
    return config
if __name__ == '__main__':
    config = read_config()
    source(config)

 执行下面的命令:

python script_source_head.py>source.h
python script_source.py >source.c

 接下来我们看一下生成的文件:

source.h

#ifndef SOURCE_H
#define SOURCE_H
#define SOURCE_IMAGE_ROOT 0
#define SOURCE_IMAGE_COLLECTION 1
void *get_source(int sourceID);
int *get_source_length(int sourceID);
#endif

source.c

#include "source.h"
extern void *source0;
extern void *source1;
static void *Image[] = {
	&source0 ,
	&source1
};
static void *ImageLength[] = {
	546 ,
	470
};
void *
get_source(int sourceID)
{
        return Image[sourceID];
}

int *
get_source_length(int sourceID)
{
        return ImageLength[sourceID];
}

 

好了到此我们已经实现了资源,用函数get_source可以获得资源的首地址,用函数get_source_length可以获得资源的大小。

下面我们研究一下怎么使用,请看下面的一个测试程序:

#include "stdio.h"
#include <gtk/gtk.h>
#define WINDOW_WIDTH 400
#define WINDOW_HEIGHT 300
#include "source.h"

static void init_tree_store ( GtkTreeModel *model);
GtkWidget *create_index_tree (gpointer data);

GdkPixbuf *
load_icon(int ID)
{
        g_print ("%d\n",ID);
        
        GdkPixbuf *icon = NULL;
        GMemoryInputStream *stream;
        stream = g_memory_input_stream_new_from_data(get_source(ID),
                                                     get_source_length(ID),
                                                     NULL);
        icon = gdk_pixbuf_new_from_stream (stream, NULL, NULL);
        
        return icon;
}


int
main (int argc, char **argv)
{
        gtk_init (&argc, &argv);
        GtkWidget *window;
        window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
        gtk_window_set_title (GTK_WINDOW(window), "资源例子");
        gtk_window_set_position (GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS);
        gtk_widget_set_size_request (GTK_WIDGET(window), WINDOW_WIDTH, WINDOW_HEIGHT);

        gtk_container_add (GTK_CONTAINER(window), create_index_tree(0));
        
        /*信号*/
        g_signal_connect (G_OBJECT(window), "delete_event", G_CALLBACK(gtk_main_quit), NULL);
                if (!gtk_widget_get_visible (window))
		gtk_widget_show_all (window);
	else
		gtk_widget_destroy (window);
	
	gtk_main ();

        return 0;
}

GtkWidget *
create_index_tree (gpointer data)
{
        GtkTreeViewColumn *col;
        GtkCellRenderer *renderer;
        GtkWidget *view;
        GtkTreeModel *model;
        GtkTreeStore *treestore;
        
        view = gtk_tree_view_new ();
        /*去掉标题栏*/
        gtk_tree_view_set_headers_visible (view, FALSE);
        gtk_tree_view_set_enable_tree_lines (view, TRUE);
        gtk_tree_view_set_enable_search (view, FALSE);
        
        col = gtk_tree_view_column_new ();
        gtk_tree_view_append_column (GTK_TREE_VIEW(view), col);

        renderer = gtk_cell_renderer_pixbuf_new ();
        gtk_tree_view_column_pack_start (col, renderer, FALSE);
        gtk_tree_view_column_add_attribute (col, renderer, "pixbuf", 0);
        
        renderer = gtk_cell_renderer_text_new ();
        gtk_tree_view_column_pack_start (col, renderer, FALSE);
        gtk_tree_view_column_add_attribute (col, renderer, "text", 1);
        
        
        treestore = gtk_tree_store_new (3,
                                        GDK_TYPE_PIXBUF,
                                        G_TYPE_STRING,
                                        G_TYPE_POINTER);
        init_tree_store (treestore);
        
        model = GTK_TREE_MODEL (treestore);
        
        gtk_tree_view_set_model (GTK_TREE_VIEW(view), model);
        g_object_unref (model);
        
        gtk_tree_view_expand_all (view);
        return view;
}
static void
init_tree_store ( GtkTreeModel *model)
{
        GtkTreeStore *treestore = GTK_TREE_STORE (model);
        GdkPixbuf *icon = NULL;
        GtkTreeIter *parentiter;
        GtkTreeIter child;
        g_print ("test\n");
        icon = load_icon (SOURCE_IMAGE_ROOT);
        gtk_tree_store_append (treestore, &child, NULL);
        gtk_tree_store_set (treestore, &child,
                            0, icon,
                            1,"测试1",
                            -1);

        icon = load_icon (SOURCE_IMAGE_COLLECTION);
        gtk_tree_store_append (treestore, &child, NULL);
        gtk_tree_store_set (treestore, &child,
                            0, icon,
                            1,"测试2",
                            -1);
}

 Makefile为:

all:
	nasm -f elf -o source_asm.o source.asm
	gcc -c -o source.o source.c 
	gcc -c -o main.o main.c `pkg-config --cflags --libs gtk+-3.0`
	gcc -o main main.o source.o source_asm.o `pkg-config --cflags --libs gtk+-3.0`

 程序运行的截图如下:

 

2.方案2:基于GCC内嵌汇编的方法

方案1的优点是可以自己选择自己喜好的汇编器,但是缺点是如果用autotools来组织工程的话很难将汇编器加到项目中。使用GCC内嵌汇编的方法可以解决这个问题,但是GCC支持的汇编是AT&T格式的汇编,不是Intel的汇编,对于初学者来说着很难理解其复杂的格式。方案的具体实施可以自己实验一下,这里不具体研究了。

3.方案3:基于C语言的方法

上述两个方法之所以用汇编来定义数据,不用c语言定义数据,这是因为C语言中没有相关的数据类型来定义该数据段,但也不是不能定义,可以定一个全局的字符数组,并对其初始化,这样就和汇编中定义的数据段是一样的。但是理解起来有点别扭,因为字符数组中存的其实是二进制数据。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值