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.
1032 lines
20 KiB
1032 lines
20 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
hcttools.c
|
|
|
|
Abstract:
|
|
|
|
This module contains helpful functions and wrappers for debugging and
|
|
tools commonly used throughout hct programs.
|
|
|
|
Environment:
|
|
|
|
User mode
|
|
|
|
Revision History:
|
|
|
|
05-Sep-1997 : Jason Allor (jasonall)
|
|
|
|
--*/
|
|
#include "hcttools.h"
|
|
|
|
#ifdef DEBUG
|
|
|
|
static PBLOCKINFO g_pbiHead;
|
|
|
|
static USHORT g_usMalloc;
|
|
static USHORT g_usFree;
|
|
|
|
#endif
|
|
|
|
/*++
|
|
|
|
Routine Description: InitializeMemoryManager
|
|
|
|
Initializes globals used by this module
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
VOID InitializeMemoryManager()
|
|
{
|
|
#ifdef DEBUG
|
|
|
|
g_pbiHead = NULL;
|
|
g_usMalloc = 0;
|
|
g_usFree = 0;
|
|
|
|
#else
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
} // InitializeMemoryManager //
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
/*++
|
|
|
|
Routine Description: GetBlockInfo
|
|
|
|
Searches the memory log to find the block that pb points into and
|
|
returns a pointer to the corresponding blockinfo structure of the
|
|
memory log. Note: pb must point into an allocated block or you
|
|
will get an assertion failure. The function either asserts or
|
|
succeeds -- it never returns an error.
|
|
|
|
Arguments:
|
|
|
|
pb: block to get info about
|
|
|
|
Return Value:
|
|
|
|
BLOCKINFO: returns the information
|
|
|
|
--*/
|
|
static PBLOCKINFO GetBlockInfo(IN PBYTE pb)
|
|
{
|
|
PBLOCKINFO pbi;
|
|
|
|
for (pbi = g_pbiHead; pbi != NULL; pbi = pbi->pbiNext)
|
|
{
|
|
PBYTE pbStart = pbi->pb;
|
|
PBYTE pbEnd = pbi->pb + pbi->size - 1;
|
|
|
|
if (PtrGrtrEq(pb, pbStart) && PtrLessEq(pb, pbEnd))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Couldn't find pointer? It is garbage, pointing to a block that was
|
|
// freed, or pointing to a block that moved when it was resized?
|
|
//
|
|
__ASSERT(pbi != NULL);
|
|
|
|
return (pbi);
|
|
|
|
} // GetBlockInfo //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: CreateBlockInfo
|
|
|
|
Creates a log entry for the memory block defined by pbNew:sizeNew.
|
|
|
|
Arguments:
|
|
|
|
pbNew: new block
|
|
sizeNew: the size of the new block
|
|
cszFile: the file name the code is located in \ these tell what code
|
|
iLine: the line number of the assertion / called the malloc
|
|
|
|
Return Value:
|
|
|
|
BOOL: TRUE if it successfully creates the log information,
|
|
FALSE otherwise
|
|
|
|
--*/
|
|
BOOL CreateBlockInfo(OUT PBYTE pbNew,
|
|
IN size_t sizeNew,
|
|
IN PCHAR cszFile,
|
|
IN UINT iLine)
|
|
{
|
|
PBLOCKINFO pbi;
|
|
|
|
__ASSERT(pbNew != NULL && sizeNew != 0);
|
|
|
|
pbi = (PBLOCKINFO)malloc(sizeof(BLOCKINFO));
|
|
if (pbi != NULL)
|
|
{
|
|
pbi->pb = pbNew;
|
|
pbi->size = sizeNew;
|
|
pbi->pbiNext = g_pbiHead;
|
|
pbi->iLine = iLine;
|
|
strcpy(pbi->cszFile, cszFile);
|
|
g_pbiHead = pbi;
|
|
}
|
|
|
|
return (pbi != NULL);
|
|
|
|
} // CreateBlockInfo //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: FreeBlockInfo
|
|
|
|
Destroys the log entry for the memory block that pbToFree
|
|
points to. pbToFree must point to the start of an allocated
|
|
block or you will get an assertion failure
|
|
|
|
Arguments:
|
|
|
|
pbToFree: the block to free
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
void FreeBlockInfo(IN PBYTE pbToFree)
|
|
{
|
|
PBLOCKINFO pbi, pbiPrev;
|
|
|
|
pbiPrev = NULL;
|
|
for (pbi = g_pbiHead; pbi != NULL; pbi = pbi->pbiNext)
|
|
{
|
|
if (PtrEqual(pbi->pb, pbToFree))
|
|
{
|
|
if (pbiPrev == NULL)
|
|
{
|
|
g_pbiHead = pbi->pbiNext;
|
|
}
|
|
else
|
|
{
|
|
pbiPrev->pbiNext = pbi->pbiNext;
|
|
}
|
|
break;
|
|
}
|
|
pbiPrev = pbi;
|
|
}
|
|
|
|
//
|
|
// If pbi is NULL, the pbToFree is invalid
|
|
//
|
|
__ASSERT(pbi != NULL);
|
|
|
|
//
|
|
// Destroy the contents of *pbi before freeing them
|
|
//
|
|
memset(pbi, GARBAGE, sizeof(BLOCKINFO));
|
|
|
|
free(pbi);
|
|
|
|
} // FreeBlockInfo //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: UpdateBlockInfo
|
|
|
|
UpdateBlockInfo looks up the log information for the memory
|
|
block that pbOld points to. The function then updates the log
|
|
information to reflect the fact that the block new lives at pbNew
|
|
and is "sizeNew" bytes long. pbOld must point to the start of an
|
|
allocated block or you will get an assertion failure
|
|
|
|
Arguments:
|
|
|
|
pbOld: old location
|
|
pbNew: new location
|
|
sizeNew: the new size
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
void UpdateBlockInfo(IN PBYTE pbOld,
|
|
IN PBYTE pbNew,
|
|
IN size_t sizeNew)
|
|
{
|
|
PBLOCKINFO pbi;
|
|
|
|
__ASSERT(pbNew != NULL && sizeNew);
|
|
|
|
pbi = GetBlockInfo(pbOld);
|
|
__ASSERT(pbOld == pbi->pb);
|
|
|
|
pbi->pb = pbNew;
|
|
pbi->size = sizeNew;
|
|
|
|
} // UpdateBlockInfo //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: SizeOfBlock
|
|
|
|
Returns the size of the block that pb points to. pb must point to
|
|
the start of an allocated block or you will get an assertion failure
|
|
|
|
Arguments:
|
|
|
|
pb: the block to find the size of
|
|
|
|
Return Value:
|
|
|
|
size_t: returns the size
|
|
|
|
--*/
|
|
size_t SizeOfBlock(IN PBYTE pb)
|
|
{
|
|
PBLOCKINFO pbi;
|
|
|
|
pbi = GetBlockInfo(pb);
|
|
__ASSERT(pb == pbi->pb);
|
|
|
|
return (pbi->size);
|
|
return 1;
|
|
|
|
} // SizeOfBlock //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: ClearMemoryRefs
|
|
|
|
ClearMemoryRefs marks all blocks in the memory log as
|
|
being unreferenced
|
|
|
|
Arguments:
|
|
|
|
void
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
void ClearMemoryRefs()
|
|
{
|
|
PBLOCKINFO pbi;
|
|
|
|
for (pbi = g_pbiHead; pbi != NULL; pbi = pbi->pbiNext)
|
|
{
|
|
pbi->boolReferenced = FALSE;
|
|
}
|
|
|
|
} // ClearMemoryRefs //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: NoteMemoryRef
|
|
|
|
Marks the block that pv points into as being referenced.
|
|
Note: pv does not have to point to the start of a block; it may
|
|
point anywhere withing an allocated block
|
|
|
|
Arguments:
|
|
|
|
pv: the block to mark
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
void NoteMemoryRef(IN PVOID pv)
|
|
{
|
|
PBLOCKINFO pbi;
|
|
|
|
pbi = GetBlockInfo((PBYTE)pv);
|
|
pbi->boolReferenced = TRUE;
|
|
|
|
} // NoteMemoryRef //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: CheckMemoryRefs
|
|
|
|
Scans the memory log looking for blocks that have not been
|
|
marked with a call to NoteMemoryRef. It this function finds an
|
|
unmarked block, it asserts.
|
|
|
|
Arguments:
|
|
|
|
void
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
void CheckMemoryRefs()
|
|
{
|
|
PBLOCKINFO pbi;
|
|
|
|
for (pbi = g_pbiHead; pbi != NULL; pbi = pbi->pbiNext)
|
|
{
|
|
//
|
|
// A simple check for block integrity. If this assert fires, it
|
|
// means that something is wrong with the debug code that manages
|
|
// blockinfo or, possibly, that a wild memory store has thrashed
|
|
// the data structure. Either way, there's a bug
|
|
//
|
|
__ASSERT(pbi->pb != NULL && pbi->size);
|
|
|
|
//
|
|
// A check for lost or leaky memory. If this assert fires, it means
|
|
// that the app has either lost track of this block or that not all
|
|
// global pointers have been accounted for with NoteMemoryRef.
|
|
//
|
|
__ASSERT(pbi->boolReferenced);
|
|
}
|
|
|
|
} // CheckMemoryRefs //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: ValidPointer
|
|
|
|
Verifies that pv points into an allocated memory block and that there
|
|
are at least "size" allocated bytes from pv to the end of a block. If
|
|
either condition is not met, ValidPointer will assert.
|
|
|
|
Arguments:
|
|
|
|
pv: the block to check out
|
|
size: the size to match against
|
|
|
|
Return Value:
|
|
|
|
BOOL: Always returns TRUE.
|
|
|
|
The reason this always returns TRUE is to allow you to call the
|
|
function within an __ASSERT macro. While this isn't the most efficient
|
|
method to use, using the macro neatly handles the debug-vs-ship
|
|
version control issue without your having to resort to #ifdef
|
|
DEBUGS or to introducing other __ASSERT-like macros.
|
|
|
|
--*/
|
|
BOOL ValidPointer(IN PVOID pv,
|
|
IN size_t size)
|
|
{
|
|
PBLOCKINFO pbi;
|
|
PBYTE pb = (PBYTE)pv;
|
|
|
|
__ASSERT(pv != NULL && size);
|
|
|
|
pbi = GetBlockInfo(pb);
|
|
|
|
//
|
|
// size isn't valid if pb+size overflows the block
|
|
//
|
|
__ASSERT(PtrLessEq(pb + size, pbi->pb + pbi->size));
|
|
|
|
return TRUE;
|
|
|
|
} // ValidPointer //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: _Assert
|
|
|
|
My assert function. Simply outputs the file name and line number
|
|
to a MessageBox and then kills the program. Note: this should only
|
|
be called by the __ASSERT macro above
|
|
|
|
Arguments:
|
|
|
|
cszFile: the file name the code is located in
|
|
iLine: the line number of the assertion
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
void MyAssert(IN PCHAR cszFile,
|
|
IN UINT iLine)
|
|
{
|
|
WCHAR wszFile[100];
|
|
TCHAR tszMessage[100];
|
|
|
|
fflush(NULL);
|
|
|
|
_stprintf(tszMessage, TEXT("Assertion failed: %s, line %u"),
|
|
ConvertAnsi(cszFile, wszFile, 100), iLine);
|
|
MessageBox(NULL, tszMessage, NULL, MB_OK);
|
|
|
|
abort();
|
|
|
|
} // MyAssert //
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: __MALLOC
|
|
|
|
Don't call this function. Instead, call the macro MyMalloc,
|
|
which calls this function.
|
|
|
|
Wrapper for malloc function. Provides better calling convention
|
|
and debug syntax. Calling example:
|
|
|
|
malloc: pbBlock = (byte *)malloc(sizeof(pbBlock));
|
|
MyMalloc: MyMalloc(&pbBlock, sizeof(pbBlock));
|
|
|
|
Arguments:
|
|
|
|
ppv: variable to be malloced
|
|
size: the memory size to use
|
|
cszFile: the file name the code is located in \ these tell what code
|
|
iLine: the line number of the assertion / called the malloc
|
|
|
|
Return Value:
|
|
|
|
BOOL: TRUE if malloc succeeds, FALSE if it does not
|
|
|
|
--*/
|
|
BOOL __MALLOC(IN OUT void **ppv,
|
|
IN size_t size,
|
|
IN PCHAR cszFile,
|
|
IN UINT iLine)
|
|
{
|
|
BYTE **ppb = (BYTE **)ppv;
|
|
|
|
__ASSERT(ppv != NULL && size != 0);
|
|
|
|
*ppb = (BYTE *)malloc(size);
|
|
|
|
#ifdef DEBUG
|
|
g_usMalloc++;
|
|
|
|
if (*ppb != NULL)
|
|
{
|
|
//
|
|
// Shred the memory
|
|
//
|
|
memset(*ppb, GARBAGE, size);
|
|
|
|
//
|
|
// Record information about this block in memory
|
|
//
|
|
if (!CreateBlockInfo(*ppb, size, cszFile, iLine))
|
|
{
|
|
free(*ppb);
|
|
*ppb = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return (*ppb != NULL);
|
|
|
|
} // __MALLOC //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: __FREE
|
|
|
|
Wrapper for free function. Provides debug syntax.
|
|
Sets the pointer to NULL after it is done freeing it.
|
|
This should always be called in a format such as:
|
|
|
|
Old syntax: free(pVariable);
|
|
New syntax: MyFree(&pVariable);
|
|
|
|
Arguments:
|
|
|
|
ppv: variable to be freed
|
|
|
|
Return Value:
|
|
|
|
void
|
|
|
|
--*/
|
|
void __FREE(IN void **ppv)
|
|
{
|
|
//
|
|
// *ppv should never be NULL. This is technically legal
|
|
// but it's not good behavior
|
|
//
|
|
__ASSERT (*ppv != NULL);
|
|
|
|
#ifdef DEBUG
|
|
g_usFree++;
|
|
|
|
//
|
|
// Shred the memory
|
|
//
|
|
memset(*ppv, GARBAGE, SizeOfBlock(*ppv));
|
|
FreeBlockInfo(*ppv);
|
|
#endif
|
|
|
|
free(*ppv);
|
|
*ppv = NULL;
|
|
|
|
} // __FREE //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: CheckAllocs
|
|
|
|
Checks to make sure that everything has been freed. This
|
|
should be called right before the program ends.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
VOID: If this function finds any allocated memory that
|
|
has not been freed, it will __ASSERT.
|
|
|
|
--*/
|
|
VOID CheckAllocs()
|
|
{
|
|
#ifndef DEBUG
|
|
|
|
return;
|
|
|
|
#else
|
|
|
|
PBLOCKINFO pbi;
|
|
TCHAR tszInvalidMemoryLocations[10000];
|
|
USHORT usCounter = 0;
|
|
BOOL bBadMemFound = FALSE;
|
|
WCHAR wszUnicode[100];
|
|
|
|
_stprintf(tszInvalidMemoryLocations,
|
|
TEXT("Unfreed Malloc Locations: \n\n"));
|
|
|
|
_stprintf(tszInvalidMemoryLocations,
|
|
TEXT("%sMallocs = %d Frees = %d\n\n"),
|
|
tszInvalidMemoryLocations, g_usMalloc, g_usFree);
|
|
|
|
for (pbi = g_pbiHead; pbi != NULL; pbi = pbi->pbiNext)
|
|
{
|
|
bBadMemFound = TRUE;
|
|
|
|
//
|
|
// Only print out the first 20 unfreed blocks we
|
|
// find, so the messagebox won't be too huge
|
|
//
|
|
if (usCounter++ < 20)
|
|
{
|
|
_stprintf(tszInvalidMemoryLocations,
|
|
TEXT("%sFile = %s \t Line = %d\n"),
|
|
tszInvalidMemoryLocations,
|
|
ConvertAnsi(pbi->cszFile, wszUnicode, 100),
|
|
pbi->iLine);
|
|
}
|
|
}
|
|
|
|
if (bBadMemFound)
|
|
{
|
|
MessageBox(NULL, tszInvalidMemoryLocations, NULL,
|
|
MB_OK | MB_ICONERROR);
|
|
|
|
__ASSERT(TRUE);
|
|
}
|
|
|
|
#endif
|
|
|
|
} // CheckAllocs //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: StrNCmp
|
|
|
|
Compares two TCHAR strings. Both strings length must be >= ulLength
|
|
|
|
Arguments:
|
|
|
|
tszString1: the first string
|
|
tszString2: the second string
|
|
ulLength: the length to compare
|
|
|
|
Return Value:
|
|
|
|
BOOL: TRUE if strings are equal, FALSE if not
|
|
|
|
--*/
|
|
BOOL StrNCmp(IN PTCHAR tszString1,
|
|
IN PTCHAR tszString2,
|
|
IN ULONG ulLength)
|
|
{
|
|
ULONG ulIndex;
|
|
|
|
//
|
|
// Both strings must be valid and ulLength must be > 0
|
|
//
|
|
__ASSERT(tszString1 != NULL);
|
|
__ASSERT(tszString2 != NULL);
|
|
__ASSERT(ulLength > 0);
|
|
|
|
if (_tcslen(tszString1) < ulLength ||
|
|
_tcslen(tszString2) < ulLength)
|
|
{
|
|
goto RetFALSE;
|
|
}
|
|
|
|
for (ulIndex = 0; ulIndex < ulLength; ulIndex++)
|
|
{
|
|
if (tszString1[ulIndex] != tszString2[ulIndex])
|
|
{
|
|
goto RetFALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
RetFALSE:
|
|
return FALSE;
|
|
|
|
} // StrNCmp //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: StrCmp
|
|
|
|
Compares two TCHAR strings
|
|
|
|
Arguments:
|
|
|
|
tszString1: the first string
|
|
tszString2: the second string
|
|
|
|
Return Value:
|
|
|
|
BOOL: TRUE if strings are equal, FALSE if not
|
|
|
|
--*/
|
|
BOOL StrCmp(IN PTCHAR tszString1,
|
|
IN PTCHAR tszString2)
|
|
{
|
|
ULONG ulIndex;
|
|
ULONG ulLength;
|
|
|
|
//
|
|
// Both strings must be valid
|
|
//
|
|
__ASSERT(tszString1 != NULL);
|
|
__ASSERT(tszString2 != NULL);
|
|
|
|
ulLength = _tcslen(tszString1);
|
|
|
|
if (ulLength != _tcslen(tszString2))
|
|
{
|
|
goto RetFALSE;
|
|
}
|
|
|
|
for (ulIndex = 0; ulIndex < ulLength; ulIndex++)
|
|
{
|
|
if (tszString1[ulIndex] != tszString2[ulIndex])
|
|
{
|
|
goto RetFALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
RetFALSE:
|
|
return FALSE;
|
|
|
|
} // StrCmp //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: AnsiToUnicode
|
|
|
|
Converts an Ansi string to a Unicode string.
|
|
|
|
Arguments:
|
|
|
|
cszAnsi: the Ansi string to convert
|
|
wszUnicode: unicode buffer to store new string. Must not be NULL
|
|
ulSize: must be set to the size of the wszUnicode buffer
|
|
|
|
Return Value:
|
|
|
|
PWCHAR: returns the wszUnicode buffer
|
|
|
|
--*/
|
|
PWCHAR AnsiToUnicode(IN PCHAR cszAnsi,
|
|
OUT PWCHAR wszUnicode,
|
|
IN ULONG ulSize)
|
|
{
|
|
USHORT i;
|
|
USHORT usAnsiLength;
|
|
CHAR cszTemp[2000];
|
|
CHAR cszTemp2[2000];
|
|
|
|
//
|
|
// Clear out the new Unicode string
|
|
//
|
|
ZeroMemory(wszUnicode, ulSize);
|
|
|
|
//
|
|
// Record the length of the original Ansi string
|
|
//
|
|
usAnsiLength = strlen(cszAnsi);
|
|
|
|
//
|
|
// Copy the unicode string to a temporary buffer
|
|
//
|
|
strcpy(cszTemp, cszAnsi);
|
|
|
|
for (i = 0; i < usAnsiLength && i < ulSize - 1; i++)
|
|
{
|
|
//
|
|
// Copy the current character
|
|
//
|
|
wszUnicode[i] = (WCHAR)cszTemp[i];
|
|
}
|
|
|
|
wszUnicode[i] = '\0';
|
|
return wszUnicode;
|
|
|
|
} // AnsiToUnicode //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: UnicodeToAnsi
|
|
|
|
Converts a Unicode string to an Ansi string.
|
|
|
|
Arguments:
|
|
|
|
wszUnicode: the Unicode string to convert
|
|
cszAnsi: Ansi buffer to store new string. Must not be NULL
|
|
ulSize: must be set to the size of the cszAnsi buffer
|
|
|
|
Return Value:
|
|
|
|
PCHAR: returns the cszAnsi buffer
|
|
|
|
--*/
|
|
PCHAR UnicodeToAnsi(IN PWCHAR wszUnicode,
|
|
OUT PCHAR cszAnsi,
|
|
IN ULONG ulSize)
|
|
{
|
|
USHORT i;
|
|
USHORT usUnicodeLength;
|
|
WCHAR wszTemp[2000];
|
|
|
|
//
|
|
// Record the length of the original Unicode string
|
|
//
|
|
usUnicodeLength = wcslen(wszUnicode);
|
|
|
|
//
|
|
// Copy the unicode string to a temporary buffer
|
|
//
|
|
wcscpy(wszTemp, wszUnicode);
|
|
|
|
for (i = 0; i < usUnicodeLength && i < ulSize - 1; i++)
|
|
{
|
|
//
|
|
// Copy the current character
|
|
//
|
|
cszAnsi[i] = (CHAR)wszTemp[0];
|
|
|
|
//
|
|
// Shift the unicode string up by one position
|
|
//
|
|
wcscpy(wszTemp, wszTemp + 1);
|
|
}
|
|
|
|
//
|
|
// Add a null terminator to the end of the new ansi string
|
|
//
|
|
cszAnsi[i] = '\0';
|
|
|
|
return cszAnsi;
|
|
|
|
} // UnicodeToAnsi //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: ConvertAnsi
|
|
|
|
Receives an Ansi string. Returns the equivalent string in Ansi or
|
|
Unicode, depending on the current environment
|
|
|
|
Arguments:
|
|
|
|
cszAnsi: the Ansi string to convert
|
|
wszUnicode: unicode buffer to store new string (if needed).
|
|
Must not be NULL
|
|
ulSize: must be set to the size of the wszUnicode buffer
|
|
|
|
Return Value:
|
|
|
|
TCHAR: returns the ANSI or Unicode string
|
|
|
|
--*/
|
|
PTCHAR ConvertAnsi(IN OUT PCHAR cszAnsi,
|
|
IN OUT PWCHAR wszUnicode,
|
|
IN ULONG ulSize)
|
|
{
|
|
//
|
|
// If Unicode, we need to convert the string
|
|
//
|
|
#ifdef UNICODE
|
|
|
|
return AnsiToUnicode(cszAnsi, wszUnicode, ulSize);
|
|
|
|
//
|
|
// If not Unicode, just return the original Ansi string
|
|
//
|
|
#else
|
|
|
|
return cszAnsi;
|
|
|
|
#endif
|
|
|
|
} // ConvertAnsi //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: ConvertUnicode
|
|
|
|
Receives a Unicode string. Returns the equivalent string in Ansi or
|
|
Unicode, depending on the current environment
|
|
|
|
Arguments:
|
|
|
|
wszUnicode: the Unicode string to convert
|
|
cszAnsi: ANSI buffer to store new string (if needed).
|
|
Must not be NULL
|
|
ulSize: must be set to the size of the cszAnsi buffer
|
|
|
|
Return Value:
|
|
|
|
TCHAR: returns the ANSI or Unicode string
|
|
|
|
--*/
|
|
PTCHAR ConvertUnicode(IN OUT PWCHAR wszUnicode,
|
|
IN OUT PCHAR cszAnsi,
|
|
IN ULONG ulSize)
|
|
{
|
|
//
|
|
// If Unicode, just return the original Unicode string
|
|
//
|
|
#ifdef UNICODE
|
|
|
|
return wszUnicode;
|
|
|
|
//
|
|
// If not Unicode, need to convert to Ansi
|
|
//
|
|
#else
|
|
|
|
return UnicodeToAnsi(wszUnicode, cszAnsi, ulSize);
|
|
|
|
#endif
|
|
|
|
} // ConvertUnicode //
|
|
|
|
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description: ErrorMsg
|
|
|
|
Converts a numerical winerror into it's string value
|
|
|
|
Arguments:
|
|
|
|
ulError: the error number
|
|
tszBuffer: this buffer is used to return the string interpretation
|
|
must be declared of size MAX_ERROR_LEN
|
|
|
|
Return Value:
|
|
|
|
PTCHAR: returns the string value of the message stored in tszBuffer
|
|
|
|
--*/
|
|
PTCHAR ErrorMsg(IN ULONG ulError,
|
|
IN OUT PTCHAR tszBuffer)
|
|
{
|
|
USHORT i, usLen;
|
|
ULONG ulSuccess;
|
|
|
|
__ASSERT(tszBuffer != NULL);
|
|
|
|
ZeroMemory(tszBuffer, MAX_ERROR_LEN);
|
|
|
|
ulSuccess = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
ulError,
|
|
0,
|
|
tszBuffer,
|
|
MAX_ERROR_LEN,
|
|
NULL);
|
|
|
|
if (!ulSuccess)
|
|
{
|
|
//
|
|
// Couldn't get a description of this error. Just return the number
|
|
//
|
|
_itot(ulError, tszBuffer, MAX_ERROR_LEN);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Strip out returns from tszBuffer string
|
|
//
|
|
for (usLen = _tcslen(tszBuffer), i = 0; i < usLen; i++)
|
|
{
|
|
if (tszBuffer[i] == RETURN_CHAR1 || tszBuffer[i] == RETURN_CHAR2)
|
|
{
|
|
tszBuffer[i] = SPACE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return tszBuffer;
|
|
|
|
} // ErrorMsg //
|
|
|
|
|
|
|
|
|