mirror of https://github.com/lianthony/NT4.0
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.
581 lines
16 KiB
581 lines
16 KiB
/*
|
|
* resaux.c
|
|
*
|
|
* handle resources in PE modules
|
|
*
|
|
* History
|
|
* 9-23-91 AviN Created
|
|
*/
|
|
|
|
|
|
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
#pragma warning(disable: 4200) // Zero-sized array in struct
|
|
|
|
typedef struct rsrc_typeinfo TYPEINFO;
|
|
typedef struct rsrc_nameinfo NAMEINFO;
|
|
typedef TYPEINFO *PTYPEINFO;
|
|
typedef NAMEINFO *PNAMEINFO;
|
|
|
|
#define MAX(a,b) ( (a) > (b) ) ? (a) : (b)
|
|
|
|
#define NE_RESOURCE_NAME_IS_INT 0x8000
|
|
#define RT_NAMES_TABLE 15
|
|
#define RID_NAMES_TABLE 1
|
|
#define NESTRINGID(x) !((x) & NE_RESOURCE_NAME_IS_INT)
|
|
#define GETRESTABLE(h) (MAKEP(h, ((struct new_exe *) MAKEP(h, 0))->ne_rsrctab))
|
|
|
|
#define STRINGID(x) ((x) & IMAGE_RESOURCE_NAME_IS_STRING)
|
|
|
|
|
|
#define WRITE_WORD(x) { \
|
|
*((WORD *) pResWrite) = x; \
|
|
pResWrite = (LPVOID) (((BYTE *) pResWrite) + 2); \
|
|
}
|
|
|
|
#define WRITE_DWORD(x) { \
|
|
*((DWORD *) pResWrite) = x; \
|
|
pResWrite = (LPVOID) (((BYTE *) pResWrite) + 4);\
|
|
}
|
|
|
|
#define RESTABSIZE (OFFSETOF(pResWrite)-OFFSETOF(pNERes))
|
|
|
|
void UniToAnsi(LPTSTR, LPTSTR, UINT);
|
|
|
|
/* This is identicatl to IMAGE_RESOURCE_DIRECTORY_STRING */
|
|
typedef struct _cbstr {
|
|
WORD cb;
|
|
TCHAR b[1];
|
|
} CBSTR, FAR* LPCBSTR;
|
|
|
|
/*
|
|
* Language ID used on the machine
|
|
*/
|
|
// BUGBUG should be set when win32s initialized
|
|
|
|
ULONG ulLanguageID = 0;
|
|
|
|
#define isDIGIT(x) ((x>=0)&&(x<=9))
|
|
|
|
ULONG InitLanguageID()
|
|
{
|
|
static BOOL fFirst = TRUE;
|
|
TCHAR achS1[256], achS2[256], SysDir[256];
|
|
int cbS2, cNum=0, i=1, cDigit, cbSysDir;
|
|
|
|
if (!fFirst) {
|
|
return(ulLanguageID);
|
|
}
|
|
|
|
fFirst = FALSE;
|
|
|
|
if( !GetProfileString(TEXT("intl"),TEXT("sLanguage"),TEXT("enu"),&achS1[0],ARRAYSIZE(achS1)))
|
|
return (ulLanguageID);
|
|
|
|
if( !( cbSysDir = GetSystemDirectory ( SysDir, ARRAYSIZE(SysDir)) ) )
|
|
return (ulLanguageID);
|
|
|
|
lstrcpy ( (LPTSTR)(&SysDir[cbSysDir]), TEXT("\\setup.inf") );
|
|
|
|
cbS2 = GetPrivateProfileString(TEXT("language"),achS1,TEXT(" 1033"),&achS2[0],
|
|
ARRAYSIZE(achS2), SysDir );
|
|
if( !cbS2 )
|
|
return (ulLanguageID);
|
|
|
|
cDigit = achS2[--cbS2]-TEXT('0');
|
|
|
|
while( (cbS2>0) && isDIGIT (cDigit) )
|
|
{
|
|
cNum += cDigit * i;
|
|
i*=10;
|
|
cDigit = achS2[--cbS2]-TEXT('0');
|
|
}
|
|
|
|
ulLanguageID = (ULONG) cNum;
|
|
|
|
#ifdef RES_DEBUG
|
|
DebugMsg(DM_TRACE, TEXT("ulLanguageID: %lx \n\r"), ulLanguageID);
|
|
#endif
|
|
return (ulLanguageID);
|
|
}
|
|
|
|
BOOL NEAR
|
|
AddOffsetToTable(LPTSTR FAR* ppNamesTable, DWORD offset)
|
|
{
|
|
|
|
void FAR* pCurrent;
|
|
|
|
Assert(*ppNamesTable != NULL);
|
|
|
|
pCurrent = (void FAR*) (* ppNamesTable);
|
|
|
|
*((DWORD *) pCurrent) = offset;
|
|
pCurrent = (LPVOID) (((BYTE *)pCurrent) + 4);
|
|
|
|
*ppNamesTable = pCurrent;
|
|
|
|
return(1);
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
* Copy string to temp buffer and return new position in buffer
|
|
* this buffer is then copied at the and of the resource table
|
|
*/
|
|
|
|
LPTSTR AddName(LPTSTR pNERes, LPCBSTR pcbstr)
|
|
{
|
|
WORD cbStr;
|
|
typedef struct _nametbl {
|
|
BYTE cbStr;
|
|
TCHAR achStr[0];
|
|
} NAMETBL;
|
|
NAMETBL FAR* pCurrent;
|
|
|
|
Assert(pNERes != NULL);
|
|
|
|
pCurrent = (NAMETBL FAR*) (pNERes);
|
|
|
|
cbStr = pcbstr->cb;
|
|
|
|
if (cbStr > 255) {
|
|
#ifdef RES_DEBUG
|
|
DebugMsg(DM_WARNING, TEXT("Win32S: Name/Type string is too long. Truncated!\r\n"));
|
|
#endif
|
|
cbStr = 255;
|
|
}
|
|
|
|
pCurrent->cbStr = (BYTE) cbStr;
|
|
|
|
UniToAnsi(&pCurrent->achStr[0], pcbstr->b, cbStr);
|
|
|
|
pCurrent = (NAMETBL FAR*) ((LPBYTE) pCurrent + cbStr + 1);
|
|
|
|
return((LPTSTR)pCurrent);
|
|
}
|
|
|
|
LPCBSTR ResGetString(LPTSTR pPETable, IMAGE_RESOURCE_DIRECTORY_ENTRY FAR* pDir)
|
|
{
|
|
|
|
// BUGBUG! if table is bigger then 32k we're hosed
|
|
|
|
Assert((pDir->Name & 0x7fffffff) < 0x8000);
|
|
|
|
return (LPCBSTR) (pPETable + (WORD) (pDir->Name & 0x7fffffff));
|
|
}
|
|
|
|
/**************************************************************************\
|
|
* GetOrdinal
|
|
*
|
|
* checks for string id and add to names table if needed
|
|
*
|
|
* returns:
|
|
* TRUE - OK
|
|
* FALSE - failed
|
|
\**************************************************************************/
|
|
|
|
BOOL NEAR
|
|
GetOrdinal(LPTSTR pPETable, LPTSTR FAR* ppNamesTable,
|
|
IMAGE_RESOURCE_DIRECTORY_ENTRY FAR* pDir,
|
|
WORD FAR* pi)
|
|
{
|
|
|
|
if ( ! STRINGID(pDir->Name) ) {
|
|
|
|
// this can be handles by translating the long int to a string
|
|
// like "Lxxxxxxxx"
|
|
Assert(pDir->Name < 0x8000);
|
|
if (pDir->Name >= 0x8000)
|
|
return(FALSE);
|
|
|
|
*pi = (WORD) pDir->Name | NE_RESOURCE_NAME_IS_INT;
|
|
} else {
|
|
|
|
// This is the offset of the string from the end of the resource
|
|
// table. We start with 0 and then fix all the entries
|
|
// according to the actual resource table size
|
|
*pi = OFFSETOF(*ppNamesTable);
|
|
*ppNamesTable = AddName(*ppNamesTable, ResGetString(pPETable, pDir));
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
void FixOffsetsAndSize(LPTSTR pNERes, WORD cbSize, WORD iAlign, LPTSTR pPERes, LPTSTR pTable)
|
|
{
|
|
PTYPEINFO pType;
|
|
PNAMEINFO pName;
|
|
WORD cTypes;
|
|
DWORD dwSize, dwPEOffsetToData;
|
|
WORD wSize;
|
|
WORD iShift;
|
|
|
|
iShift = iAlign - 1;
|
|
|
|
for (pType = (PTYPEINFO) (pNERes+2); RT_ID(*pType); pType = (PTYPEINFO) pName) {
|
|
|
|
|
|
//Fix Offset to Strings
|
|
if (!(RT_ID(*pType) & RSORDID)) {
|
|
RT_ID(*pType) += cbSize;
|
|
}
|
|
|
|
|
|
// for all ids in type check if string id and fix offset
|
|
// When this loop is done pName point to the next type entry
|
|
for (pName = (PNAMEINFO)(pType+1), cTypes = RT_NRES(*pType); cTypes--; pName++) {
|
|
|
|
//Fix Offset to Strings
|
|
if (!(RN_ID(*pName) & RSORDID)) {
|
|
RN_ID(*pName) += cbSize;
|
|
}
|
|
|
|
//Fix resource size
|
|
dwPEOffsetToData = *(DWORD FAR*) (RN_OFFSET(*pName)+(BYTE FAR*)pTable);
|
|
dwSize = ((IMAGE_RESOURCE_DATA_ENTRY FAR*)((BYTE FAR*) pPERes + dwPEOffsetToData))->Size;
|
|
|
|
if(iShift >= 1)
|
|
wSize = (WORD)(dwSize >> 1);
|
|
else
|
|
wSize = (WORD) dwSize;
|
|
RN_LENGTH(*pName) = wSize;
|
|
|
|
//Fix resource offset
|
|
RN_OFFSET(*pName) += cbSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* CreateNEResTable
|
|
*
|
|
* Create NE format resource table for the given PE resource table
|
|
\****************************************************************************/
|
|
|
|
HANDLE CreateNEResTable(PVOID pPERes)
|
|
{
|
|
|
|
PVOID pResWrite=NULL;
|
|
PVOID pNERes;
|
|
WORD cIntTypes, cSzTypes, cTypes;
|
|
WORD cIntIds, cSzIds, cIds;
|
|
WORD cIntLangs, cLangs;
|
|
IMAGE_RESOURCE_DIRECTORY_ENTRY FAR* pTypeDir;
|
|
IMAGE_RESOURCE_DIRECTORY_ENTRY FAR* pIdDir;
|
|
IMAGE_RESOURCE_DIRECTORY_ENTRY FAR* pLangDir;
|
|
IMAGE_RESOURCE_DIRECTORY_ENTRY FAR* pUserLangDir=NULL;
|
|
IMAGE_RESOURCE_DIRECTORY_ENTRY FAR* pPrimeLangDir=NULL;
|
|
IMAGE_RESOURCE_DIRECTORY_ENTRY FAR* pZeroLangDir=NULL;
|
|
IMAGE_RESOURCE_DIRECTORY_ENTRY FAR* pUSEngLangDir=NULL;
|
|
WORD iType, iId;
|
|
BOOL fNamesResource=FALSE;
|
|
HANDLE hNERes = 0;
|
|
LPTSTR pNamesTable=NULL;
|
|
LPTSTR pNamesTableBase = NULL;
|
|
DWORD dwMaxRes = 0xFFFF; // BUGBUG!
|
|
WORD iAlign=0;
|
|
DWORD dwSize;
|
|
DWORD dwMaxSize = 0;
|
|
HANDLE hMem = 0;
|
|
|
|
#define SETLANGUAGE1(ptr) \
|
|
WRITE_WORD((WORD)((UINT)pNamesTable - (UINT)pNamesTableBase)); \
|
|
AddOffsetToTable(&pNamesTable,ptr->OffsetToData); \
|
|
dwSize = ((IMAGE_RESOURCE_DATA_ENTRY FAR*) (ptr->OffsetToData + (DWORD) pPERes))->Size; \
|
|
WRITE_WORD((WORD)dwSize); \
|
|
dwMaxSize = MAX(dwSize, dwMaxSize);
|
|
#if RES_DEBUG
|
|
#define SETLANGUAGE(ptr) \
|
|
{ \
|
|
/* DebugMsg(DM_TRACE, "Setting language 0x%lX\r\n", ptr->Name); */ \
|
|
SETLANGUAGE1(ptr) \
|
|
}
|
|
#else
|
|
#define SETLANGUAGE(ptr) { SETLANGUAGE1(ptr) }
|
|
#endif
|
|
|
|
#define LANG 0x03FF
|
|
#define SUBLANG 0xFC00
|
|
|
|
do {
|
|
dwMaxRes >>= 1;
|
|
iAlign ++;
|
|
} while ( dwMaxRes > 0xffff );
|
|
|
|
hNERes = GlobalAlloc(GMEM_ZEROINIT, 0xffff); // BUGBUG!
|
|
|
|
pResWrite = pNERes = (PVOID) GlobalLock(hNERes);
|
|
|
|
if (!pNERes) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
//Allocate the memory for the names of the named resource.
|
|
//Inefficient if all the resources are ID's.
|
|
|
|
// PERFORMANCE!
|
|
// There's a tradeoff between "lean", i.e. alloc just as much as we need
|
|
// and fast (alloc worst case)
|
|
// To find the size we need to go thru the PE resource table and
|
|
// check all the string type/id
|
|
|
|
hMem = GlobalAlloc(0, 0xffff);
|
|
if (!hMem) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
pNamesTable = pNamesTableBase = (LPTSTR) GlobalLock(hMem);
|
|
if(!pNamesTable) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
//PATCH! Dont write at offset 0, otherwise FixOffsetsAndSize
|
|
//might be confuse. So leave 4 bytes empty.
|
|
pNamesTable = (LPTSTR)((BYTE FAR*) pNamesTable + 4);
|
|
|
|
|
|
// point to root - directory and get the counts
|
|
cIntTypes = ((IMAGE_RESOURCE_DIRECTORY FAR*) pPERes)->NumberOfIdEntries;
|
|
cSzTypes = ((IMAGE_RESOURCE_DIRECTORY FAR*) pPERes)->NumberOfNamedEntries;
|
|
|
|
|
|
#ifdef RES_DEBUG
|
|
DebugMsg(DM_TRACE, TEXT("resources: %d integer types, %d named types\n\r"), cIntTypes, cSzTypes);
|
|
#endif
|
|
|
|
|
|
/*
|
|
* handle number id's first
|
|
*/
|
|
pTypeDir = (IMAGE_RESOURCE_DIRECTORY_ENTRY FAR*)
|
|
((IMAGE_RESOURCE_DIRECTORY FAR*) pPERes + 1);
|
|
|
|
|
|
// write the Dummy resource shift count
|
|
|
|
WRITE_WORD(iAlign);
|
|
|
|
// Win32s expects two level directory tree only
|
|
// corresponding to type/id pair in Windows 3.x
|
|
|
|
for (cTypes = cIntTypes + cSzTypes ; cTypes; cTypes--, pTypeDir++) {
|
|
|
|
Assert(pTypeDir->OffsetToData&IMAGE_RESOURCE_DATA_IS_DIRECTORY);
|
|
|
|
if (!GetOrdinal(pPERes, &pNamesTable, pTypeDir, &iType)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
#ifdef RES_DEBUG
|
|
if (pTypeDir->Name & IMAGE_RESOURCE_NAME_IS_STRING) {
|
|
DebugMsg(DM_TRACE, TEXT("Loading resource types (named) 0x%X \n\r"),
|
|
pTypeDir->Name&(~IMAGE_RESOURCE_NAME_IS_STRING));
|
|
} else {
|
|
DebugMsg(DM_TRACE, TEXT("Loading resource types (ID) 0x%X \n\r"),
|
|
pTypeDir->Name&(~IMAGE_RESOURCE_NAME_IS_STRING));
|
|
}
|
|
#endif
|
|
|
|
// point to sub-directory and get the counts
|
|
pIdDir = (IMAGE_RESOURCE_DIRECTORY_ENTRY FAR*)
|
|
((pTypeDir->OffsetToData&(~IMAGE_RESOURCE_DATA_IS_DIRECTORY)) +
|
|
(BYTE FAR*) pPERes);
|
|
|
|
cIntIds = ((IMAGE_RESOURCE_DIRECTORY FAR*) pIdDir)->NumberOfIdEntries;
|
|
cSzIds = ((IMAGE_RESOURCE_DIRECTORY FAR*) pIdDir)->NumberOfNamedEntries;
|
|
|
|
// write the bundle header
|
|
WRITE_WORD(iType);
|
|
WRITE_WORD(cIntIds + cSzIds);
|
|
WRITE_DWORD(0); // resource handler
|
|
|
|
|
|
pIdDir = (IMAGE_RESOURCE_DIRECTORY_ENTRY FAR*)
|
|
((IMAGE_RESOURCE_DIRECTORY FAR*) pIdDir + 1);
|
|
|
|
for (cIds = cIntIds + cSzIds ; cIds; cIds--, pIdDir++) {
|
|
|
|
// Assert(!(pIdDir->OffsetToData&IMAGE_RESOURCE_DATA_IS_DIRECTORY));
|
|
|
|
if (!GetOrdinal(pPERes, &pNamesTable, pIdDir, &iId)) {
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
if ( NESTRINGID(iType) || NESTRINGID(iId) ) {
|
|
|
|
if (!pNamesTable) {
|
|
goto ErrorReturn;
|
|
}
|
|
fNamesResource = TRUE;
|
|
|
|
}
|
|
|
|
#ifdef RES_DEBUG
|
|
if (pIdDir->Name & IMAGE_RESOURCE_NAME_IS_STRING) {
|
|
DebugMsg(DM_TRACE, TEXT("Loading resource (named) 0x%X "),
|
|
pIdDir->Name&(~IMAGE_RESOURCE_NAME_IS_STRING));
|
|
} else {
|
|
DebugMsg(DM_TRACE, TEXT("Loading resource (ID) 0x%X "),
|
|
pIdDir->Name&(~IMAGE_RESOURCE_NAME_IS_STRING));
|
|
}
|
|
#endif
|
|
|
|
// !!! write resource information
|
|
// write the 32bit offset of the resource_data_entry in the
|
|
//
|
|
// 16 bit offset+size
|
|
|
|
if (pIdDir->OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY)
|
|
{
|
|
// point to sub-directory and get the counts
|
|
pLangDir = (IMAGE_RESOURCE_DIRECTORY_ENTRY FAR*)
|
|
((pIdDir->OffsetToData&(~IMAGE_RESOURCE_DATA_IS_DIRECTORY)) +
|
|
(BYTE FAR*) pPERes);
|
|
cIntLangs = ((IMAGE_RESOURCE_DIRECTORY FAR*) pLangDir)->NumberOfIdEntries;
|
|
|
|
if (!cIntLangs) { // Directory is empty !
|
|
#ifdef RES_DEBUG
|
|
DebugMsg(DM_ERROR, TEXT("Directory is empty !!!\r\n"));
|
|
#endif
|
|
goto ErrorReturn;
|
|
}
|
|
|
|
// point to first directory entry
|
|
pLangDir = (IMAGE_RESOURCE_DIRECTORY_ENTRY FAR*)
|
|
((IMAGE_RESOURCE_DIRECTORY FAR*) pLangDir + 1);
|
|
|
|
pUserLangDir = NULL;
|
|
pPrimeLangDir = NULL;
|
|
pZeroLangDir = NULL;
|
|
pUSEngLangDir = NULL;
|
|
|
|
for (cLangs = cIntLangs; cLangs; cLangs--, pLangDir++)
|
|
{
|
|
Assert(!(pLangDir->OffsetToData&IMAGE_RESOURCE_DATA_IS_DIRECTORY));
|
|
|
|
#ifdef RES_DEBUG
|
|
// DebugMsg(DM_TRACE, "%4lX, ", pLangDir->Name);
|
|
#endif
|
|
|
|
switch (pLangDir->Name) {
|
|
case MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL):
|
|
pZeroLangDir=pLangDir;
|
|
break;
|
|
case MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US):
|
|
pUSEngLangDir = pLangDir;
|
|
break;
|
|
default:
|
|
if (pLangDir->Name == ulLanguageID) {
|
|
pUserLangDir = pLangDir;
|
|
} else {
|
|
if (PRIMARYLANGID(pLangDir->Name) == PRIMARYLANGID(ulLanguageID)) {
|
|
pPrimeLangDir = pLangDir;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if(pZeroLangDir) {
|
|
#ifdef RES_DEBUG
|
|
// DebugMsg(DM_TRACE, "Picked language 0.\r\n");
|
|
#endif
|
|
SETLANGUAGE(pZeroLangDir);
|
|
} else { // No neutral language resource
|
|
if (pUserLangDir) {
|
|
#ifdef RES_DEBUG
|
|
DebugMsg(DM_TRACE, TEXT("language matched.\n\r"));
|
|
#endif
|
|
SETLANGUAGE(pUserLangDir);
|
|
} else { // No user language resource available
|
|
if (pPrimeLangDir) {
|
|
|
|
#ifdef RES_DEBUG
|
|
// DebugMsg(DM_TRACE, "Picked primary language.\r\n");
|
|
#endif
|
|
SETLANGUAGE(pPrimeLangDir);
|
|
} else { // No matching primary language
|
|
if (pUSEngLangDir) {
|
|
#ifdef RES_DEBUG
|
|
//DebugMsg(DM_TRACE, "Picked English language.\r\n");
|
|
#endif
|
|
SETLANGUAGE(pUSEngLangDir);
|
|
} else { // No English language
|
|
#ifdef RES_DEBUG
|
|
// DebugMsg(DM_TRACE, "Picked last language.\r\n");
|
|
#endif
|
|
--pLangDir;
|
|
SETLANGUAGE(pLangDir);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else SETLANGUAGE(pIdDir)
|
|
|
|
#ifdef RES_DEBUG
|
|
DebugMsg(DM_TRACE, TEXT(" - Offset: 0x%lX - Size: %ld"), pIdDir->OffsetToData & 0x7fffffffL, dwSize);
|
|
#endif
|
|
|
|
WRITE_WORD(0); // BUGBUG! flags?
|
|
WRITE_WORD(iId);
|
|
WRITE_WORD(0); // handle
|
|
WRITE_WORD(0); // usage
|
|
}
|
|
}
|
|
|
|
WRITE_WORD(0); // table terminator
|
|
|
|
Assert(pNamesTable != NULL);
|
|
|
|
// Check the alignment
|
|
iAlign = 1;
|
|
while(dwMaxSize > 0xffff)
|
|
{
|
|
iAlign++;
|
|
dwMaxSize >>= 1;
|
|
}
|
|
|
|
// Write now the right Alignement
|
|
*(WORD FAR*) pNERes = iAlign;
|
|
|
|
// Fix all named resources with actual offset of strings
|
|
FixOffsetsAndSize(pNERes, (WORD)RESTABSIZE, iAlign, pPERes, pNamesTableBase);
|
|
|
|
|
|
// Copy table
|
|
dwSize = (DWORD)pNamesTable - (DWORD)pNamesTableBase;
|
|
hmemcpy(pResWrite, pNamesTableBase, (int)dwSize);
|
|
dwSize += ((DWORD)pResWrite - (DWORD)pNERes);
|
|
|
|
GlobalFree(hMem);
|
|
|
|
GlobalReAlloc(hNERes, dwSize, 0);
|
|
|
|
return(hNERes);
|
|
|
|
ErrorReturn:
|
|
if (hNERes) {
|
|
GlobalFree(hNERes);
|
|
}
|
|
if (hMem) {
|
|
GlobalFree(hMem);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
void UniToAnsi(LPTSTR pDst, LPTSTR pSrc, UINT cbCount)
|
|
{
|
|
for (; cbCount; cbCount--) {
|
|
*pDst++ = *pSrc++;
|
|
#ifdef RES_DEBUG
|
|
if (*pSrc) {
|
|
DebugMsg(DM_TRACE, TEXT("Win32S: hi byte not nul in UniToAnsi\r\n"));
|
|
}
|
|
#endif
|
|
++pSrc;
|
|
}
|
|
}
|