当我们开发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 可以与 LoadResourceLockResource 等函数一起使用,用于加载和操作资源数据。当数据资源被加载到内存之后则可以直接通过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++ 实现动态资源文件释放的更多相关文章

  1. 实现iOS图片等资源文件的热更新化(三):动态的资源文件夹

    简介 此文,将尝试动态从某个不确定的文件夹中加载资源文件.文章,会继续完善自定义的 imageNamed 函数,并为下一篇文章铺垫. 这么做的意义 正如我们经常所说的那样,大多数情景知道做事的意义往往 ...

  2. VC释放EXE资源文件

    原文地址:http://blog.csdn.net/wangningyu/article/details/4378378 今天有个朋友问到VC能否释放多个EXE.DLL或WAV等文件,我便做了个实例给 ...

  3. servlet基本原理(手动创建动态资源+工具开发动态资源)

    一.手动开发动态资源 1 静态资源和动态资源的区别 静态资源: 当用户多次访问这个资源,资源的源代码永远不会改变的资源. 动态资源:当用户多次访问这个资源,资源的源代码可能会发送改变. <scr ...

  4. QT下资源使用和资源占用…(可以动态加载资源文件,这样不占内存)

    原文地址:关于QT下资源使用和资源占用内存过多的问题作者:技术成就梦想     最近研究了一下如何从外部动态调用图片的问题,从而研究了图片资源的使用方法.网上最常见的帖子是这个,感觉总结的还不错. h ...

  5. 动态加载资源文件(ResourceDictionary)

    原文:动态加载资源文件(ResourceDictionary) 在xaml中控件通过绑定静态资源StaticResource来获取样式Style有多种方式: 1.在项目的启动文件App中<App ...

  6. 不停服务,动态加载properties资源文件

    系统运行过程中,我们用注解@Value("${****}")可以获取资源文件中的内 容,获取的内容会被存储在spring缓存中,因此如果我们修改了资源文件,要 想读取到修改后的内容 ...

  7. Linux磁盘空间被占用问题 (分区目录占用空间比实际空间要大: 资源文件删除后, 空间没有真正释放)

    问题说明:IDC里的一台服务器的/分区使用率爆满了!已达到100%!经查看发现有个文件过大(80G),于是在跟有关同事确认后rm -f果断删除该文件.但是发现删除该文件后,/分区的磁盘空间压根没有释放 ...

  8. Style样式的四种使用(包括用C#代码动态加载资源文件并设置样式)

    Posted on 2012-03-23 11:21 祥叔 阅读(2886) 评论(6) 编辑 收藏 在Web开发中,我们通过CSS来控制页面元素的样式,一般常用三种方式: 1.       内联样式 ...

  9. 如何在Qt资源文件中包含和释放exe等各种类型文件?

    操作系统:Windows 10 X64 企业版 Qt: 5.8.0 QtCreater: 4.2.1 刚刚开始学习Qt,不断遇到困难和挑战,前几天在各个QQ群里询问如何在Qt的资源文件中包含和释放ex ...

  10. maven 打包时动态替换properties资源文件中的配置值

    pom build节点下面添加resource配置: <resources> <resource> <directory>src/main/resources/&l ...

随机推荐

  1. 让 GPT-4 来修复 Golang “数据竞争”问题 - 每天5分钟玩转 GPT 编程系列(6)

    目录 1. Golang 中的"数据竞争" 2. GoPool 中的数据竞争问题 3. 让 GPT-4 来修复数据竞争问题 3.1 和 GPT-4 的第一轮沟通 3.2 和 GPT ...

  2. 论文解读(AdSPT)《Adversarial Soft Prompt Tuning for Cross-Domain Sentiment Analysis》

    Note:[ wechat:Y466551 | 可加勿骚扰,付费咨询 ] 论文信息 论文标题:Adversarial Soft Prompt Tuning for Cross-Domain Senti ...

  3. 原来你是这样的JAVA[05]--String

    1.从概念上讲,java字符串就是Unicode字符串. 2.字符串拼接 用指定分隔符拼接字符串数组时,使用StringJoiner或者String.join()更方便: 用StringJoiner拼 ...

  4. Java下载多个网络文件并打成压缩包

    需求:浏览器访问后台的http地址后,后台将多个网络文件打成压缩包返回给浏览器,用户可以通过浏览器直接下载压缩包. 实现: 根据文件链接把文件下载下来并且转成字节码  ,代码: package com ...

  5. Denpendcy Injection 8.0新功能——KeyedService

    Denpendcy Injection 8.0新功能--KeyedService 本文只介绍 .NET Denpendcy Injection 8.0新功能--KeyedService,假定读者已熟练 ...

  6. 整理php防注入和XSS攻击通用过滤

    对网站发动XSS攻击的方式有很多种,仅仅使用php的一些内置过滤函数是对付不了的,即使你将filter_var,mysql_real_escape_string,htmlentities,htmlsp ...

  7. [NSSCTF 2022 Spring Recruit]babyphp

    打开靶机,先看了一遍代码,发现要拿到flag,就必须先满足三个条件,即分别为a,b,c 第一层:需要a满足条件 isset($_POST['a'])&&!preg_match('/[0 ...

  8. Anaconda虚拟环境配置Python库与Spyder编译器

      本文介绍在Anaconda中,为Python的虚拟环境安装第三方库与Spyder等配套软件的方法.   在文章创建Anaconda虚拟Python环境的方法中,我们介绍了在Anaconda环境下, ...

  9. Chiplet解决芯片技术发展瓶颈

    这是IC男奋斗史的第38篇原创 本文1776字,预计阅读4分钟. Chiplet封装是什么 介绍Chiplet前,先说下SOC.Chiplet和SOC是两个相互对立的概念,刚好可以用来互为参照. SO ...

  10. Linux 回收站

    聊一聊执行 rm -rf 数据恢复以及建立 Linux 回收站 误删除 rm -rf 如果在Linux 平台下,执行 rm -rf 误删除文件,我们可以做哪些数据恢复的工作以及我们该如何应对不小心删除 ...