首页 > 编程语言 >C/C++ 实现动态资源文件释放

C/C++ 实现动态资源文件释放

时间:2023-12-08 19:55:05浏览次数:41  
标签:释放 函数 句柄 LoadResource 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 可以与 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 进行资源操作有所帮助。

标签:释放,函数,句柄,LoadResource,C++,内存,模块,动态,资源
From: https://www.cnblogs.com/LyShark/p/17888926.html

相关文章

  • mybatis动态sql将字符串转换成数字类型报错
    报错信息org.mybatis.spring.MyBatisSystemException:nestedexceptionisorg.apache.ibatis.exceptions.PersistenceException: ###Errorqueryingdatabase.Cause:java.lang.NumberFormatException:Forinputstring:"xxx" ###Cause:java.lang.NumberFo......
  • 通过数据驱动设计动态的全面预算管理架构
    在过去的20年里,电子表格一直是企业用于规划、预测、预算和管理报告的主要工具,尽管有的企业具备针对财务的系统,但其应用效率和规划技术仍然难以满足市场需求。并且,大部分企业对于财务管理的部署成本相对较低,其可访问性、安全性、及时性都难以保障,随着企业在用户和信息需求方面的增长......
  • JetBrains CLion 2023.3 (macOS, Linux, Windows) - C 和 C++ 跨平台 IDE
    JetBrainsCLion2023.3(macOS,Linux,Windows)-C和C++跨平台IDE请访问原文链接:https://sysin.org/blog/jb-clion-2023/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgJetBrainsCLion-C和C++跨平台IDECLion2022现已发布。查看最新变化用于强大语......
  • 静态HTTP和动态HTTP的区别:理解二者的优势和局限
    在互联网的世界里,HTTP(HypertextTransferProtocol)是当之无愧的“交通规则”。它负责在浏览器和服务器之间传输数据,让你可以在网页上浏览、互动和下载内容。根据动态和静态的不同,HTTP网站可以分为静态HTTP网站和动态HTTP网站。这两种类型网站都有其特定的优势和局限。静态HTTP网站:......
  • 静态HTTP和动态HTTP的混合使用:最佳实践
    在当今的互联网环境中,静态HTTP和动态HTTP各有其优势和局限。静态HTTP具有速度快、安全性高和易于维护的特点,而动态HTTP则能够实现动态交互和处理大量动态数据。为了充分利用两者的优势,越来越多的网站开始采用静态HTTP和动态HTTP混合使用的模式。本文将探讨这种混合模式的最佳实践。......
  • 使用静态HTTP提供动态内容
    在Web开发中,静态HTTP和动态HTTP常被视作两种截然不同的技术。静态HTTP主要用于传输不变的内容,如HTML、CSS和JavaScript文件,而动态HTTP则能处理用户交互、实时数据等动态需求。但鲜为人知的是,我们其实可以通过一些技巧,用静态HTTP来提供动态内容。本文将深入探讨这一主题。首先,为什么......
  • C++(智能指针)
    在C++中,智能指针是一种用于管理动态分配内存的智能化工具。它们提供了对动态分配资源的自动管理,以减少内存泄漏和资源泄漏的风险。C++标准库提供了两种主要的智能指针类型:std::shared_ptr和std::unique_ptr。以下是这两种智能指针的基本解释:1.std::shared_ptr:std::shared_......
  • ACPM高效C++组件管理让音视频终端SDK性能更好、稳定性更高
    本专栏将分享阿里云视频云MediaBox系列技术文章,深度剖析音视频开发利器的技术架构、技术性能、开发能效和最佳实践,一起开启音视频的开发之旅。本文为MediaBox技术架构篇,重点从 ACPM介绍、技术架构以及高效管理等方面,介绍如何通过MediaBoxACPM的高效组件管理,来提升音视频终端SDK......
  • C++(template)
    这是C++中的模板声明,用于定义一个通用的模板(template)。这种模板可以用于创建通用的、类型无关的代码,使得可以在不同的数据类型上使用相同的算法或数据结构。具体而言,template关键字声明了一个模板,typenameT则是模板参数声明,表示一个未知的类型T,可以在模板的定义中使用T代......
  • C++(extern)
    在C++中,extern是一个关键字,用于说明某个变量或函数是在其他文件中定义的,而不是在当前文件中定义。extern的主要作用是进行外部链接性的声明。1.外部变量的声明:extern可以用于声明在其他文件中定义的全局变量,以便在当前文件中使用这些变量。//文件1.cppintglobalVariab......