You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

667 lines
15 KiB

/**************************************************************************
THIS CODE AND INFORMATION IS PROVIDED 'AS IS' WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright 1998 Microsoft Corporation. All Rights Reserved.
**************************************************************************/
/**************************************************************************
File: strblock.c
Description: Implements the functions that manipulate a string block.
**************************************************************************/
#include <windows.h>
#include "strblock.h"
// The format of string resources is explained below.
//
// The smallest granularity of string resource that can be loaded/updated is a block.
// Each block is identified by an ID, starting with 1. You need to use the block ID
// when calling FindResource(), LoadResource(), UpdateResource().
//
// A string with ID, nStringID, is in the block with ID, nBlockID, given by the following
// formula:
// nBlockID = (nStringID / 16) + 1; // Note integer division.
//
// A block of string resource is laid out as follows:
// Each block has NO_OF_STRINGS_PER_BLOCK (= 16) strings. Each string is represented as
// an ordered pair, (LENGTH, TEXT). The LENGTH is a WORD that specifies the size, in terms
// of number of characters, of the string that follows. TEXT follows the LENGTH and is
// a sequence of UNICODE characters, NOT terminated by a NULL character.Any TEXT may be of
// zero-length, in which case, LENGTH is zero.
//
// An executable does not have a string table block with ID, nBlockID, if it does not have any
// strings with IDs - ((nBlockID - 1) * 16) thru' ((nBlockID * 16) - 1).
//
// This format is the same for Windows NT, Windows 95 & Windows 98. Yes, strings in a resource
// are internally stored in UNICODE format even in Windows 95 & Windows 98.
// Internal data structure format for a string block.
// Our block of strings has as an array of UNICODE string pointers.
typedef struct tagSTRINGBLOCK
{
UINT nBlockID; // The ID of the block.
WORD wLangID; // The language ID.
LPWSTR strArray[NO_OF_STRINGS_PER_BLOCK]; // We maintain the strings
// internally in UNICODE.
} STRINGBLOCK, * PSTRINGBLOCK;
// A thread-specific error number for the last block operation.
__declspec(thread) STRBLOCKERR g_strBlockErr = STRBLOCKERR_OK;
// Set the error code.
void SetBlockError( STRBLOCKERR err ) { g_strBlockErr = err; }
// Forward declarations.
// Create a string block & return the pointer to the block. Return NULL on failure.
// Sets the error code.
PSTRINGBLOCK CreateBlock( HINSTANCE hInstLib, UINT nBlockID, WORD wLangID );
// Parse the string block resource pointed at by, pParse, and fill the strings in pStrBlock.
BOOL ParseRes( LPVOID pRes, PSTRINGBLOCK pStrBlock );
// Get the size of the raw string block resource in the given block.
DWORD GetResSize( PSTRINGBLOCK pStrBlock );
// Update a block of string in the specified library.
// hUpdate specifies the update-file handle. This handle is returned by the BeginUpdateResource.
// pStrBlock contains the new strings.
// nBlockID specifies the ID of the block. Use the same block ID as of pStrBlock if this value is -1.
// wlangID specifies the language ID of the block. Use the same language ID as of pStrBlock, if this value is 0.
// Returns TRUE on success and FALSE on failure.
// Sets the error code.
BOOL UpdateBlock( HANDLE hUpdate, PSTRINGBLOCK pStrBlock, int nBlockID, WORD wLangID );
// Use the strings in the block, pStrBloc, and build a buffer whose format matches that of the
// string resource block that can be used to update string resource.
// pRes points to a buffer that gets filled. It must be large enough to hold the entire block.
// To figure out the size needed, call GetResSize().
VOID BuildRes( PSTRINGBLOCK pStrBlock, LPVOID pRes );
// Create a string block.
HSTRBLOCK WINAPI GetStringBlockA( LPCSTR strAppName, UINT nBlockID, WORD wLangID )
{
PSTRINGBLOCK pStrBlock = NULL;
HINSTANCE hInstLib = NULL;
hInstLib = LoadLibraryExA(
strAppName,
NULL,
DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE
);
if( NULL == hInstLib )
{
SetBlockError(STRBLOCKERR_APPLOADFAILED);
return NULL;
}
// Create the block of strings.
pStrBlock = CreateBlock(hInstLib, nBlockID, wLangID);
// Free the library.
FreeLibrary(hInstLib);
if( pStrBlock )
SetBlockError(STRBLOCKERR_OK);
return (HSTRBLOCK)pStrBlock;
}
// Create a string block.
HSTRBLOCK WINAPI GetStringBlockW( LPCWSTR strAppName, UINT nBlockID, WORD wLangID )
{
PSTRINGBLOCK pStrBlock = NULL;
HINSTANCE hInstLib = NULL;
hInstLib = LoadLibraryExW(
strAppName,
NULL,
DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE
);
if( NULL == hInstLib )
{
SetBlockError(STRBLOCKERR_APPLOADFAILED);
return NULL;
}
// Create the block of strings.
pStrBlock = CreateBlock(hInstLib, nBlockID, wLangID);
// Free the library.
FreeLibrary(hInstLib);
if( pStrBlock )
SetBlockError(STRBLOCKERR_OK);
return (HSTRBLOCK)pStrBlock;
}
BOOL WINAPI DeleteStringBlock( HSTRBLOCK hStrBlock )
{
PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock;
int i;
if( NULL == pStrBlock )
{
SetBlockError(STRBLOCKERR_INVALIDBLOCK);
return FALSE;
}
for( i = 0; i < NO_OF_STRINGS_PER_BLOCK; i++ )
{
if( pStrBlock->strArray[i] )
free(pStrBlock->strArray[i]);
}
free( pStrBlock);
SetBlockError(STRBLOCKERR_OK);
return TRUE;
}
int WINAPI GetStringLength( HSTRBLOCK hStrBlock, UINT nIndex )
{
int nLen;
PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock;
if( NULL == pStrBlock )
{
SetBlockError(STRBLOCKERR_INVALIDBLOCK);
return -1;
}
if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) )
{
SetBlockError(STRBLOCKERR_INVALIDINDEX);
return -1;
}
nLen = wcslen(pStrBlock->strArray[nIndex]);
SetBlockError(STRBLOCKERR_OK);
return nLen;
}
BOOL WINAPI GetStringA( HSTRBLOCK hStrBlock, UINT nIndex, LPSTR pszText )
{
PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock;
if( NULL == pStrBlock )
{
SetBlockError(STRBLOCKERR_INVALIDBLOCK);
return FALSE;
}
if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) )
{
SetBlockError(STRBLOCKERR_INVALIDINDEX);
return FALSE;
}
if( NULL == pszText )
{
SetBlockError(STRBLOCKERR_STRINVALID);
return FALSE;
}
if( !WideCharToMultiByte(CP_ACP, 0, pStrBlock->strArray[nIndex], -1, pszText,
wcslen(pStrBlock->strArray[nIndex]) + 1, NULL, NULL) )
{
SetBlockError(STRBLOCKERR_UNKNOWN);
return FALSE;
}
SetBlockError(STRBLOCKERR_OK);
return TRUE;
}
BOOL WINAPI GetStringW( HSTRBLOCK hStrBlock, UINT nIndex, LPWSTR pszText )
{
PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock;
if( NULL == pStrBlock )
{
SetBlockError(STRBLOCKERR_INVALIDBLOCK);
return FALSE;
}
if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) )
{
SetBlockError(STRBLOCKERR_INVALIDINDEX);
return FALSE;
}
if( NULL == pszText )
{
SetBlockError(STRBLOCKERR_STRINVALID);
return FALSE;
}
wcscpy(pszText, pStrBlock->strArray[nIndex]);
SetBlockError(STRBLOCKERR_OK);
return TRUE;
}
BOOL WINAPI SetStringA( HSTRBLOCK hStrBlock, UINT nIndex, LPCSTR pszText )
{
PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock;
int nLen;
if( NULL == pStrBlock )
{
SetBlockError(STRBLOCKERR_INVALIDBLOCK);
return FALSE;
}
if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) )
{
SetBlockError(STRBLOCKERR_INVALIDINDEX);
return FALSE;
}
// Delete the current string & reallocate a new one..
free(pStrBlock->strArray[nIndex]);
nLen = strlen(pszText) + 1;
pStrBlock->strArray[nIndex] = (LPWSTR)malloc( sizeof(WCHAR) * nLen);
if( NULL == pStrBlock->strArray[nIndex] )
{
SetBlockError(STRBLOCKERR_NOMEMORY);
return FALSE;
}
if( !MultiByteToWideChar(CP_ACP, 0, pszText, -1, pStrBlock->strArray[nIndex], nLen) )
{
SetBlockError(STRBLOCKERR_UNKNOWN);
return FALSE;
}
SetBlockError(STRBLOCKERR_OK);
return TRUE;
}
BOOL WINAPI SetStringW( HSTRBLOCK hStrBlock, UINT nIndex, LPCWSTR pszText )
{
PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock;
int nLen;
if( NULL == pStrBlock )
{
SetBlockError(STRBLOCKERR_INVALIDBLOCK);
return FALSE;
}
if( (nIndex < 0) || (nIndex >= NO_OF_STRINGS_PER_BLOCK) )
{
SetBlockError(STRBLOCKERR_INVALIDINDEX);
return FALSE;
}
// Delete the current string & reallocate a new one..
free(pStrBlock->strArray[nIndex]);
nLen = wcslen(pszText) + 1;
pStrBlock->strArray[nIndex] = (LPWSTR)malloc( sizeof(WCHAR) * nLen);
if( NULL == pStrBlock->strArray[nIndex] )
{
SetBlockError(STRBLOCKERR_NOMEMORY);
return FALSE;
}
wcscpy(pStrBlock->strArray[nIndex], pszText);
SetBlockError(STRBLOCKERR_OK);
return TRUE;
}
int WINAPI GetFirstStringID( HSTRBLOCK hStrBlock )
{
PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock;
if( NULL == pStrBlock )
{
SetBlockError(STRBLOCKERR_INVALIDBLOCK);
return -1;
}
SetBlockError(STRBLOCKERR_OK);
return (pStrBlock->nBlockID - 1) * NO_OF_STRINGS_PER_BLOCK;
}
int WINAPI GetBlockID( HSTRBLOCK hStrBlock )
{
PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock;
if( NULL == pStrBlock )
{
SetBlockError(STRBLOCKERR_INVALIDBLOCK);
return -1;
}
SetBlockError(STRBLOCKERR_OK);
return pStrBlock->nBlockID;
}
WORD WINAPI GetBlockLanguage( HSTRBLOCK hStrBlock )
{
PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock;
if( NULL == pStrBlock )
{
SetBlockError(STRBLOCKERR_INVALIDBLOCK);
return -1;
}
SetBlockError(STRBLOCKERR_OK);
return pStrBlock->wLangID;
}
BOOL WINAPI UpdateStringBlockA( LPCSTR strAppName, HSTRBLOCK hStrBlock, int nBlockID, WORD wLangID )
{
HANDLE hUpdate;
PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock;
if( NULL == pStrBlock )
{
SetBlockError(STRBLOCKERR_INVALIDBLOCK);
return -1;
}
hUpdate = BeginUpdateResourceA(strAppName, FALSE);
if( NULL == hUpdate )
{
DWORD dwError = GetLastError();
switch( dwError )
{
case ERROR_CALL_NOT_IMPLEMENTED:
SetBlockError(STRBLOCKERR_UPDATENOTIMPLEMENTED);
break;
default:
SetBlockError(STRBLOCKERR_UPDATEFAILED);
break;
}
return FALSE;
}
// Update the resource.
if( !UpdateBlock(hUpdate, pStrBlock, nBlockID, wLangID) )
{
EndUpdateResource(hUpdate, FALSE);
return FALSE;
}
if( !EndUpdateResource(hUpdate, FALSE) )
{
SetBlockError(STRBLOCKERR_UPDATEFAILED);
return FALSE;
}
SetBlockError(STRBLOCKERR_OK);
return TRUE;
}
BOOL WINAPI UpdateStringBlockW( LPCWSTR strAppName, HSTRBLOCK hStrBlock, int nBlockID, WORD wLangID )
{
HANDLE hUpdate;
PSTRINGBLOCK pStrBlock = (PSTRINGBLOCK)hStrBlock;
if( NULL == pStrBlock )
{
SetBlockError(STRBLOCKERR_INVALIDBLOCK);
return -1;
}
hUpdate = BeginUpdateResourceW(strAppName, FALSE);
if( NULL == hUpdate )
{
DWORD dwError = GetLastError();
switch( dwError )
{
case ERROR_CALL_NOT_IMPLEMENTED:
SetBlockError(STRBLOCKERR_UPDATENOTIMPLEMENTED);
break;
default:
SetBlockError(STRBLOCKERR_UPDATEFAILED);
break;
}
return FALSE;
}
// Update the resource.
if( !UpdateBlock(hUpdate, pStrBlock, nBlockID, wLangID) )
{
EndUpdateResource(hUpdate, FALSE);
return FALSE;
}
if( !EndUpdateResource(hUpdate, FALSE) )
{
SetBlockError(STRBLOCKERR_UPDATEFAILED);
return FALSE;
}
SetBlockError(STRBLOCKERR_OK);
return TRUE;
}
STRBLOCKERR WINAPI GetStringBlockError()
{
return g_strBlockErr;
}
// Create a string block & return the pointer to the block. Return NULL on failure.
PSTRINGBLOCK CreateBlock( HINSTANCE hInstLib, UINT nBlockID, WORD wLangID )
{
PSTRINGBLOCK pStrBlock;
HRSRC hFindRes;
HGLOBAL hLoadRes;
LPVOID pRes;
hFindRes = FindResourceEx(hInstLib, RT_STRING, MAKEINTRESOURCE(nBlockID), wLangID);
if( NULL == hFindRes )
{
SetBlockError(STRBLOCKERR_RESNOTFOUND);
return NULL;
}
hLoadRes = LoadResource(hInstLib, hFindRes);
if( NULL == hLoadRes )
{
SetBlockError(STRBLOCKERR_LOADRESFAILED);
return NULL;
}
pRes = LockResource(hLoadRes);
if( NULL == pRes )
{
SetBlockError(STRBLOCKERR_LOADRESFAILED);
return NULL;
}
// Create a new string block, fill the strings based on the resource contents.
pStrBlock = (PSTRINGBLOCK)malloc(sizeof(STRINGBLOCK));
if( NULL == pStrBlock )
{
SetBlockError(STRBLOCKERR_NOMEMORY);
return NULL;
}
pStrBlock->nBlockID = nBlockID;
pStrBlock->wLangID = wLangID;
if( !ParseRes(pRes, pStrBlock) )
{
free(pStrBlock);
return NULL;
}
return pStrBlock;
}
// Parse the raw string resource, pRes, and build up the string block, pStrBlock.
// The parsing illustrates the format of a string block in an executable.
BOOL ParseRes( LPVOID pRes, PSTRINGBLOCK pStrBlock )
{
int i, j;
int nLen;
WCHAR* pParse = (WCHAR *)pRes;
// There are NO_OF_STRINGS_PER_BLOCK(=16) strings per block.
for( i = 0; i < NO_OF_STRINGS_PER_BLOCK; i++ )
{
nLen = (int)*pParse++; // The length of the string.
pStrBlock->strArray[i] = (LPWSTR)malloc((nLen + 1) * sizeof(WCHAR));
if( NULL == pStrBlock->strArray[i] )
{
int k;
for( k = 0; k < i; k++ ) // Free up the memory allocated so far.
free(pStrBlock->strArray[k]);
SetBlockError(STRBLOCKERR_NOMEMORY);
return FALSE;
}
for( j = 0; j < nLen; j++ ) // Copy the string.
pStrBlock->strArray[i][j] = *pParse++;
pStrBlock->strArray[i][j] = 0;
}
SetBlockError(STRBLOCKERR_OK);
return TRUE;
}
DWORD GetResSize( PSTRINGBLOCK pStrBlock )
{
DWORD dwResSize = 0;
int i = 0;
for( i = 0; i < NO_OF_STRINGS_PER_BLOCK; i++ )
dwResSize += (wcslen(pStrBlock->strArray[i]) + 1);
return dwResSize * sizeof(WCHAR);
}
// Build a raw resource block, pRes, based on our string block, pStrBlock.
// The raw resource block may be used to update a string resource.
VOID BuildRes( PSTRINGBLOCK pStrBlock, LPVOID pRes )
{
int i, j;
int nLen;
WCHAR* pParse = (WCHAR *)pRes;
// There are NO_OF_STRINGS_PER_BLOCK (= 16) strings per block.
for( i = 0; i < NO_OF_STRINGS_PER_BLOCK; i++ )
{
*pParse++ = nLen = wcslen(pStrBlock->strArray[i]);
for( j = 0; j < nLen; j++ )
*pParse++ = pStrBlock->strArray[i][j];
}
}
BOOL UpdateBlock( HANDLE hUpdate, PSTRINGBLOCK pStrBlock, int nBlockID, WORD wLangID )
{
DWORD dwResSize;
LPVOID pRes;
DWORD dwRet = 0;
WORD wLanguageID = (0 == wLangID) ? pStrBlock->wLangID : wLangID;
// Get the resource length as required by a raw string resource block.
dwResSize = GetResSize(pStrBlock);
pRes = malloc(dwResSize);
if( NULL == pRes )
{
SetBlockError(STRBLOCKERR_NOMEMORY);
return FALSE;
}
BuildRes(pStrBlock, pRes);
if( !UpdateResource(
hUpdate,
RT_STRING,
MAKEINTRESOURCE(((-1 == nBlockID) ? pStrBlock->nBlockID : nBlockID)),
wLanguageID,
pRes,
dwResSize
) )
{
DWORD dwError = GetLastError();
switch( dwError )
{
case ERROR_CALL_NOT_IMPLEMENTED:
SetBlockError(STRBLOCKERR_UPDATENOTIMPLEMENTED);
break;
default:
SetBlockError(STRBLOCKERR_UPDATEFAILED);
break;
}
free(pRes);
return FALSE;
}
free(pRes);
SetBlockError(STRBLOCKERR_OK);
return TRUE;
}