PE文件资源管理开发小结|以此无聊之作当做2010年最后一件积累

  2010年快要过完了,这最后一个月实在不打算搞啥研究了,新换的工作又忙又累,我既不擅长熬夜,也不擅长通宵,周末之余还是搞搞外包,睡睡大觉比较好。。。抱怨完了,进入正题。

 

  PE文件的资源访问通过一组API进行,主要有LoadLibrary, FreeLibrary, FindResource, LoadResource, LockResource, FreeResource, BeginUpdateResource, UpdateResource, EndUpdateResource,这些应该差不多了。其中LoadLibrary和FreeLibrary是加载程序模块的函数,这里主要用来访问程序自身之外的模块文件,exe或dll。

  资源名称貌似有很多限制,参数类型是LPCWSTR,但文档说了,如果地址的数值小于64k,就被当做一个16位ID,作为资源的标识名称。我当然倾向于使用实际意义的字符串当资源名称,但是总是有莫名其妙的错误,大概有以下几种情况:含有文件路径非法字符(/,/,*,?之类);含有"."和"_"(这个有点严重了);字符太长的。含有文件路径非法字符时直接保存失败,包含"."和"_"或名称太长时,保存资源返回成功了,用EnumResourceNames遍历也可以查到名称,但是就是取不到数据,SizeofResource()检查资源大小总是0,真是被整的很郁闷。因此打算用ID来保存所有资源,自己来管理资源名称到ID的映射。

 

  设计思路如下:

  选一个ID保存一个固定资源,我选的是0xfc00(63k),这个资源可以称作“资源名称映射表”,里面保存其它资源的ID和字符串名称,对每个资源保存一个记录,以字符串名称作为Key,资源ID作为Value,运行时就可以读取出来放在哈希表里供查询使用。

  自定一个资源类别,尽量避免和其它类别冲突。资源一律用ID来标识,并保存到这个类别里,而ID通过程序来自动分配和回收。资源所用ID都要大于0xfc00,尽量不和MFC窗口的各种资源ID冲突。

  保存资源时,以一个任意字符串做名称,只要不和现有资源的名称重复,就自动分配一个未使用的ID,并记录到映射表。访问资源时,根据字符串名称查找ID,再通过ID获取资源数据。删除资源时,把资源数据和资源记录一并删除。由于资源ID是大于0xfc00,又有0xffff的上限,用户可保存的资源数量不超过1023个,我觉得差不多够用了吧。

 

  实现过程没什么难度我就忽略不提了。

 

  测试后发现有两个问题:

  1.读写效率下降。由于名称保存在一个映射表里,任何添加和删除资源都要修改这个表,而表本身也是一个资源,必须整个读取,整个写入,因此进行频繁添加或删除资源时效率低下。由于MSDN说明了资源写入PE文件的实际操作是在调用EndUpdateResource时进行,因此在需要添加或删除多个资源时先连续调用UpdateResource,包括更新映射表本身调用的UpdateResource,最后用一句EndUpdateResource一起写入。于是资源的添加和删除函数都做了两个版本,一个是每次添加一个资源,一个是每次添加多个资源。

  2.资源名称浪费存储空间。对每个资源ID和名称字符串的成对保存,程序内部使用了一个数据结构:

  struct _RsInternalInfo

  {

    WORD Id;

     wchar_t Name[128];

  }

  限制了每个资源的名称不超过127字符,保存也是定长记录,这样做方便了程序实现,但是那127字符会浪费大半。为了节约空间,可以使用不定长记录,每个名称字符串前先保存一个WORD,记录名称字符串的长度,然后按这个长度读取后面的字符。试验了一下,这样做又导致解析速度变慢。这个问题最后不打算解决了,名称映射信息直接用RsInternalInfo保存了。

 

  最后做了一个静态库,提供了以下接口:

这部分用来给其它程序模块添加或删除资源

  struct RsCreateInfo

  {

    wchar_t name[128];

    size_t data_size;

    void* data_bits;

  }

  bool ModuleResourceList(wchar_t* bin_file_name, vector<RsCreateInfo>* result);

  bool CreateModuleResource(wchar_t* bin_file_name, RsCreateInfo& create_info);

  bool CreateModuleResources(wchar_t* bin_file_name, vector<RsCreateInfo>* create_list);

  bool DeleteModuleResource(wchar_t* bin_file_name, wchar_t* name);

  bool DeleteModuleResources(wchar_t* bin_file_name, vector<wstring>* name_list);

这部分用来获得当前运行的程序自身的资源

  WORD MyResourceNumber();

  wstring MyResourceName(WORD id);

  bool GetMyResourceSizeByName(wchar_t* name, size_t* result);

  bool GetMyResourceDataByName(wchar_t* name, void** result);

 

  这些函数只能访问用CreateModuleResource和CreateModuleResources添加的资源,但是资源名称可以包含任意Unicode字符,不再受系统限制。尤其可以直接保存文件路径,把资源释放到文件时就方便多了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值