【C++】代码实现:程序运行时释放自包含资源

一、概述:

如果程序额外需要加载一些 DLL 文件、文本文件、图片文件,或者其他的音/视频文件等,则可以把它们作为资源插入到程序里,等到程序运行后,再把它们释放到本地。

这样做的好处是编译出来的程序只有一个 exe 文件,而不需要附带其他文件,因而程序变更很简洁。

二、实现原理:

Windows 操作系统提供了一系列带有操作资源的 WIN32 API 函数。所以,程序实现也是基于这些 WIN32 API 函数进行操作的。

实现步骤:

1、通过 FindResource 定位程序里的资源,主要是根据“资源类型”和“资源名称”进行定位,从而获取资源信息块的句柄。

2、根据上面获取的资源信息块的句柄,利用 SizeofResource 获取资源的大小后,再通过 LoadResource 把资源加载到程序内存中。

3、通过 LockResource 锁定加载到内存中的资源,防止程序中的其他操作影响这块内存。其中,返回值就是资源在进程内存中的起始地址。

4、最后,根据资源大小以及进程内存的起始地址,可将资源数据读取出来并保存为本地文件。

经过上述 4 个步骤,便可以定位出资源,并将其释放到本地硬盘。它的原理就是通过 PE 文件结构,确定资源在 PE 文件中的偏移和大小。

 

注意:


在资源释放过程中,要特别注意:必须明确资源所在的模块,要指明所在模块句柄并且统一。因为文件可以以资源的形式插入到 DLL 文件中,所以当 DLL 加载到其他进程时,资源所在模块仍是该 DLL 模块。

要想成功释放资源,则需要先通过 GetModualHandle 函数获取该 DLL 模块的句柄。否则,资源释放会因为指定了错误模块而失败。


三、主要代码:

  • ResourceFree.h
#ifndef _RESOURCE_FREE_H_
#define _RESOURCE_FREE_H_

#include <windows.h>

BOOL FreeMyResource(UINT uiResouceName, char *lpszResourceType, char *lpszSaveFileName);

BOOL GetResourceInfo(UINT uiResouceName, char *lpszResourceType, PVOID *ppVoid, DWORD *pdwResSize);

BOOL ChangeIcon(char *pszChangedIconExeFileName, char *pszSrcIconExeFileName);

BOOL ChangeExeRes(char *pszSrcFileName, char *pszInstallFileName, UINT uiDestResId, char *pszDestResType);

#endif
  • ResourceFree.cpp
#include "stdafx.h"
#include "ResourceFree.h"

void FreeRes_ShowError(char *pszText)
{
	char szErr[MAX_PATH] = { 0 };
	::wsprintf(szErr, "%s Error[%d]\n", pszText, ::GetLastError());
#ifdef _DEBUG
	::MessageBox(NULL, szErr, "ERROR", MB_OK | MB_ICONERROR);
#endif
}

BOOL FreeMyResource(UINT uiResouceName, char *lpszResourceType, char *lpszSaveFileName)
{
	// 获取指定模块里的指定资源
	HRSRC hRsrc = ::FindResource(NULL, MAKEINTRESOURCE(uiResouceName), lpszResourceType);
	if (NULL == hRsrc)
	{
		FreeRes_ShowError("FindResource");
		return FALSE;
	}
	// 获取资源的大小
	DWORD dwSize = ::SizeofResource(NULL, hRsrc);
	if (0 >= dwSize)
	{
		FreeRes_ShowError("SizeofResource");
		return FALSE;
	}
	// 将资源加载到内存里
	HGLOBAL hGlobal = ::LoadResource(NULL, hRsrc);
	if (NULL == hGlobal)
	{
		FreeRes_ShowError("LoadResource");
		return FALSE;
	}
	// 锁定资源
	LPVOID lpVoid = ::LockResource(hGlobal);
	if (NULL == lpVoid)
	{
		FreeRes_ShowError("LockResource");
		return FALSE;
	}

	// 保存资源为文件
	FILE *fp = NULL;
	fopen_s(&fp, lpszSaveFileName, "wb+");
	if (NULL == fp)
	{
		FreeRes_ShowError("LockResource");
		return FALSE;
	}
	fwrite(lpVoid, sizeof(char), dwSize, fp);
	fclose(fp);

	return TRUE;
}

BOOL GetResourceInfo(UINT uiResouceName, char *lpszResourceType, PVOID *ppVoid, DWORD *pdwResSize)
{
	HRSRC hRsrc = ::FindResource(NULL, MAKEINTRESOURCE(uiResouceName), lpszResourceType);
	if (NULL == hRsrc)
	{
		FreeRes_ShowError("FindResource");
		return FALSE;
	}
	DWORD dwSize = ::SizeofResource(NULL, hRsrc);
	if (0 >= dwSize)
	{
		FreeRes_ShowError("SizeofResource");
		return FALSE;
	}
	HGLOBAL hGlobal = ::LoadResource(NULL, hRsrc);
	if (NULL == hGlobal)
	{
		FreeRes_ShowError("LoadResource");
		return FALSE;
	}
	LPVOID lpVoid = ::LockResource(hGlobal);
	if (NULL == lpVoid)
	{
		FreeRes_ShowError("LockResource");
		return FALSE;
	}

	// 返回
	*ppVoid = lpVoid;
	*pdwResSize = dwSize;

	return TRUE;
}

BOOL ChangeIcon(char *pszChangedIconExeFileName, char *pszSrcIconExeFileName)
{
	// 将在其他程序,并获取程序模块句柄
	HMODULE hEXE = ::LoadLibrary(pszSrcIconExeFileName);
	if (NULL == hEXE)
	{
		FreeRes_ShowError("LoadLibrary");
		return FALSE;
	}
	// 获取其他EXE程序图标资源数据
	HRSRC hRsrc = ::FindResource(hEXE, (LPCSTR)1, RT_ICON);
	if (NULL == hRsrc)
	{
		FreeRes_ShowError("FindResource");
		return FALSE;
	}
	// 获取资源大小
	DWORD dwSize = ::SizeofResource(hEXE, hRsrc);
	if (0 >= dwSize)
	{
		FreeRes_ShowError("SizeofResource");
		return FALSE;
	}
	// 加载资源到程序内存
	HGLOBAL hGlobal = ::LoadResource(hEXE, hRsrc);
	if (NULL == hGlobal)
	{
		FreeRes_ShowError("LoadResource");
		return FALSE;
	}
	// 锁定资源内存
	LPVOID lpVoid = ::LockResource(hGlobal);
	if (NULL == lpVoid)
	{
		FreeRes_ShowError("LockResource");
		return FALSE;
	}

	// 开始修改图标
	HANDLE hUpdate = ::BeginUpdateResource(pszChangedIconExeFileName, FALSE);
	if (NULL == hUpdate)
	{
		FreeRes_ShowError("BeginUpdateResource");
		return FALSE;
	}
	// 如果资源ID存在, 则替换资源; 否则创建资源
	// 程序把ICON的最小的资源ID作为程序图标, 所以从1开始, 1最小
	BOOL bRet = ::UpdateResource(hUpdate, RT_ICON, (LPCSTR)1, LANG_NEUTRAL, lpVoid, dwSize);
	if (FALSE == bRet)
	{
		FreeRes_ShowError("UpdateResource");
		return FALSE;
	}

	::EndUpdateResource(hUpdate, FALSE);
	// 释放模块
	::FreeLibrary(hEXE);

	return TRUE;
}

BOOL ChangeExeRes(char *pszSrcFileName, char *pszInstallFileName, UINT uiDestResId, char *pszDestResType)
{
	BYTE *pData = NULL;
	DWORD dwDataSize = 0;
	// 打开目标文件获取数据
	HANDLE hFile = ::CreateFile(pszInstallFileName, GENERIC_READ,
		FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
		FILE_ATTRIBUTE_ARCHIVE, NULL);
	if (INVALID_HANDLE_VALUE == hFile)
	{
		FreeRes_ShowError("CreateFile");
		return FALSE;
	}
	// 获取文件大小
	dwDataSize = ::GetFileSize(hFile, NULL);
	// 申请动态内存
	pData = new BYTE[dwDataSize];
	if (NULL == pData)
	{
		FreeRes_ShowError("new");
		return FALSE;
	}
	// 读取数据
	DWORD dwRet = 0;
	if (FALSE == ::ReadFile(hFile, pData, dwDataSize, &dwRet, NULL))
	{
		FreeRes_ShowError("ReadFile");
		return FALSE;
	}
	// 关闭文件句柄
	::CloseHandle(hFile);

	// 更改资源
	HANDLE hUpdate = ::BeginUpdateResource(pszSrcFileName, FALSE);
	if (NULL == hUpdate)
	{
		FreeRes_ShowError("BeginUpdateResource");
		return FALSE;
	}

	// 如果资源ID存在, 则替换资源; 否则创建资源
	BOOL bRet = ::UpdateResource(hUpdate, pszDestResType, (LPCSTR)uiDestResId, LANG_NEUTRAL, pData, dwDataSize);
	if (FALSE == bRet)
	{
		FreeRes_ShowError("UpdateResource");
		return FALSE;
	}

	::EndUpdateResource(hUpdate, FALSE);

	// 释放内存
	delete[]pData;
	pData = NULL;

	return TRUE;
}

四、调用示例:

	char szSaveName[MAX_PATH] = "mydata.ttt";

	// 释放资源
	BOOL bRet = FreeMyResource(IDR_MYRES2, "MYRES", szSaveName);
	if (FALSE == bRet)
	{
		::MessageBox(NULL, "Free Resource Error!", "ERROR", MB_OK);
	}
	else
	{
		::MessageBox(NULL, "Free Resource OK!", "OK", MB_OK);
	}

五、Visual Studio 2017 项目配置:

  • Debug / Win32 配置:

  • Release / Win32:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值