如果程序额外需要加载一些 DLL 文件、文本文件、图片文件,或者其他的音/视频文件等,则可以把它们作为资源插入到程序里,等到程序运行后,再把它们释放到本地。
这样做的好处是编译出来的程序只有一个 exe 文件,而不需要附带其他文件,因而程序变更很简洁。
Windows 操作系统提供了一系列带有操作资源的 WIN32 API 函数。所以,程序实现也是基于这些 WIN32 API 函数进行操作的。
实现步骤:
1、通过 FindResource 定位程序里的资源,主要是根据“资源类型”和“资源名称”进行定位,从而获取资源信息块的句柄。
2、根据上面获取的资源信息块的句柄,利用 SizeofResource 获取资源的大小后,再通过 LoadResource 把资源加载到程序内存中。
3、通过 LockResource 锁定加载到内存中的资源,防止程序中的其他操作影响这块内存。其中,返回值就是资源在进程内存中的起始地址。
4、最后,根据资源大小以及进程内存的起始地址,可将资源数据读取出来并保存为本地文件。
经过上述 4 个步骤,便可以定位出资源,并将其释放到本地硬盘。它的原理就是通过 PE 文件结构,确定资源在 PE 文件中的偏移和大小。
在资源释放过程中,要特别注意:必须明确资源所在的模块,要指明所在模块句柄并且统一。因为文件可以以资源的形式插入到 DLL 文件中,所以当 DLL 加载到其他进程时,资源所在模块仍是该 DLL 模块。
要想成功释放资源,则需要先通过 GetModualHandle 函数获取该 DLL 模块的句柄。否则,资源释放会因为指定了错误模块而失败。
#ifndef _RESOURCE_FREE_H_
#define _RESOURCE_FREE_H_
#include
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
#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);
}