#include "precomp.h" #include "linklist.h" #define LLHlliInit LLInit #define LLHlleCreate LLCreate #define LLAddHlleToLl LLAdd #define LLFDeleteHlleFromLl LLDelete #define LLHlleFindNext LLNext #define LLHlleFindLpv LLFind #define LLLpvFromHlle LLLock #define BMUnlock LLUnlock typedef PRTL_CRITICAL_SECTION PCS; PCS PcsAllocInit(void) { PCS pcs = ExAllocatePoolWithTag(PagedPool, sizeof(PCS), 'illh'); if(pcs) InitializeCriticalSection(pcs); return pcs; } #define FreePcs(pcs) ExFreePoolWithTag(pcs, 'illh'); #define AcquireLockPcs(pcs) EnterCriticalSection((PCS)pcs) #define ReleaseLockPcs(pcs) LeaveCriticalSection((PCS)pcs); /*** LLHlliInit * * Purpose: * Create a new list with specified options * * Input: * cbUserData : Number of bytes for user data per node. Must be > 0. * llf : List type flags (llfNull, llfAscending, or llfDescending) * (indicates wether or not list is sorted) * lpfnKill : callback to application for node deletion notification * (may be NULL) * lpfnCmp : Node comparison callback. May be NULL for non-sorted * lists. Otherwise, required. * * Output: * Either returns valid HLLI for newly created list or NULL for failure. * * Exceptions: * * Notes: * *************************************************************************/ HLLI PASCAL LLHlliInit( DWORD cbUserData, LLF llf, LPFNKILLNODE lpfnKill, LPFNFCMPNODE lpfnCmp ) { HLLI hlli; LPLLI lplli; assert( cbUserData ); assert( llf == llfNull || lpfnCmp ); if ( hlli = HlliAlloc() ) { lplli = LockHlli( hlli ); _fmemset( lplli, 0, sizeof( LLI ) ); lplli->pcs = PcsAllocInit(); lplli->cbUserData = cbUserData; lplli->lpfnKill = lpfnKill; lplli->lpfnCmp = lpfnCmp; lplli->llf = llf; UnlockHlli( hlli ); } return hlli; } /*** LLHlleCreate * * Purpose: * Allocate and initialize new node for list * * Input: * hlli : List to create node for. * * Output: * Returns newly created node (zero filled) if successful, otherwise * NULL. * * Exceptions: * * Notes: * *************************************************************************/ HLLE PASCAL LLHlleCreate( HLLI hlli ) { LPLLE lplle; LPLLI lplli; HLLE hlle; WORD cbNode; assert( hlli ); // // Ensure that the list is OK // #if DEBUG > 1 LLFCheckHlli( hlli ); #endif // // Compute size of node with user data area // lplli = LockHlli( hlli ); cbNode = sizeof( LLE ) + lplli->cbUserData; UnlockHlli( hlli ); #ifdef DEBUGVER // // Debug version, we're going to stuff a "sentinel" on to the end of the // user's data for consistency checks. // cbNode += sizeof( WORD ); #endif // DEBUGVER // // Allocate the node // if ( hlle = HlleAllocCb( cbNode ) ) { lplle = LockHlle( hlle ); _fmemset( (LPV)lplle, 0, cbNode ); #ifdef DEBUGVER // // Stuff in the consistency check stuff // *(WORD FAR *)((BYTE FAR *)lplle + cbNode - sizeof( WORD )) = WCONSIST; lplle->wTest = WCONSIST; #endif // DEBUGVER UnlockHlle( hlle ); } return hlle; } /*** LLFAddHlleToLl * * Purpose: * Add a new node to the end of a list. * * Input: * hlli : List to add node to. * hlle : Node to append to list. * * Output: * * Exceptions: * * Notes: * *************************************************************************/ void PASCAL LLAddHlleToLl( HLLI hlli, HLLE hlle ) { LPLLE lplle; LPLLI lplli; assert( hlli ); assert( hlle ); lplle = LockHlle( hlle ); lplli = LockHlli( hlli ); AcquireLockPcs(lplli->pcs); assert( lplli->llf == llfNull ); // // Initalize node: Since this is to be the last item in the list, // pNext should be null. Also, the pPrev should point to the // currently last item in the list (includes NULL) // lplle->hlleNext = hlleNull; #ifdef DBLLINK lplle->hllePrev = lplli->hlleTail; #endif // DBLLINK // // Chalk up one more for the list // lplli->chlleMac++; // // If the pHead is NULL then initialize the pHead and pTail // (you know, like this is the only item in the list) // if ( lplli->hlleHead == hlleNull ) { lplli->hlleHead = lplli->hlleTail = hlle; } // // Otherwise, update the tail pointer and the pNext for the old tail // else { HLLE hlleT = lplli->hlleTail; LockHlle( hlleT )->hlleNext = hlle; UnlockHlle( hlleT ); lplli->hlleTail = hlle; } UnlockHlle( hlle ); // // Ensure that the list is OK // #if DEBUG > 1 LLFCheckHlli( hlli ); #endif ReleaseLockPcs(lplli->pcs); UnlockHlli( hlli ); } /*** * * Purpose: * * Input: * * Output: * * Exceptions: * * Notes: * *************************************************************************/ void PASCAL LLInsertHlleInLl( HLLI hlli, HLLE hlleNew, DWORD lParam ) { USHORT fNeedPos = fTrue; LPLLI lplli = LockHlli( hlli ); HLLE hlle = hlleNull; HLLE hllePrev = hlleNull; WORD wPos = 0; LPFNFCMPNODE lpfnCmp; LPV lpv; assert( hlli ); AcquireLockPcs( lplli->pcs ); lpfnCmp = lplli->lpfnCmp; assert( lplli->llf == llfNull || lpfnCmp ); lpv = LLLpvFromHlle( hlleNew ); switch( lplli->llf ) { case llfNull: hlle = LLHlleFindNext( hlli, hlle ); while( wPos++ < LOWORD( lParam ) ) { hllePrev = hlle; hlle = LLHlleFindNext( hlli, hlle ); } break; case llfAscending: while( fNeedPos && ( hlle = LLHlleFindNext( hlli, hlle ) ) ) { fNeedPos = lpfnCmp( LLLpvFromHlle( hlle ), lpv, lParam ) == fCmpLT; UnlockHlle( hlle ); if ( fNeedPos ) { hllePrev = hlle; } } break; case llfDescending: while( fNeedPos && ( hlle = LLHlleFindNext( hlli, hlle ) ) ) { fNeedPos = lpfnCmp( LLLpvFromHlle( hlle ), lpv, lParam ) == fCmpGT; UnlockHlle( hlle ); if ( fNeedPos ) { hllePrev = hlle; } } break; } LLInsertHlle( hlli, hllePrev, hlleNew, hlle ); UnlockHlle( hlleNew ); ReleaseLockPcs( lplli->pcs ); UnlockHlli( hlli ); } /*** LLFDeleteHlleIndexed * * Purpose: * Delete the ith node from a list. * * Input: * hlli : List containing node to delete * lPos : zero based index of node to delete. * * Output: * Return fTrue for successful deletion, else fFalse. * * Exceptions: * * Notes: * *************************************************************************/ BOOL PASCAL LLFDeleteHlleIndexed( HLLI hlli, DWORD lPos ) { USHORT fRet = fFalse; LPLLI lplli = LockHlli( hlli ); HLLE hlleKill = lplli->hlleHead; HLLE hllePrev = hlleNull; DWORD lPosCur = 0L; assert( hlli ); AcquireLockPcs( lplli->pcs ); // // Make sure that we're not deleting past the end of the list! // if ( lPos < lplli->chlleMac ) { // // Chug through the list until we find the sucker to kill! // while ( lPos != lPosCur ) { hllePrev = hlleKill; hlleKill = LLHlleFindNext( hlli, hlleKill ); ++lPosCur; } LLDeleteHlle( hlli, hllePrev, hlleKill ); fRet = fTrue; } #if DEBUG > 1 LLFCheckHlli( hlli ); #endif ReleaseLockPcs( lplli->pcs ); UnlockHlli( hlli ); return fRet; } /*** LLFDeleteLpvFromLl * * Purpose: * Delete a node from a list containing lpv data. * * Input: * hlli : List containing node to delete * hlle : Node to begin search for delete node at. * lpv : far pointer to comparison data. Passed onto compare callback. * lParam : Application supplied data. Just passed onto compare callback. * * Output: * Returns fTrue if node has been deleted, else fFalse * * Exceptions: * * Notes: * There must be a compare routine assiciated with this list!!! * For a doubly linked list, this is simple. For singly linked * list, we have to go through the list to get the previous node * to ensure that the pointers are correct. * *************************************************************************/ BOOL PASCAL LLFDeleteLpvFromLl( HLLI hlli, HLLE hlle, LPV lpv, DWORD lParam ) { #ifdef DBLLINK LPLLI lplli = LockHlli ( hlli ); AcquireLockPcs ( lplli->pcs ); hlle = LLHlleFindLpv( hlli, hlle, lpv, lParam ); if ( hlle ) { LLDeleteHlle( hlli, LLHlleFindPrev( hlli, hlle ), hlle ); } #if DEBUG > 1 LLFCheckHlli( hlli ); #endif ReleaseLockPcs ( lplli->pcs ); UnlockHlli ( hlli ); return hlle != hlleNull; #else // DBLLINK LPLLI lplli = LockHlli( hlli ); HLLE hlleNext; LPFNFCMPNODE lpfnCmp; AcquireLockPcs ( lplli->pcs ); lpfnCmp = lplli->lpfnCmp; assert( lpfnCmp ); // // We're goint to delete the first occurance AFTER the one specified! // hlleNext = LLHlleFindNext( hlli, hlle ); // // Look up the data in the list // while( hlleNext && lpfnCmp( LLLpvFromHlle( hlleNext ), lpv, lParam ) != fCmpEQ ) { UnlockHlle( hlleNext ); hlle = hlleNext; hlleNext = LLHlleFindNext( hlli, hlleNext ); } // // if hlleNext is non-null then we've found something to delete!!! // if ( hlleNext ) { LLDeleteHlle( hlli, hlle, hlleNext ); } #if DEBUG > 1 LLFCheckHlli( hlli ); #endif ReleaseLockPcs ( lplli->pcs ); UnlockHlli( hlli ); return hlleNext != hlleNull; #endif // DBLLINK } /*** LLHlleFindNext * * Purpose: * Get the next node in a list. * * Input: * hlli : List to search in. * hlle : Place to begin. If NULL, then return hlleHead. * * Output: * Returns a handle to the next item in the list. hlleNull is returned * if the end of the list has been reached. * * Exceptions: * * Notes: * *************************************************************************/ HLLE PASCAL LLHlleFindNext( HLLI hlli, HLLE hlle ) { HLLE hlleRet; // // if hlle is non-null, return the next handle // if ( hlle ) { hlleRet = LockHlle( hlle )->hlleNext; UnlockHlle( hlle ); } // // otherwise, we want the beginning of the list, so return the "head" // else { LPLLI lplli; lplli = LockHlli ( hlli ); AcquireLockPcs ( lplli->pcs ); hlleRet = lplli->hlleHead; ReleaseLockPcs ( lplli->pcs ); UnlockHlli( hlli ); } return hlleRet; } /*** LLHlleFindPrev * * Purpose: * Get the previous node in a list. DOUBLY LINKED LIBRARY ONLY!!! * * Input: * hlli : List to search in. * hlle : Place to beging search at. If hlleNull, the return the * last node in the list. * * Output: * Return a handle to the previous node in the list, hlleNull if the * beginning of the list has been hit. * * Exceptions: * * Notes: * *************************************************************************/ #ifdef DBLLINK HLLE PASCAL LLHlleFindPrev( HLLI hlli, HLLE hlle ) { HLLE hlleRet; // // if hlle is non-null, return the next handle // if ( hlle ) { hlleRet = LockHlle( hlle )->hllePrev; UnlockHlle( hlle ); } // // otherwise, we want the beginning of the list, so return the "head" // else { LPLLI lplli; lplli = LockHlli ( hlli ); AcquireLockPcs ( lplli->pcs ); hlleRet = lplli->hlleTail; ReleaseLockPcs ( lplli->pcs ); UnlockHlli( hlli ); } return hlleRet; } #endif // DBLLINK /*** LLChlleDestroyList * * Purpose: * Free all memory associated with a specified list. Completely * destroys the list. Must call HlliInit() to add new items to list. * * Input: * hlli : List to destroy * * Output: * Returns number of nodes destroyed in list. * * Exceptions: * * Notes: * *************************************************************************/ DWORD PASCAL LLChlleDestroyLl( HLLI hlli ) { DWORD cRet = 0; LPLLI lplli; assert( hlli ); lplli = LockHlli ( hlli ); AcquireLockPcs ( lplli->pcs ); while ( LLChlleInLl( hlli ) != 0 ) { LLFDeleteHlleIndexed( hlli, 0 ); ++cRet; } ReleaseLockPcs ( lplli->pcs ); FreePcs ( lplli->pcs ); FreeHlli( hlli ); return cRet; } /*** LLHlleFindLpv * * Purpose: * Locate a node in a list containing specific data. * * Input: * hlli : List to search in. * hlle : Starting place to begin search. If NULL, start at * beginning of list. * lpv : Data passed on to compare callback for match. * lParam : Application supplied data. Just passed on to callback. * * Output: * Returns handle to node meeting compare criteria or NULL if not found. * * Exceptions: * * Notes: * Requires that list has a callback function. * *************************************************************************/ HLLE PASCAL LLHlleFindLpv( HLLI hlli, HLLE hlle, LPV lpvFind, DWORD lParam ) { HLLE hlleRet = hlleNull; LPLLI lplli = LockHlli( hlli ); LPLLE lplle; AcquireLockPcs ( lplli->pcs ); assert( hlli ); assert( lplli->lpfnCmp ); hlle = LLHlleFindNext( hlli, hlle ); while ( hlle != hlleNull && hlleRet == hlleNull ) { lplle = LockHlle( hlle ); if ( lplli->lpfnCmp( (LPV)lplle->rgw, lpvFind, lParam ) == fCmpEQ ) { hlleRet = hlle; } UnlockHlle( hlle ); hlle = LLHlleFindNext( hlli, hlle ); } ReleaseLockPcs ( lplli->pcs ); UnlockHlli( hlli ); return hlleRet; } /*** LLFCheckHlli * * Purpose: * Consistency check for a list. * * Input: & hlli : List to check. * * Output: * Returns non-null if list is OK, otherwise failure is indicated. * * Exceptions: * * Notes: * This is a debug only function. We will zip through the entire list * and check the head and tail words of EACH node for our magic WCONSIST * value. If either one is not correct, someone trashed a node. We also * check to see that the last node in the list is actually hlleTail for the * list. * *************************************************************************/ #ifdef DEBUGVER BOOL PASCAL LLFCheckHlli( HLLI hlli ) { LPLLI lplli = LockHlli( hlli ); LPLLE lplle; HLLE hlle = hlleNull; WORD cbOffSet = sizeof( LLE ) + lplli->cbUserData; ULONG fRet; HLLE hlleLast = LLHlleFindNext( hlli, hlleNull ); fRet = lplli->cbUserData; // && lplli->lpfnCmp && lplli->lpfnKill; if ( lplli->chlleMac ) { fRet = fRet && lplli->hlleHead && lplli->hlleTail; } else { fRet = fRet && !lplli->hlleHead && !lplli->hlleTail; } while( fRet && ( hlle = LLHlleFindNext( hlli, hlle ) ) ) { lplle = LockHlle( hlle ); fRet = ( lplle->wTest == WCONSIST && *((WORD FAR *)( (BYTE FAR *)lplle + cbOffSet )) == WCONSIST ); hlleLast = hlle; UnlockHlle( hlle ); } if ( fRet ) { fRet = hlleLast == lplli->hlleTail; } UnlockHlli( hlli ); assert( fRet ); return fRet; } #endif // DEBUGVER /*** LLInsertHlle * * Purpose: * INTERNAL utility function to insert a node into a specified place * in the list. This will update the pointers and head/tail information * for the list. * * Input: * hlli : List to insert node into. * hllePrev : Node which will appear right before the inserted one. * hlle : Our newly created node. * hlleNext : The node which will be immediately after the new one. * * Output: * * Exceptions: * * Notes: * *************************************************************************/ void PASCAL LLInsertHlle( HLLI hlli, HLLE hllePrev, HLLE hlle, HLLE hlleNext ) { LPLLE lplle; LPLLI lplli; // // Note that we don't need to acquire critical sections here since the // higher level routines do that. // lplli = LockHlli( hlli ); lplle = LockHlle( hlle ); // // If there is a previous node, update its hnext // if ( hllePrev ) { LockHlle( hllePrev )->hlleNext = hlle; UnlockHlle( hllePrev ); } // // Otherwise, update the head // else { lplli->hlleHead = hlle; } // // Set the hNext for the new node // lplle->hlleNext = hlleNext; // // We're adding to the end of the list, update the hlleTail // if ( hlleNext == hlleNull ) { lplli->hlleTail = hlle; } // // If there is a next, update its hPrev // #ifdef DBLLINK else { LockHlle( hlleNext )->hllePrev = hlle; UnlockHlle( hlleNext ); } // // Set the hPrev for the new node // lplle->hllePrev = hllePrev; #endif // DBLLINK // // Increment the number of items on the list // ++lplli->chlleMac; UnlockHlle( hlle ); UnlockHlli( hlli ); } /*** LLFDeleteHlleFromLl * * Purpose: * Delete a specified hlle. Update head/tail and node pointers. * * Input: * hlli : List containing node to delete. * hlle : The node to destroy. * * Output: * None. * * Exceptions: * * Notes: * For doubly linked list, we could just use the hllePrev handle, * but there's no guarantee that the node is in the list specified. So * We do a look up just to make sure. * *************************************************************************/ BOOL PASCAL LLFDeleteHlleFromLl( HLLI hlli, HLLE hlle ) { HLLE hllePrev = hlleNull; HLLE hlleCur = hlleNull; USHORT fRet; LPLLI lplli; assert( hlli ); // // REVIEW:PERFORMANCE don't need to lock hlli down on non NT versions // lplli = LockHlli ( hlli ); AcquireLockPcs ( lplli->pcs ); assert( hlle ); while( ( hlleCur = LLHlleFindNext( hlli, hlleCur ) ) && hlleCur != hlle ) { hllePrev = hlleCur; } if ( fRet = ( hlle == hlleCur ) ) { LLDeleteHlle( hlli, hllePrev, hlle ); } ReleaseLockPcs ( lplli->pcs ); UnlockHlli ( hlli ); return fRet; } /*** LLDeleteHlle * * Purpose: * INTERNAL utility routine to delete a node. Update head/tail and * node pointers. * * Input: * hlli : List containing node to delete. * hllePrev : Node immediately preceding node to be deleted in the list. * hlle : The node to destroy. * * Output: * None. * * Exceptions: * * Notes: * *************************************************************************/ void PASCAL LLDeleteHlle( HLLI hlli, HLLE hllePrev, HLLE hlle ) { LPLLI lplli = LockHlli( hlli ); HLLE hlleNext = LLHlleFindNext( hlli, hlle ); // // If there is a previous node, update its hnext // if ( hllePrev ) { LockHlle( hllePrev )->hlleNext = hlleNext; UnlockHlle( hllePrev ); } // // Otherwise, update the head // else { lplli->hlleHead = hlleNext; } // // We're adding to the end of the list, update the hlleTail // if ( hlleNext == hlleNull ) { lplli->hlleTail = hllePrev; } // // If there is a next, update its hPrev // #ifdef DBLLINK else { LockHlle( hlleNext )->hllePrev = hllePrev; UnlockHlle( hlleNext ); } #endif // DBLLINK // // Let the app free up its own mess for the data from this node // if ( lplli->lpfnKill ) { lplli->lpfnKill( (LPV)LockHlle( hlle )->rgw ); UnlockHlle( hlle ); } FreeHlle( hlle ); // // Decrement the number of items on the list // --lplli->chlleMac; UnlockHlli( hlli ); } /*** LLChlleInLl * * Purpose: * Return the number of nodes in a list * * Input: * hlli : List to get count for * * Output: * Number of nodes in the list. * * Exceptions: * * Notes: * *************************************************************************/ DWORD PASCAL LLChlleInLl( HLLI hlli ) { DWORD lRet; LPLLI lplli; assert( hlli ); lplli = LockHlli ( hlli ); AcquireLockPcs ( lplli->pcs ); lRet = lplli->chlleMac; ReleaseLockPcs ( lplli->pcs ); UnlockHlli( hlli ); return lRet; } /*** LLLpvFromHlle * * Purpose: * Get a FAR pointer to the user data of a node. This locks the node * down. It is the application's responsibility to unlock it! * * Input: * hlle : Node to get data for. * * Output: * A FAR pointer to the data. * * Exceptions: * * Notes: * *************************************************************************/ LPV PASCAL LLLpvFromHlle( HLLE hlle ) { if (hlle == hlleNull) { return NULL; } else { return (LPV)( LockHlle( hlle )->rgw ); } } /*** LLHlleGetLast * * Purpose: * Get the last node in the specified list. * * Input: * hlli : List to look up last item in. * * Output: * handle to the last item, or NULL if empty list. * * Exceptions: * * Notes: * *************************************************************************/ HLLE PASCAL LLHlleGetLast( HLLI hlli ) { HLLE hlleRet; LPLLI lplli; assert( hlli ); lplli = LockHlli ( hlli ); AcquireLockPcs ( lplli->pcs ); hlleRet = lplli->hlleTail; ReleaseLockPcs ( lplli->pcs ); UnlockHlli( hlli ); return hlleRet; } /*** LLHlleAddToHeadOfLI * * Purpose: * Add a new node to the head of a list. * * Input: * hlli : List to add node to. * hlle : Node to append to list. * * Output: * * Exceptions: * * Notes: * *************************************************************************/ void PASCAL LLHlleAddToHeadOfLI( HLLI hlli, HLLE hlle ) { LPLLE lplle; LPLLI lplli; assert( hlli ); assert( hlle ); lplli = LockHlli( hlli ); AcquireLockPcs ( lplli->pcs ); lplle = LockHlle( hlle ); assert( lplli->llf == llfNull ); lplle->hlleNext = lplli->hlleHead; // If the pHead is NULL then initialize the pTail // (you know, like this is the only item in the list) if ( lplli->hlleHead == hlleNull ) { lplli->hlleTail = hlle; } lplli->hlleHead = hlle; #ifdef DBLLINK lplle->hllePrev = hlleNull; #endif // DBLLINK // Chalk up one more for the list lplli->chlleMac++; // Ensure that the list is OK UnlockHlle( hlle ); #if DEBUG > 1 LLFCheckHlli( hlli ); #endif ReleaseLockPcs ( lplli->pcs ); UnlockHlli( hlli ); } /*** LLFRemoveHlleFromLl * * Purpose: * Remove a specified hlle. Update head/tail and node pointers. * * Input: * hlli : List containing node to remove. * hlle : The node to remove. * * Output: * None. * * Exceptions: * * Notes: * For doubly linked list, we could just use the hllePrev handle, * but there's no guarantee that the node is in the list specified. So * We do a look up just to make sure. * *************************************************************************/ BOOL PASCAL LLFRemoveHlleFromLl( HLLI hlli, HLLE hlle ) { HLLE hllePrev = hlleNull; HLLE hlleCur = hlleNull; USHORT fRet; LPLLI lplli; assert( hlli ); assert( hlle ); lplli = LockHlli ( hlli ); AcquireLockPcs ( lplli->pcs ); while( ( hlleCur = LLHlleFindNext( hlli, hlleCur ) ) && hlleCur != hlle ) { hllePrev = hlleCur; } if ( fRet = ( hlle == hlleCur ) ) { HLLE hlleNext = LLHlleFindNext( hlli, hlle ); // If there is a previous node, update its hnext if ( hllePrev ) { LockHlle( hllePrev )->hlleNext = hlleNext; UnlockHlle( hllePrev ); } // Otherwise, update the head else { lplli->hlleHead = hlleNext; } // We're adding to the end of the list, update the hlleTail if ( hlleNext == hlleNull ) { lplli->hlleTail = hllePrev; } // If there is a next, update its hPrev #ifdef DBLLINK else { LockHlle( hlleNext )->hllePrev = hllePrev; UnlockHlle( hlleNext ); } #endif // DBLLINK // Decrement the number of items on the list --lplli->chlleMac; } #if DEBUG > 1 LLFCheckHlli( hlli ); #endif ReleaseLockPcs ( lplli->pcs ); UnlockHlli( hlli ); return fRet; } PVOID MHRealloc(LPVOID pv, size_t cbNew) { PVOID pvNew = MHAlloc(cbNew); BOOLEAN f; if(pvNew) { ULONG cbOld = ExQueryPoolBlockSize(pv, &f); if(cbOld < cbNew) { memcpy(pvNew, pv, cbOld); RtlZeroMemory((LPBYTE)pvNew + cbOld, cbNew - cbOld); } else memcpy(pvNew, pv, cbNew); } MHFree(pv); return pvNew; }