windows-nt/Source/XPSP1/NT/base/fs/hsm/wsb/wsbtrak.cpp

724 lines
21 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
<EFBFBD> 1998 Seagate Software, Inc. All rights reserved
Module Name:
wsbtrak.cpp
Abstract:
Utility functions to keep track of run-time information.
Author:
Ron White [ronw] 5-Dec-1997
Revision History:
--*/
#include "stdafx.h"
#include "wsbguid.h"
#if defined(WSB_TRACK_MEMORY)
#define OBJECT_TABLE_SIZE 100
#define POINTER_DATA_CURRENT 0
#define POINTER_DATA_CUMULATIVE 2
#define POINTER_DATA_MAX 1
#define POINTER_DATA_SIZE 3
#define POINTER_LIST_SIZE 1000
typedef struct {
GUID guid;
LONG count;
LONG total_count;
LONG max_count;
} OBJECT_TABLE_ENTRY;
typedef struct {
OLECHAR * guid_string;
OLECHAR * name;
GUID * pGuid;
} OBJECT_NAME_ENTRY;
typedef struct {
LONG count;
LONGLONG size;
} POINTER_DATA_ENTRY;
typedef struct {
const void * addr;
LONG order;
ULONG size;
int index; // Index into object table
const char * filename;
int linenum;
} POINTER_LIST_ENTRY;
// Module data
#if defined(CRT_DEBUG_MEMORY)
static _CrtMemState CrtMemState;
#endif
static OBJECT_TABLE_ENTRY object_table[OBJECT_TABLE_SIZE];
static int object_table_count = 0;
static POINTER_DATA_ENTRY pointer_data[POINTER_DATA_SIZE];
static BOOL pointer_data_initialized = FALSE;
static POINTER_LIST_ENTRY pointer_list[POINTER_LIST_SIZE];
static int pointer_list_count = 0;
static OBJECT_NAME_ENTRY object_name_table[] = {
{ L"{C03D4861-70D7-11d1-994F-0060976A546D}", L"CWsbStringPtr", NULL },
{ L"{C03D4862-70D7-11d1-994F-0060976A546D}", L"CWsbBstrPtr", NULL },
{ L"{A0FF1F42-237A-11D0-81BA-00A0C91180F2}", L"CWsbGuid", NULL },
{ L"{9C7D6F13-1562-11D0-81AC-00A0C91180F2}", L"CWsbOrderedCollection", NULL },
{ L"{46CE9EDE-447C-11D0-98FC-00A0C9058BF6}", L"CWsbDbKey", NULL },
{ L"{E707D9B2-4F89-11D0-81CC-00A0C91180F2}", L"CFsaServerNTFS", NULL },
{ L"{FCDC8671-7329-11d0-81DF-00A0C91180F2}", L"CFsaFilterNTFS", NULL },
{ L"{112981B3-1BA5-11D0-81B2-00A0C91180F2}", L"CFsaResourceNTFS", NULL },
{ L"{0B8B6F12-8B3A-11D0-990C-00A0C9058BF6}", L"CFsaPremigratedDb", NULL },
{ L"{14005FF1-8B4F-11d0-81E6-00A0C91180F2}", L"CFsaTruncatorNTFS", NULL },
{ L"{CECCB131-286D-11d1-993E-0060976A546D}", L"CFsaRecoveryRec", NULL },
{ L"{7CA819F2-8AAB-11D0-990C-00A0C9058BF6}", L"CFsaPremigratedRec", NULL },
{ L"{B2AD2931-84FD-11d0-81E4-00A0C91180F2}", L"CFsaFilterClientNTFS", NULL },
{ L"{F7860350-AA27-11d0-B16D-00A0C916F120}", L"CFsaPostIt", NULL },
{ L"{B2AD2932-84FD-11d0-81E4-00A0C91180F2}", L"CFsaFilterRecallNTFS", NULL },
{ L"{112981B2-1BA5-11D0-81B2-00A0C91180F2}", L"CFsaScanItemNTFS", NULL },
{ L"{7B22FF29-1AD6-11D0-81B1-00A0C91180F2}", L"CHsmActionManage", NULL },
{ L"{D9E04211-14D7-11d1-9938-0060976A546D}", L"CHsmActionOnResourcePostValidate", NULL },
{ L"{D9E04212-14D7-11d1-9938-0060976A546D}", L"CHsmActionOnResourcePreValidate", NULL },
{ L"{7B22FF24-1AD6-11D0-81B1-00A0C91180F2}", L"CHsmActionTruncate", NULL },
{ L"{D3AF5DB1-1DF8-11D0-81B6-00A0C91180F2}", L"CHsmActionUnmanage", NULL },
{ L"{7B22FF26-1AD6-11D0-81B1-00A0C91180F2}", L"CHsmActionValidate", NULL },
{ L"{AD40235F-00FC-11D0-819C-00A0C91180F2}", L"CHsmCritAlways", NULL },
{ L"{CFB04622-1C9F-11D0-81B4-00A0C91180F2}", L"CHsmCritManageable", NULL },
{ L"{7B22FF2C-1AD6-11D0-81B1-00A0C91180F2}", L"CHsmCritMigrated", NULL },
{ L"{7B22FF2D-1AD6-11D0-81B1-00A0C91180F2}", L"CHsmCritPremigrated", NULL },
{ L"{AD402346-00FC-11D0-819C-00A0C91180F2}", L"CHsmJob", NULL },
{ L"{AD402364-00FC-11D0-819C-00a0C91180F2}", L"CHsmJobContext", NULL },
{ L"{AD40234B-00FC-11D0-819C-00a0C91180F2}", L"CHsmJobDef", NULL },
{ L"{B8E1CD21-81D3-11d0-81E4-00A0C91180F2}", L"CHsmJobWorkItem", NULL },
{ L"{AB939AD0-6D67-11d0-9E2E-00A0C916F120}", L"CHsmManagedResource", NULL },
{ L"{8448dd80-7614-11d0-9e33-00a0c916f120}", L"CHsmManagedResourceCollection", NULL },
{ L"{BEA60F8A-7EBA-11d0-81E4-00A0C91180F2}", L"CHsmPhase", NULL },
{ L"{AD402350-00FC-11D0-819C-00A0C91180F2}", L"CHsmPolicy", NULL },
{ L"{AD40235A-00FC-11D0-819C-00A0C91180F2}", L"CHsmRule", NULL },
{ L"{C2E29801-B1BA-11d0-81E9-00A0C91180F2}", L"CHsmRuleStack", NULL },
{ L"{2D1E3156-25DE-11D0-8073-00A0C905F098}", L"CHsmServer", NULL },
{ L"{BEA60F80-7EBA-11d0-81E4-00A0C91180F2}", L"CHsmSession", NULL },
{ L"{FF67BB34-8430-11d0-81E4-00A0C91180F2}", L"CHsmSessionTotals", NULL },
{ L"{61F0B790-82D9-11d0-9E35-00A0C916F120}", L"CHsmStoragePool", NULL },
{ L"{23E45B60-C598-11d0-B16F-00A0C916F120}", L"CHsmWorkItem", NULL },
{ L"{247DF540-C558-11d0-B16F-00A0C916F120}", L"CHsmWorkQueue", NULL },
{ L"{450024A3-47D0-11D0-9E1E-00A0C916F120}", L"CBagHole", NULL },
{ L"{B13FA473-4E1B-11D0-9E22-00A0C916F120}", L"CBagInfo", NULL },
{ L"{F0D7AFE0-9026-11d0-9E3B-00A0C916F120}", L"CMediaInfo", NULL },
{ L"{768AD5A4-40C8-11D0-9E17-00A0C916F120}", L"CSegDB", NULL },
{ L"{37F704E6-3EF9-11D0-9E17-00A0C916F120}", L"CSegRec", NULL },
{ L"{BD030C00-000B-11D0-D0DD-00A0C9190459}", L"CMemIo Class", NULL },
{ L"{BD040C00-000B-11D0-D0DD-00A0C9190459}", L"CNtTapeIo Class", NULL },
{ L"{BD050C00-000B-11D0-D0DD-00A0C9190459}", L"CNtFileIo Class", NULL },
{ L"{FE37FA04-3729-11D0-8CF4-00A0C9190459}", L"RmsCartridge Class", NULL },
{ L"{FE37FA07-3729-11D0-8CF4-00A0C9190459}", L"RmsMediumChanger Class", NULL },
{ L"{FE37FA12-3729-11D0-8CF4-00A0C9190459}", L"RmsClient Class", NULL },
{ L"{FE37FA03-3729-11D0-8CF4-00A0C9190459}", L"RmsDriveClass Class", NULL },
{ L"{FE37FA05-3729-11D0-8CF4-00A0C9190459}", L"RmsDrive Class", NULL },
{ L"{FE37FA08-3729-11D0-8CF4-00A0C9190459}", L"RmsIEPort Class", NULL },
{ L"{FE37FA02-3729-11D0-8CF4-00A0C9190459}", L"RmsLibrary Class", NULL },
{ L"{FE37FA09-3729-11D0-8CF4-00A0C9190459}", L"RmsMediaSet Class", NULL },
{ L"{FE37FA13-3729-11D0-8CF4-00A0C9190459}", L"RmsNTMS Class", NULL },
{ L"{FE37FA11-3729-11D0-8CF4-00A0C9190459}", L"RmsPartition Class", NULL },
{ L"{FE37FA10-3729-11D0-8CF4-00A0C9190459}", L"RmsRequest Class", NULL },
{ L"{FE37FA01-3729-11D0-8CF4-00A0C9190459}", L"RmsServer Class", NULL },
{ L"{FE37FA14-3729-11D0-8CF4-00A0C9190459}", L"RmsSink Class", NULL },
{ L"{FE37FA06-3729-11D0-8CF4-00A0C9190459}", L"RmsStorageSlot Class", NULL },
{ L"{FE37FA15-3729-11D0-8CF4-00A0C9190459}", L"RmsTemplate Class", NULL },
{ NULL, NULL, NULL }
};
// Local functions
static BOOL AddPointer(const void* addr, ULONG size, int index, const char * filename,
int linenum);
static OLECHAR* GuidToObjectName(const GUID& guid);
static BOOL SubPointer(const void* addr, int index);
// AddPointer - add a new pointer to the pointer list
// Return FALSE on failure (list is full or pointer is already in list)
static BOOL AddPointer(const void* addr, ULONG size, int index,
const char * filename, int linenum)
{
int empty_slot = -1;
int i;
BOOL status = TRUE;
if (!pointer_data_initialized) {
pointer_data[POINTER_DATA_CURRENT].count = 0;
pointer_data[POINTER_DATA_CURRENT].size = 0;
pointer_data[POINTER_DATA_MAX].count = 0;
pointer_data[POINTER_DATA_MAX].size = 0;
pointer_data[POINTER_DATA_CUMULATIVE].count = 0;
pointer_data[POINTER_DATA_CUMULATIVE].size = 0;
pointer_data_initialized = TRUE;
}
for (i = 0; i < pointer_list_count; i++) {
if (NULL == pointer_list[i].addr) {
empty_slot = i;
} else if (addr == pointer_list[i].addr) {
WsbTraceAlways(OLESTR("AddPointer: address already in list: %lx (<%ls>, line: <%ld>\n"),
(ULONG)addr, (OLECHAR *)filename, index);
status = FALSE;
break;
}
}
if (i == pointer_list_count) {
// Not in list. Is the list full?
if (-1 == empty_slot && POINTER_LIST_SIZE == pointer_list_count) {
WsbTraceAlways(OLESTR("AddPointer: pointer list is full: %lx\n"),
(ULONG)addr);
status = FALSE;
} else if (-1 == empty_slot) {
pointer_list_count++;
} else {
i = empty_slot;
}
}
if (status) {
pointer_list[i].addr = addr;
pointer_list[i].size = size;
pointer_list[i].index = index;
pointer_list[i].filename = filename;
pointer_list[i].linenum = linenum;
pointer_data[POINTER_DATA_CURRENT].count++;
pointer_data[POINTER_DATA_CURRENT].size += size;
if (pointer_data[POINTER_DATA_CURRENT].count > pointer_data[POINTER_DATA_MAX].count) {
pointer_data[POINTER_DATA_MAX].count = pointer_data[POINTER_DATA_CURRENT].count;
}
if (pointer_data[POINTER_DATA_CURRENT].size > pointer_data[POINTER_DATA_MAX].size) {
pointer_data[POINTER_DATA_MAX].size = pointer_data[POINTER_DATA_CURRENT].size;
}
pointer_data[POINTER_DATA_CUMULATIVE].count++;
pointer_data[POINTER_DATA_CUMULATIVE].size += size;
pointer_list[i].order = pointer_data[POINTER_DATA_CUMULATIVE].count;
}
return(status);
}
// GuidToObjectName - convert a guid to an object name
static OLECHAR* GuidToObjectName(const GUID& guid)
{
HRESULT hr = S_OK;
int i;
OLECHAR * name = NULL;
try {
// Need to do conversions from string to Guid?
if (NULL == object_name_table[0].pGuid) {
i = 0;
while (object_name_table[i].guid_string) {
GUID * pg = new GUID;
WsbAffirmHr(WsbGuidFromString(object_name_table[i].guid_string,
pg));
object_name_table[i].pGuid = pg;
i++;
}
}
// See if this Guid is in the name table
i = 0;
while (object_name_table[i].guid_string) {
if (guid == *object_name_table[i].pGuid) {
name = object_name_table[i].name;
break;
}
i++;
}
} WsbCatch(hr);
return(name);
}
// SubPointer - remove a pointer from the pointer list
// Return FALSE on failure (pointer is not in list or index doesn't match)
static BOOL SubPointer(const void* addr, int index)
{
int i;
BOOL status = TRUE;
for (i = 0; i < pointer_list_count; i++) {
if (addr == pointer_list[i].addr) {
break;
}
}
if (i == pointer_list_count) {
WsbTraceAlways(OLESTR("SubPointer: pointer not found in list: %lx\n"),
(ULONG)addr);
status = FALSE;
} else if (index != pointer_list[i].index) {
WsbTraceAlways(OLESTR("SubPointer: type index doesn't match for pointer: %lx\n"),
(ULONG)addr);
WsbTraceAlways(OLESTR(", original type index = %d, new type index = %d\n"),
pointer_list[i].index, index);
status = FALSE;
}
if (status) {
pointer_list[i].addr = NULL;
pointer_data[POINTER_DATA_CURRENT].count--;
pointer_data[POINTER_DATA_CURRENT].size -= pointer_list[i].size;
}
return(status);
}
HRESULT WsbObjectAdd(
const GUID & guid,
const void * addr
)
/*++
Routine Description:
Add another object to the object table
Arguments:
guid - Guid for the object type
addr - Memory address of object
Return Value:
S_OK - Success
--*/
{
HRESULT hr = S_OK;
#if defined(CRT_DEBUG_MEMORY)
// Set CRT debug flag
static BOOL first = TRUE;
if (first) {
int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
// Set the new state for the flag
_CrtSetDbgFlag( tmpFlag );
}
#endif
try {
int i;
// Reserve the first entry for non-objects and table overflow
if (0 == object_table_count) {
object_table[0].guid = GUID_NULL;
object_table[0].count = 0;
object_table[0].total_count = 0;
object_table[0].max_count = 0;
object_table_count = 1;
}
// Check in object type is already in table
for (i = 0; i < object_table_count; i++) {
if (guid == object_table[i].guid) break;
}
// Add a new entry if not (and there is room)
if (i == object_table_count && i < OBJECT_TABLE_SIZE) {
// WsbTraceAlways(OLESTR("WsbObjectAdd: new object, guid = %ls\n"),
// WsbGuidAsString(guid));
#if defined(CRT_DEBUG_MEMORY)
WsbTraceAlways(OLESTR("WsbObjectAdd: _CrtCheckMemory = %ls\n"),
WsbBoolAsString(_CrtCheckMemory()));
#endif
object_table[i].guid = guid;
object_table[i].count = 0;
object_table[i].total_count = 0;
object_table[i].max_count = 0;
object_table_count++;
} else if (OBJECT_TABLE_SIZE == i) {
// Use the first entry for everything else
i = 0;
}
object_table[i].count++;
object_table[i].total_count++;
if (object_table[i].count > object_table[i].max_count) {
object_table[i].max_count = object_table[i].count;
}
// Add to pointer list
AddPointer(addr, 0, i, NULL, 0);
} WsbCatch(hr);
return(hr);
}
HRESULT WsbObjectSub(
const GUID & guid,
const void * addr
)
/*++
Routine Description:
Subtract an object from the object table
Arguments:
guid - Guid for the object type
addr - Memory address of object
Return Value:
S_OK - Success
--*/
{
HRESULT hr = S_OK;
try {
int i;
// Find the object type in table
for (i = 0; i < object_table_count; i++) {
if (guid == object_table[i].guid) {
// Allow count to go negative since this could indicate a problem
object_table[i].count--;
// Remove from pointer list
SubPointer(addr, i);
}
}
} WsbCatch(hr);
return(hr);
}
HRESULT WsbObjectTracePointers(
ULONG flags
)
/*++
Routine Description:
Dump the pointer list information to the trace file.
Arguments:
flags - WSB_OTP_ flags
Return Value:
S_OK - Success
--*/
{
HRESULT hr = S_OK;
try {
int i;
OLECHAR string[300];
// Dump the current sequence number
if (flags & WSB_OTP_SEQUENCE) {
WsbTraceAlways(OLESTR("WsbObjectTracePointers: current sequence number = %ld\n"),
pointer_data[POINTER_DATA_CUMULATIVE].count);
}
// Dump statistics
if (flags & WSB_OTP_STATISTICS) {
WsbTraceAlways(OLESTR("WsbObjectTracePointers: count, size\n"));
WsbTraceAlways(OLESTR(" Current %8ld %12ls\n"),
pointer_data[POINTER_DATA_CURRENT].count,
WsbLonglongAsString(pointer_data[POINTER_DATA_CURRENT].size));
WsbTraceAlways(OLESTR(" Maximum %8ld %12ls\n"),
pointer_data[POINTER_DATA_MAX].count,
WsbLonglongAsString(pointer_data[POINTER_DATA_MAX].size));
WsbTraceAlways(OLESTR(" Cumulative %8ld %12ls\n"),
pointer_data[POINTER_DATA_CUMULATIVE].count,
WsbLonglongAsString(pointer_data[POINTER_DATA_CUMULATIVE].size));
}
// Dump non-NULL pointers
if (flags & WSB_OTP_ALLOCATED) {
WsbTraceAlways(OLESTR("WsbObjectTracePointers: allocated memory list (addr, size, order, name/GUID/file):\n"));
for (i = 0; i < pointer_list_count; i++) {
if (pointer_list[i].addr) {
GUID guid = GUID_NULL;
int index;
OLECHAR * name = NULL;
OLECHAR * pstr = NULL;
index = pointer_list[i].index;
if (index > 0 && index < object_table_count) {
guid = object_table[index].guid;
name = GuidToObjectName(guid);
}
if (0 == index) {
wsprintf(string, OLESTR("%hs(%d)"), pointer_list[i].filename,
pointer_list[i].linenum);
pstr = string;
} else if (name) {
pstr = name;
} else {
wcscpy(string, WsbGuidAsString(guid));
pstr = string;
}
WsbTraceAlways(OLESTR(" %8lx %8ld %8ld %ls\n"), pointer_list[i].addr,
pointer_list[i].size, pointer_list[i].order, pstr);
}
}
}
#if defined(CRT_DEBUG_MEMORY)
WsbTraceAlways(OLESTR("WsbObjectTrace: calling _CrtMemCheckpoint\n"));
_CrtMemCheckpoint(&CrtMemState);
WsbTraceAlways(OLESTR("WsbObjectTrace: MemState.lHighWaterCount = %ld, TotalCount = %ld\n"),
CrtMemState.lHighWaterCount, CrtMemState.lTotalCount);
WsbTraceAlways(OLESTR("WsbObjectTrace: MemState.blocks count & size\n"));
WsbTraceAlways(OLESTR(" FREE %4ld %6ld\n"), CrtMemState.lCounts[_FREE_BLOCK],
CrtMemState.lSizes[_FREE_BLOCK]);
WsbTraceAlways(OLESTR(" NORMAL %4ld %6ld\n"), CrtMemState.lCounts[_NORMAL_BLOCK],
CrtMemState.lSizes[_NORMAL_BLOCK]);
WsbTraceAlways(OLESTR(" CRT %4ld %6ld\n"), CrtMemState.lCounts[_CRT_BLOCK],
CrtMemState.lSizes[_CRT_BLOCK]);
WsbTraceAlways(OLESTR(" IGNORE %4ld %6ld\n"), CrtMemState.lCounts[_IGNORE_BLOCK],
CrtMemState.lSizes[_IGNORE_BLOCK]);
WsbTraceAlways(OLESTR(" CLIENT %4ld %6ld\n"), CrtMemState.lCounts[_CLIENT_BLOCK],
CrtMemState.lSizes[_CLIENT_BLOCK]);
// WsbTraceAlways(OLESTR("WsbObjectTrace: calling _CrtMemDumpStatistics\n"));
// _CrtMemDumpStatistics(&CrtMemState);
// _CrtDumpMemoryLeaks();
#endif
} WsbCatch(hr);
return(hr);
}
HRESULT WsbObjectTraceTypes(
void
)
/*++
Routine Description:
Dump the object table information to the trace file.
Arguments:
None.
Return Value:
S_OK - Success
--*/
{
HRESULT hr = S_OK;
try {
int i;
WsbTraceAlways(OLESTR("WsbObjectTraceTypes: object table (GUID, total count, max count, current count, name):\n"));
// Find the object type in table
for (i = 0; i < object_table_count; i++) {
OLECHAR * name;
name = GuidToObjectName(object_table[i].guid);
WsbTraceAlways(OLESTR(" %ls %6ld %5ld %5ld %ls\n"), WsbGuidAsString(object_table[i].guid),
object_table[i].total_count, object_table[i].max_count,
object_table[i].count, (name ? name : OLESTR("")));
}
} WsbCatch(hr);
return(hr);
}
LPVOID WsbMemAlloc(ULONG cb, const char * filename, int linenum)
/*++
Routine Description:
Debug tracking replacement for CoTaskAlloc.
--*/
{
LPVOID p;
p = CoTaskMemAlloc(cb);
if (p) {
AddPointer(p, cb, 0, filename, linenum);
}
return(p);
}
void WsbMemFree(LPVOID pv, const char *, int)
/*++
Routine Description:
Debug tracking replacement for CoTaskFree.
--*/
{
if (pv) {
SubPointer(pv, 0);
}
CoTaskMemFree(pv);
}
LPVOID WsbMemRealloc(LPVOID pv, ULONG cb, const char * filename, int linenum)
/*++
Routine Description:
Debug tracking replacement for CoTaskRealloc.
--*/
{
LPVOID p;
p = CoTaskMemRealloc(pv, cb);
if (p) {
if (pv) {
SubPointer(pv, 0);
}
AddPointer(p, cb, 0, filename, linenum);
}
return(p);
}
BSTR WsbSysAllocString(const OLECHAR FAR * sz,
const char * filename, int linenum)
/*++
Routine Description:
Debug tracking replacement for SysAllocString
--*/
{
BSTR b;
b = SysAllocString(sz);
if (b) {
AddPointer(b, SysStringByteLen(b), 0, filename, linenum);
}
return(b);
}
BSTR WsbSysAllocStringLen(const OLECHAR FAR * sz,
unsigned int cc, const char * filename, int linenum)
/*++
Routine Description:
Debug tracking replacement for SysAllocStringLen
--*/
{
BSTR b;
b = SysAllocStringLen(sz, cc);
if (b) {
AddPointer(b, SysStringByteLen(b), 0, filename, linenum);
}
return(b);
}
void WsbSysFreeString(BSTR bs, const char *, int)
/*++
Routine Description:
Debug tracking replacement for SysFreeString
--*/
{
if (bs) {
SubPointer(bs, 0);
}
SysFreeString(bs);
}
HRESULT WsbSysReallocString(BSTR FAR * pb, const OLECHAR FAR * sz,
const char * filename, int linenum)
/*++
Routine Description:
Debug tracking replacement for SysReallocString
--*/
{
HRESULT hr;
if (*pb) {
SubPointer(*pb, 0);
}
hr = SysReAllocString(pb, sz);
if (*pb) {
AddPointer(*pb, SysStringByteLen(*pb), 0, filename, linenum);
}
return(hr);
}
HRESULT WsbSysReallocStringLen(BSTR FAR * pb,
const OLECHAR FAR * sz, unsigned int cc, const char * filename, int linenum)
/*++
Routine Description:
Debug tracking replacement for SysStringLen
--*/
{
HRESULT hr;
if (*pb) {
SubPointer(*pb, 0);
}
hr = SysReAllocStringLen(pb, sz, cc);
if (*pb) {
AddPointer(*pb, SysStringByteLen(*pb), 0, filename, linenum);
}
return(hr);
}
#endif // WSB_TRACK_MEMORY