C/C++ 实现动态资源文件释放
当我们开发Windows应用程序时,通常会涉及到使用资源(Resource)的情况。资源可以包括图标、位图、字符串等,它们以二进制形式嵌入到可执行文件中。在某些情况下,我们可能需要从可执行文件中提取自定义资源并保存为独立的文件。在这篇博客文章中,我们将讨论如何使用C++和WinAPI实现这个目标。
简介
首先,让我们考虑一个场景:我们有一个 Windows 应用程序,其中包含了一个自定义的二进制资源比如默认的配置文件,我们希望将这个资源提取出来并保存为一个独立的文件以用于初始化程序配置项。为了实现这个目标,我们可以使用Windows API提供的相关函数,来完成对资源的释放工作。
关键函数概述
GetModuleHandle
该函数用于获取指定模块的句柄。模块可以是一个可执行文件(例如 .exe 文件)或一个动态链接库(例如 .dll 文件)。该函数返回指定模块的实例句柄,以便在后续的操作中使用。
以下是 GetModuleHandle 函数的一般形式:
HMODULE GetModuleHandle(
LPCTSTR lpModuleName
);
参数说明:
lpModuleName:指定要获取句柄的模块的名称。如果为NULL,则返回调用线程的可执行模块句柄。
在许多情况下,GetModuleHandle 主要用于获取当前进程的模块句柄,以便在后续的操作中使用该句柄。模块句柄通常用于在进程中查找资源、定位函数地址等目的。
FindResource
该函数用于定位并返回指定模块(通常是 .exe 或 .dll 文件)中的资源。资源可以是诸如位图、图标、对话框模板、字符串等等的数据。
以下是 FindResource 函数的一般形式:
HRSRC FindResource(
HMODULE hModule,
LPCTSTR lpName,
LPCTSTR lpType
);
参数说明:
hModule:指定包含资源的模块的句柄。如果为NULL,则表示使用当前可执行模块的句柄。lpName:指定资源的名称或标识符。可以是字符串或整数标识符。lpType:指定资源的类型。通常是一个字符串,如 "RT_BITMAP" 表示位图资源。
如果找到,则返回指向资源的句柄(HRSRC)。这个句柄可以用于后续的资源加载和操作,函数的第二个参数经常配合MAKEINTRESOURCE一起使用,MAKEINTRESOURCE 是一个宏(macro),用于将整数标识符(ID)转换为字符串指针。在 Windows 编程中,通常用于标识资源的 ID。
#define MAKEINTRESOURCE(i) ((LPCTSTR)((DWORD)((WORD)(i))))
这个宏接受一个整数参数 i,然后将其转换为字符串指针。在资源标识符上下文中,通常将整数标识符转换为字符串是为了在使用相关资源函数时传递正确的参数。
举个例子,如果有一个字符串资源的标识符是 IDR_MYSTRING,则可以使用 MAKEINTRESOURCE 将其转换为字符串:
LPCTSTR pszResourceName = MAKEINTRESOURCE(IDR_MYSTRING);
在这里,pszResourceName 将指向字符串 "IDR_MYSTRING"。
在前面提到的 FindResource 中,通常将 MAKEINTRESOURCE(IDR_MYSTRING) 作为 lpName 参数传递给 FindResource。这是因为 FindResource 函数期望资源名称是字符串类型,而 IDR_MYSTRING 可能是一个整数标识符。通过使用 MAKEINTRESOURCE,则可以将整数标识符转换为字符串,以便正确地在资源中查找。
SizeofResource
该函数用于获取指定资源的大小。它返回资源的字节数,可以用于确定加载资源所需的内存大小。
以下是 SizeofResource 函数的一般形式:
DWORD SizeofResource(
HMODULE hModule,
HRSRC hResInfo
);
参数说明:
hModule:指定包含资源的模块的句柄。如果为NULL,则表示使用当前可执行模块的句柄。hResInfo:指定资源的句柄,通常由FindResource返回。
SizeofResource 返回资源的大小,以字节为单位。这个函数在加载资源之前可以用来分配足够的内存空间。
LoadResource
该函数用于加载指定资源的数据。该函数返回一个全局内存块的句柄,该内存块包含了资源的实际数据,你可以通过 LockResource 函数获取该内存块的指针来访问资源数据。
以下是 LoadResource 函数的一般形式:
HGLOBAL LoadResource(
HMODULE hModule,
HRSRC hResInfo
);
参数说明:
hModule:指定包含资源的模块的句柄。如果为NULL,则表示使用当前可执行模块的句柄。hResInfo:指定资源的句柄,通常由FindResource返回。
LoadResource 用于将资源数据加载到全局内存块中,并返回该内存块的句柄。在加载资源后,可以使用 LockResource 函数获取指向资源数据的指针。
LockResource
用于获取指定资源的数据指针。它接受一个全局内存块的句柄,该内存块通常由 LoadResource 函数返回,然后返回一个指向资源数据的指针。
以下是 LockResource 函数的一般形式:
LPVOID LockResource(
HGLOBAL hResData
);
参数说明:
hResData:指定资源数据的全局内存块句柄,通常由LoadResource函数返回。
LockResource 用于锁定指定资源的全局内存块,并返回指向资源数据的指针。请注意,这个函数实际上并不执行拷贝,而是返回指向内存块的指针,因此对返回指针的任何修改都会直接影响到内存块本身。
FreeResource
用于释放由 LoadResource 函数加载的资源。这个函数通常用于释放不再需要的资源,以防止资源泄漏。
以下是 FreeResource 函数的一般形式:
BOOL FreeResource(
HGLOBAL hResData
);
参数说明:
hResData:指定要释放的全局内存块句柄,通常由LoadResource函数返回。
FreeResource 用于释放之前由 LoadResource 加载的资源。请注意,这个函数通常在资源的生命周期结束时调用,以确保释放资源占用的内存。但在实际应用中,现代 Windows 应用通常不需要显式调用 FreeResource,因为 Windows 会在程序退出时自动释放资源。
在实际的应用程序中,FindResource 可以与 LoadResource 和 LockResource 等函数一起使用,用于加载和操作资源数据。当数据资源被加载到内存之后则可以直接通过fwrite函数将其直接写出到磁盘中,以此来实现释放资源的目的。
代码功能实现
首先新建一个控制台程序以作为本次的测试环境,接着准备好我们需要写出的数据,这里就准备一个lyshark.ini配置文件,在项目中右键选择添加并添加资源,此时会弹出如下图所示的提示信息;

此时会弹出添加资源菜单,通过点击导入按钮并输入资源类型为LYSHARK点击确定保存这个更改,如下图所示;

此时我们在主程序中引入#include "resource.h"包含资源头文件,并修改FindResource中的特定位置使其指向我们导入的配置文件,在释放时同样需要保持fopen("map\\lyshark.ini", "wb+")配置文件的格式。

这段资源释放的完整代码如下所示;
#define _CRT_SECURE_NO_WARNINGS
#include <Windows.h>
#include <iostream>
#include <WinUser.h>
#include "resource.h"
BOOL UseCustomResource()
{
// 定位我们的自定义资源
HMODULE hModule = GetModuleHandle(NULL);
if (hModule == NULL)
{
std::cerr << "错误:获取模块句柄失败。" << std::endl;
return FALSE;
}
HRSRC hRsrc = FindResource(hModule, MAKEINTRESOURCE(IDR_LYSHARK1), TEXT("LYSHARK"));
if (hRsrc == NULL)
{
std::cerr << "错误:无法找到资源。" << std::endl;
return FALSE;
}
// 获取资源大小
DWORD dwSize = SizeofResource(hModule, hRsrc);
if (dwSize == 0)
{
std::cerr << "错误:无效的资源大小。" << std::endl;
return FALSE;
}
// 加载资源
HGLOBAL hGlobal = LoadResource(hModule, hRsrc);
if (hGlobal == NULL)
{
std::cerr << "错误:无法加载资源。" << std::endl;
return FALSE;
}
// 锁定资源
LPVOID lpVoid = LockResource(hGlobal);
if (lpVoid == NULL)
{
std::cerr << "错误:无法锁定资源。" << std::endl;
FreeResource(hGlobal); // 在返回前释放资源
return FALSE;
}
// 如果不存在,创建一个“map”目录
if (!CreateDirectory("map", NULL) && GetLastError() != ERROR_ALREADY_EXISTS)
{
std::cerr << "错误:无法创建目录。" << std::endl;
FreeResource(hGlobal);
return FALSE;
}
// 将资源写入文件
FILE* fp = fopen("map\\lyshark.ini", "wb+");
if (fp == NULL)
{
std::cerr << "错误:无法创建或打开文件。" << std::endl;
FreeResource(hGlobal);
return FALSE;
}
fwrite(lpVoid, sizeof(char), dwSize, fp);
fclose(fp);
// 释放资源
FreeResource(hGlobal);
return TRUE;
}
int main(int argc, char* argv[])
{
BOOL ref = UseCustomResource();
std::cout << "释放状态: " << ref << std::endl;
system("pause");
return 0;
}
以管理员模式运行上述程序,并等待,此时会释放一个目录并包含一个配置文件,如下图所示的输出结果;

结语
通过以上的代码实现,我们成功地将自定义资源提取并保存为一个独立的文件。这种技术在一些特殊情况下可能会很有用,例如需要动态加载或替换资源的情况。希望这篇博客对你理解如何使用 C++ 和 Windows API 进行资源操作有所帮助。
C/C++ 实现动态资源文件释放的更多相关文章
- 实现iOS图片等资源文件的热更新化(三):动态的资源文件夹
简介 此文,将尝试动态从某个不确定的文件夹中加载资源文件.文章,会继续完善自定义的 imageNamed 函数,并为下一篇文章铺垫. 这么做的意义 正如我们经常所说的那样,大多数情景知道做事的意义往往 ...
- VC释放EXE资源文件
原文地址:http://blog.csdn.net/wangningyu/article/details/4378378 今天有个朋友问到VC能否释放多个EXE.DLL或WAV等文件,我便做了个实例给 ...
- servlet基本原理(手动创建动态资源+工具开发动态资源)
一.手动开发动态资源 1 静态资源和动态资源的区别 静态资源: 当用户多次访问这个资源,资源的源代码永远不会改变的资源. 动态资源:当用户多次访问这个资源,资源的源代码可能会发送改变. <scr ...
- QT下资源使用和资源占用…(可以动态加载资源文件,这样不占内存)
原文地址:关于QT下资源使用和资源占用内存过多的问题作者:技术成就梦想 最近研究了一下如何从外部动态调用图片的问题,从而研究了图片资源的使用方法.网上最常见的帖子是这个,感觉总结的还不错. h ...
- 动态加载资源文件(ResourceDictionary)
原文:动态加载资源文件(ResourceDictionary) 在xaml中控件通过绑定静态资源StaticResource来获取样式Style有多种方式: 1.在项目的启动文件App中<App ...
- 不停服务,动态加载properties资源文件
系统运行过程中,我们用注解@Value("${****}")可以获取资源文件中的内 容,获取的内容会被存储在spring缓存中,因此如果我们修改了资源文件,要 想读取到修改后的内容 ...
- Linux磁盘空间被占用问题 (分区目录占用空间比实际空间要大: 资源文件删除后, 空间没有真正释放)
问题说明:IDC里的一台服务器的/分区使用率爆满了!已达到100%!经查看发现有个文件过大(80G),于是在跟有关同事确认后rm -f果断删除该文件.但是发现删除该文件后,/分区的磁盘空间压根没有释放 ...
- Style样式的四种使用(包括用C#代码动态加载资源文件并设置样式)
Posted on 2012-03-23 11:21 祥叔 阅读(2886) 评论(6) 编辑 收藏 在Web开发中,我们通过CSS来控制页面元素的样式,一般常用三种方式: 1. 内联样式 ...
- 如何在Qt资源文件中包含和释放exe等各种类型文件?
操作系统:Windows 10 X64 企业版 Qt: 5.8.0 QtCreater: 4.2.1 刚刚开始学习Qt,不断遇到困难和挑战,前几天在各个QQ群里询问如何在Qt的资源文件中包含和释放ex ...
- maven 打包时动态替换properties资源文件中的配置值
pom build节点下面添加resource配置: <resources> <resource> <directory>src/main/resources/&l ...
随机推荐
- 表格JS实现在线Excel的附件上传与下载
摘要:本文由葡萄城技术团队于博客园原创并首发.转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 前言 在本地使用Excel时,经常会有需要在Excel中添加一 ...
- CentOS7.9中的Glibc2.17源码编译升级到Glibc2.31
一.准备工作 1.配置yum阿里镜像源 查看yum当前配置的仓库,如果yum配置的不是阿里云源,请配置阿里云源. yum repolist all 验证是否能ping通阿里云 # 如果不能ping通可 ...
- 原神盲盒风格:AI绘画Stable Diffusion原神人物公仔实操:核心tag+lora模型汇总
本教程收集于:AIGC从入门到精通教程汇总 在这篇文章中,我们将深入探讨原神盲盒的艺术风格,以及如何运用AI绘画技术(Stable Diffusion)--来创造原神角色公仔.我们将通过实践操作让读者 ...
- GitHub Actions CI/CD 工作流实战
1. 什么是 GitHub Actions 与 workflow ? GitHub Actions 是 GitHub 提供的一种持续集成(CI)和持续部署(CD)的工具,用于自动化软件开发过程中的各种 ...
- 利用接口测试框架实现web状态的监控
之前,我们已经说明了如何实现一个我们的接口测试框架RATF,当然这个框架不止可以用于管理我们的接口测试代码,我们还可以用他来对我们的web进行简单粗暴的监控. 原理: 1. 通过使用配置文件,对要监控 ...
- 触动精灵生成的APK文件如何加固保护
触动精灵是一款模拟手机触摸.按键操作的软件,通过制作脚本,可以让触动精灵代替双手,自动执行一系列触摸.按键操作, 深受一些极客开发者喜爱. 触动精灵生成的APK文件自带了一些基础的加密,可以保护APK ...
- SQL函数升序Asc,降序Desc使用总结
关键字-升序Asc及降序Desc的使用语法 对某一结果集按列进行升序或降序排列即:结果集 Order by 列名/数字 Asc/Desc. 一.Asc,Desc排序讲以下5点 1.不写关键字Asc/D ...
- 《流畅的Python》 读书笔记 231007(第二章第一部分)
第2章 数据结构 ABC语言是Python的爸爸~ 很多点子在现在看来都很有 Python 风格:序列的泛型操作.内置的元组和映射类型.用缩进来架构的源码.无需变量声明的强类型 不管是哪种数据结构,字 ...
- ios ipa apple company 开发者账号申请分享攻略
ios公司开发者账号申请分享攻略 好不容易终于申请下来了ios 公司开发者账号,真是一路艰辛和漫长啊,特别是对于远在大洋彼岸的大中华国家.以下我就分享一下这一路下来的经验,希望对于那些新手同仁们有所帮 ...
- 调节LED的亮度
虽然Devices Plus已经介绍过Arduino的多种应用和作品示例,但是了解相关基础知识对于任何项目的构建仍然是非常重要的!这次,我们将为您介绍Arduino电子制作的基础知识.此次的分享嘉宾是 ...