UpdateResource系列函数用法

最近用到了需要实时更新exe和dll中的资源文件,网上查了很多资料,做一些总结,方便下次使用。


UpdateResource这个函数主要用于添加、删除或者替换PE文件中的资源,它的原型为

BOOL WINAPI UpdateResource(
  _In_      HANDLE hUpdate,
  _In_      LPCTSTR lpType,
  _In_      LPCTSTR lpName,
  _In_      WORD wLanguage,
  _In_opt_  LPVOID lpData,
  _In_      DWORD cbData
);

我用两个例子来讲解下这个替换的使用,一个是更新自定义资源文件,一个是用的比较多的就是更新版本信息。


更新自定义资源文件,首先是对资源文件的读取,然后就是写入。读取的时候先通过LoadLibrary得到文件的HMODULE,然后通过FindResource获取到文件中的资源信息,FindResource需要提供HMODULE和资源的类型以及名字,这个一般在资源的头文件中有定义,通过exeScope也可查到,比如ABC   PNG   “abc.png”,这一行中,ABC就是名字,而PNG就是类型,而”abc.png“是你实际的文件名。FindResource得到一个HRSRC的句柄后,就可以通过LoadResource来载入这个资源文件,接着LockResource可以提供上一步的结果缓存指针,然后大家就可以将这段缓存保存下来,进行自己的处理,这样就得到了PE文件中的自定义资源的内容。当然,在保存的时候如果用memcpy的话,需要提供资源缓存的大小,可以通过SizeofResource这个函数来获取大小。其实,系统定义的文件也可以这样做,只是将类型名改为系统的就行了,比如BMP,STRING等等。

下面是我写的读取文件的函数,请指正:

BOOL GetResourceFromFile(LPCTSTR lpszFile, LPCTSTR lpName, LPCTSTR lpType, LPBYTE lpOut)
{
	if( NULL == lpszFile || !PathFileExists(lpszFile) || NULL == lpName || NULL == lpType )
		return FALSE;

	BOOL bRet = FALSE;

	HMODULE hFile = LoadLibrary( lpszFile );
	if( NULL == hFile )
		return bRet;

	HRSRC hRsrc = FindResource( hFile, lpName, lpType );
	if( NULL == hRsrc )
		return bRet;

	HGLOBAL hGlobal = LoadResource( hFile, hRsrc );
	if( NULL == hGlobal )
		return bRet;

	LPBYTE lpBuffer = (LPBYTE)LockResource( hGlobal );
	if( NULL == lpBuffer)
		return bRet;

	DWORD dwSize = SizeofResource( hFile, hRsrc );
	memcpy( lpOut, lpBuffer, dwSize );
	bRet = TRUE;

	FreeLibrary( hFile );

	return bRet;
}


写入资源文件也比较简单。先通过GetFileVersionInfoSize得到资源文件的大小,然后根据大小开辟一段缓存,通过GetFileVersionInfo来填充这段缓存,接着调用BeginUpdateResource来得到资源的句柄,这样就可以开始更新了。但是更新之前还需要获取当前的语言版本,这步不能少,所以用VerQueryValue来传入“\\VarFileInfo\\Translation”就可以得到语言版本。VerQueryValue可以查询三种信息,我们一会就会讲到。得到语言版本后,就可以UpdateResource来更新资源了,最后需要再调用EndUpdateResource来结束更新。还是附上源码参考:

BOOL UpdateDllFile(LPCTSTR lpszFile, LPBYTE pIniBuf, LPCTSTR lpType, LPCTSTR lpName)
{
	if( NULL == lpszFile || !PathFileExists(lpszFile) || NULL == pIniBuf || NULL == lpType || NULL == lpName )
		return FALSE;

	BOOL bRet = FALSE;
	DWORD dwBufSize = strlen( (char*)pIniBuf );
	DWORD dwSize = 0;
	DWORD dwHandle = 0;

	dwSize = GetFileVersionInfoSize(lpszFile, &dwHandle);
	if( 0 >= dwSize)
		return bRet;

	LPBYTE lpBuffer = new BYTE[dwSize];
	memset( lpBuffer, 0, dwSize );

	if (GetFileVersionInfo(lpszFile, dwHandle, dwSize, lpBuffer) != FALSE)
	{
		HANDLE hResource = BeginUpdateResource(lpszFile, FALSE);
		if( NULL != hResource )
		{
			UINT uTemp = 0;
			if (VerQueryValue(lpBuffer, _T("\\VarFileInfo\\Translation"), (LPVOID *)&lpTranslate, &uTemp) != FALSE)
			{
				if( FALSE != UpdateResource( hResource, lpType, lpName, lpTranslate->wLanguage, (LPVOID)pIniBuf, dwBufSize) )
					if (EndUpdateResource(hResource, FALSE) != FALSE)
						bRet = TRUE;
			}
		}
	}

	return bRet;
}

对了,资源类型如果是一个整形的话,一般用MAKEINTRESOURCE来转换。比如我自定义的资源类型为2000,传入lpType的时候就可以用MAKEINTRESOURCE(2000)。顺便提供下lpTranslate的定义:

struct
{
	WORD wLanguage;
	WORD wCodePage;
} *lpTranslate;


下面讲下更新PE文件中版本信息。版本信息主要有两个,一个是数字形式的,一个是字符串形式的。以版本号为例,最开始我更新的时候只注意到了数字形式,修改了之后发现有些信息还是原来的,后来才查到还需要修改字符串形式,否则显示的仍然是原来的版本号。我下面也只讲下版本号的修改,其他都是一样的。

首先获取数字形式的版本号。之前还是需要先GetFileVersionInfoSize和GetFileVersionInfoSize来得到资源,并BeginUpdateResource获取资源句柄,这些可以参考上面的代码。然后得到语言版本,也是上面有的代码,VerQueryValue并传入“\\VarFileInfo\\Translation”。对于数字的版本号,我们可以通过VerQueryValue并传入“\\”来获取,这个时候我们将得到的缓存转换为VS_FIXEDFILEINFO* 形式的指针,就可以得到版本信息了。这个VS_FIXEDFILEINFO结构体里面dwFileVersionMS和dwFileVersionLS信息是文件版本号,dwProductVersionLS和dwProductVersionMS是产品版本号,MS是主版本号,LS是副版本号。更改之后再调用UpdateResource传入缓存指针就可以了。

另外一个就是字符串形式的版本号。字符串的获取是通过VerQueryValue传入“\\StringFileInfo\\语言版本\\需要的信息”来获取的,里面的“语言版本“需要已16进制格式化上面的lpTranslate结构体后得到。"需要的信息"是你想要查询的,这个在VerQueryValue的介绍里面也有,用exeScope打开一个PE文件,就可以查到,附图:


UpdateResource系列函数用法_第1张图片

这里面可以看到语言版本是080404b0,我们如果想得到文件版本信息,则传入\\StringFileInfo\\080404b0\\FileVersionName,如果是产品版本号,则是\\StringFileInfo\\080404b0\\ProduceVersion,等等,就不一一举例了。

得到我们想要的字符串信息的缓存之后,很好办了,直接替换这段缓存,然后UpdateResource回去就可以了。最后记得EndUpdateResource。最后附下修改版本信息的源码吧:

BOOL UpdateDllFile(LPCTSTR lpszFile)
{
	if( NULL == lpszFile || !PathFileExists(lpszFile) )
		return FALSE;

	BOOL bRet = FALSE;
	DWORD dwHandle = 0;
	DWORD dwSize = 0;

	dwSize = GetFileVersionInfoSize(lpszFile, &dwHandle);
	if( 0 >= dwSize)
		return bRet;

	LPBYTE lpBuffer = new BYTE[dwSize];
	memset( lpBuffer, 0, dwSize );

	if (GetFileVersionInfo(lpszFile, dwHandle, dwSize, lpBuffer) != FALSE)
	{
		HANDLE hResource = BeginUpdateResource(lpszFile, FALSE);
		if (NULL != hResource)
		{
			UINT uTemp;
			DWORD dwVer[4] = {0};

			if (VerQueryValue(lpBuffer, _T("\\VarFileInfo\\Translation"), (LPVOID *)&lpTranslate, &uTemp) != FALSE)
			{
				// 修改版本信息,给副版本号加1
				LPVOID lpFixedBuf = NULL;
				DWORD dwFixedLen = 0;
				if( FALSE != VerQueryValue( lpBuffer, _T("\\"), &lpFixedBuf, (PUINT)&dwFixedLen ))
				{
					VS_FIXEDFILEINFO* pFixedInfo = (VS_FIXEDFILEINFO*)lpFixedBuf;

					pFixedInfo->dwFileVersionLS    = pFixedInfo->dwFileVersionLS + 0x1;
					pFixedInfo->dwProductVersionLS = pFixedInfo->dwProductVersionLS + 0x1;

					dwVer[0] = HIWORD(pFixedInfo->dwFileVersionMS);
					dwVer[1] = LOWORD(pFixedInfo->dwFileVersionMS);
					dwVer[2] = HIWORD(pFixedInfo->dwFileVersionLS);
					dwVer[3] = LOWORD(pFixedInfo->dwFileVersionLS);
				}

				// 修改版本的文本信息
				LPVOID lpStringBuf = NULL;
				DWORD dwStringLen = 0;
				TCHAR szTemp[MAX_PATH] = {0};
				TCHAR szVersion[MAX_PATH] = {0};

				_stprintf_s( szTemp, MAX_PATH - 1, _T("\\StringFileInfo\\%04x%04x\\FileVersion"), lpTranslate->wLanguage, lpTranslate->wCodePage );
				_stprintf_s( szVersion, MAX_PATH - 1, _T("%d, %d, %d, %d"), dwVer[0], dwVer[1], dwVer[2], dwVer[3] );

				if( FALSE != VerQueryValue( lpBuffer, szTemp, &lpStringBuf, (PUINT)&dwStringLen ) )
					memcpy( lpStringBuf, szVersion, (_tcslen(szVersion) + 1) * sizeof(TCHAR) );

				memset( szTemp, 0, sizeof(szTemp) );
				_stprintf_s( szTemp, MAX_PATH - 1, _T("\\StringFileInfo\\%04x%04x\\ProductVersion"), lpTranslate->wLanguage, lpTranslate->wCodePage );

				if( FALSE != VerQueryValue( lpBuffer, szTemp, &lpStringBuf, (PUINT)&dwStringLen ) )
					memcpy( lpStringBuf, szVersion, (_tcslen(szVersion) + 1) * sizeof(TCHAR) );

				// 更新
				if (UpdateResource(hResource, RT_VERSION, MAKEINTRESOURCE(VS_VERSION_INFO), lpTranslate->wLanguage, lpBuffer, dwSize) != FALSE)
				{
					if (EndUpdateResource(hResource, FALSE) != FALSE)
						bRet = TRUE;
				}
			}
		}
	}

	if( lpBuffer )
		delete [] lpBuffer;

	return bRet;
}

水平有限,请指正~~~~~




你可能感兴趣的:(UpdateResource系列函数用法)