/*++ Copyright (c) 1996 Microsoft Corporation Module Name: watch.c Abstract: This module contains routines for watching changes to the current user's profile directory and the HKEY_CURRENT_USER key. Author: Chuck Lenzmeier (chuckl) Revision History: --*/ #include "setupp.h" #pragma hdrstop // // Debugging aids. // #if WATCH_DEBUG DWORD WatchDebugLevel = 0; #define dprintf(_lvl_,_x_) if ((_lvl_) <= WatchDebugLevel) DbgPrint _x_ #define DEBUG(_x_) _x_ PWCH StartDirectoryName = TEXT(""); PWCH StartKeyName = TEXT(""); PWCH StopDirectoryName = TEXT(""); PWCH StopKeyName = TEXT(""); static PSZ Types[] = {"ACK!", "DIR ", "FILE ", "KEY ", "VALUE"}; static PSZ States[] = {"NONE", "CHANGED", "DELETED", "NEW", "MATCHED"}; #undef MyMalloc #undef MyFree #define MyMalloc MyMallocEx #define MyFree MyFreeEx PVOID MyMallocEx ( IN DWORD Size ); VOID MyFreeEx ( IN PVOID p ); VOID DumpMallocStats ( PSZ Event ); #else #define dprintf(_lvl_,_x_) #define DEBUG(_x_) #define DumpMallocStats(_event) #endif // // Additions to the change types in WatchEnum. // #define WATCH_NONE 0 #define WATCH_MATCHED 4 // // Common header for container entries (directories and keys). // typedef struct _CONTAINER_ENTRY { LIST_ENTRY SiblingListEntry; LIST_ENTRY ContainerList; LIST_ENTRY ObjectList; struct _CONTAINER_ENTRY *Parent; DWORD State; #if WATCH_DEBUG DWORD IsDirectory; #endif } CONTAINER_ENTRY, *PCONTAINER_ENTRY; // // Common header for object entries (files and values). // typedef struct _OBJECT_ENTRY { LIST_ENTRY SiblingListEntry; DWORD State; } OBJECT_ENTRY, *POBJECT_ENTRY; // // Macros for manipulating containers and objects. // #if WATCH_DEBUG #define SetIsDirectory(_container,_isdir) (_container)->IsDirectory = (_isdir) #else #define SetIsDirectory(_container,_isdir) #endif #define InitializeContainer(_container,_state,_parent,_isdir) { \ InitializeListHead(&(_container)->ContainerList); \ InitializeListHead(&(_container)->ObjectList); \ (_container)->Parent = (PCONTAINER_ENTRY)(_parent); \ (_container)->State = (_state); \ SetIsDirectory((_container),(_isdir)); \ } #define InitializeObject(_object,_state) (_object)->State = (_state); #define InsertContainer(_container,_subcontainer) \ InsertTailList(&(_container)->ContainerList,&(_subcontainer)->SiblingListEntry) #define InsertObject(_container,_object) \ InsertTailList(&(_container)->ObjectList,&(_object)->SiblingListEntry) #define RemoveObject(_object) RemoveEntryList(&(_object)->SiblingListEntry) #define RemoveContainer(_container) RemoveEntryList(&(_container)->SiblingListEntry) #define GetFirstObject(_container) \ ((_container)->ObjectList.Flink != &(_container)->ObjectList ? \ CONTAINING_RECORD( (_container)->ObjectList.Flink, \ OBJECT_ENTRY, \ SiblingListEntry ) : NULL) #define GetNextObject(_container,_object) \ ((_object)->SiblingListEntry.Flink != &(_container)->ObjectList ? \ CONTAINING_RECORD( (_object)->SiblingListEntry.Flink, \ OBJECT_ENTRY, \ SiblingListEntry ) : NULL) #define GetFirstContainer(_container) \ ((_container)->ContainerList.Flink != &(_container)->ContainerList ? \ CONTAINING_RECORD( (_container)->ContainerList.Flink, \ CONTAINER_ENTRY, \ SiblingListEntry ) : NULL) #define GetNextContainer(_container) \ ((_container)->SiblingListEntry.Flink != &(_container)->Parent->ContainerList ? \ CONTAINING_RECORD( (_container)->SiblingListEntry.Flink, \ CONTAINER_ENTRY, \ SiblingListEntry ) : NULL) #define GetParent(_container) (_container)->Parent #define GetEntryState(_entry) (_entry)->State #define SetEntryState(_entry,_state) ((_entry)->State = (_state)) #if WATCH_DEBUG #define CONTAINER_NAME(_container) \ (_container)->IsDirectory ? ((PDIRECTORY_ENTRY)(_container))->Name : \ ((PKEY_ENTRY)(_container))->Name #define OBJECT_NAME(_container,_object) \ (_container)->IsDirectory ? ((PFILE_ENTRY)(_object))->Name : \ ((PVALUE_ENTRY)(_object))->Name #endif // // Structures for entries in the watch tree. // typedef struct _DIRECTORY_ENTRY { CONTAINER_ENTRY ; WCHAR Name[1]; } DIRECTORY_ENTRY, *PDIRECTORY_ENTRY; typedef struct _FILE_ENTRY { OBJECT_ENTRY ; FILETIME LastWriteTime; WCHAR Name[1]; } FILE_ENTRY, *PFILE_ENTRY; typedef struct _KEY_ENTRY { CONTAINER_ENTRY ; HKEY Handle; WCHAR Name[1]; } KEY_ENTRY, *PKEY_ENTRY; typedef struct _VALUE_ENTRY { OBJECT_ENTRY ; DWORD Type; DWORD NameLength; DWORD ValueDataLength; WCHAR Name[1]; } VALUE_ENTRY, *PVALUE_ENTRY; // // The root of the watch tree is allocated as a ROOT_ENTRY followed by // a DIRECTORY_ENTRY and a KEY_ENTRY. // typedef struct _ROOT_ENTRY { PDIRECTORY_ENTRY RootDirectoryEntry; PKEY_ENTRY RootKeyEntry; } ROOT_ENTRY, *PROOT_ENTRY; // // Macro for comparing file times. // #define TIMES_EQUAL(_a,_b) \ (((_a).dwLowDateTime == (_b).dwLowDateTime) && \ ((_a).dwHighDateTime == (_b).dwHighDateTime)) typedef struct _KEY_ENUM_CONTEXT { PKEY_ENTRY ParentKey; PWCH CurrentPath; } KEY_ENUM_CONTEXT, *PKEY_ENUM_CONTEXT; // // Forward declaration of local subroutines. // VOID WatchFreeChildren ( IN PCONTAINER_ENTRY Container ); DWORD WatchDirStart ( IN PROOT_ENTRY Root ); DWORD WatchDirStop ( IN PROOT_ENTRY Root ); DWORD WatchKeyStart ( IN PROOT_ENTRY Root ); DWORD WatchKeyStop ( IN PROOT_ENTRY Root ); DWORD AddValueAtStart ( IN PVOID Context, IN DWORD ValueNameLength, IN PWCH ValueName, IN DWORD ValueType, IN PVOID ValueData, IN DWORD ValueDataLength ); DWORD AddKeyAtStart ( IN PVOID Context, IN DWORD KeyNameLength, IN PWCH KeyName ); DWORD CheckValueAtStop ( IN PVOID Context, IN DWORD ValueNameLength, IN PWCH ValueName, IN DWORD ValueType, IN PVOID ValueData, IN DWORD ValueDataLength ); DWORD CheckKeyAtStop ( IN PVOID Context, IN DWORD KeyNameLength, IN PWCH KeyName ); DWORD WatchStart ( OUT PVOID *WatchHandle ) /*++ Routine Description: Starts watching. Captures the initial state of the Start Menu directory and HKEY_CURRENT_USER. Arguments: WatchHandle - returns a handle for calls to the other Watch routines. Return Value: DWORD - Win32 status of the operation. --*/ { PROOT_ENTRY root; PDIRECTORY_ENTRY rootDirectory; PKEY_ENTRY rootKey; DWORD dirSize; DWORD keySize; DWORD size; DWORD error; // // Calculate the size of the root entry, which includes entries for the // root directory and the root key. The root directory and the root key // do not have names, so we don't have to allocate additional space. // // Allocate and initialize the root entry. // #if !WATCH_DEBUG dirSize = (sizeof(DIRECTORY_ENTRY) + 7) & ~7; keySize = (sizeof(KEY_ENTRY) + 7) & ~7; #else dirSize = (sizeof(DIRECTORY_ENTRY) + (wcslen(StartDirectoryName)*sizeof(WCHAR)) + 7) & ~7; keySize = (sizeof(KEY_ENTRY) + (wcslen(StartKeyName)*sizeof(WCHAR)) + 7) & ~7; #endif root = MyMalloc( ((sizeof(ROOT_ENTRY) + 7) & ~7) + dirSize + keySize ); if ( root == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } rootDirectory = (PDIRECTORY_ENTRY)(root + 1); rootKey = (PKEY_ENTRY)((PCHAR)rootDirectory + dirSize); root->RootDirectoryEntry = rootDirectory; root->RootKeyEntry = rootKey; // // Initialize the root directory and the root key. // InitializeContainer( rootDirectory, 0, NULL, TRUE ); InitializeContainer( rootKey, 0, NULL, FALSE ); rootKey->Handle = NULL; #if !WATCH_DEBUG rootDirectory->Name[0] = 0; rootKey->Name[0] = 0; #else wcscpy( rootDirectory->Name, StartDirectoryName ); wcscpy( rootKey->Name, StartKeyName ); #endif // // Start watching the Start Menu directory and the current user key. // error = WatchDirStart( root ); DumpMallocStats( "After WatchDirStart" ); if ( error == NO_ERROR ) { error = WatchKeyStart( root ); DumpMallocStats( "After WatchKeyStart" ); } // // If an error occurred, free the root entry. Otherwise, return the // address of the root entry as the watch handle. // if ( error != NO_ERROR ) { WatchFree( root ); DumpMallocStats( "After WatchFree" ); } else { *WatchHandle = root; } return error; } // WatchStart DWORD WatchStop ( IN PVOID WatchHandle ) /*++ Routine Description: Stops watching. Compares the current state of the directory and key to the initial state. Arguments: WatchHandle - supplies the handle returned by WatchStart. Return Value: DWORD - Win32 status of the operation. --*/ { PROOT_ENTRY root; DWORD error; root = WatchHandle; // // Stop watching the Start Menu directory and the current user key. // Capture the differences from the initial state. // #if WATCH_DEBUG if ( (wcslen(StopDirectoryName) > wcslen(root->RootDirectoryEntry->Name)) || (wcslen(StopKeyName) > wcslen(root->RootKeyEntry->Name)) ) { return ERROR_INVALID_PARAMETER; } wcscpy( root->RootDirectoryEntry->Name, StopDirectoryName ); wcscpy( root->RootKeyEntry->Name, StopKeyName ); #endif error = WatchDirStop( root ); DumpMallocStats( "After WatchDirStop" ); if ( error == NO_ERROR ) { error = WatchKeyStop( root ); DumpMallocStats( "After WatchKeyStop" ); } return error; } // WatchStop DWORD WatchEnum ( IN PVOID WatchHandle, IN PVOID Context, IN PWATCH_ENUM_ROUTINE EnumRoutine ) /*++ Routine Description: Enumerates the new, changed, and deleted elements of the watched directory and key. Call the EnumRoutine for each such entry. Arguments: WatchHandle - handle returned by WatchStart. Context - context value to be passed to EnumRoutine. EnumRoutine - routine to call for each entry. Return Value: DWORD - Win32 status of the operation. --*/ { PROOT_ENTRY root; PWCH name; PCONTAINER_ENTRY rootContainer; PCONTAINER_ENTRY currentContainer; PCONTAINER_ENTRY container; POBJECT_ENTRY object; WATCH_ENTRY enumEntry; DWORD i; DWORD containerNameOffset; DWORD objectNameOffset; DWORD containerType; DWORD objectType; DWORD error; WCHAR currentPath[MAX_PATH + 1]; root = WatchHandle; // // Loop twice -- once for the watched directory and once for // the watched key. // for ( i = 0; i < 2; i++ ) { // // Set up for walking the appropriate tree. // if ( i == 0 ) { rootContainer = (PCONTAINER_ENTRY)root->RootDirectoryEntry; containerType = WATCH_DIRECTORY; objectType = WATCH_FILE; containerNameOffset = FIELD_OFFSET( DIRECTORY_ENTRY, Name ); objectNameOffset = FIELD_OFFSET( FILE_ENTRY, Name ); } else { rootContainer = (PCONTAINER_ENTRY)root->RootKeyEntry; containerType = WATCH_KEY; objectType = WATCH_VALUE; containerNameOffset = FIELD_OFFSET( KEY_ENTRY, Name ); objectNameOffset = FIELD_OFFSET( VALUE_ENTRY, Name ); } currentContainer = rootContainer; wcscpy( currentPath, (PWCH)((PCHAR)rootContainer + containerNameOffset) ); enumEntry.Name = currentPath; if ( wcslen(currentPath) == 0 ) { enumEntry.Name += 1; // skip leading backslash } do { // // Call the EnumRoutine for each object (file/value) in the // container (directory/key). All objects remaining in the // tree are either changed, new, or deleted. // object = GetFirstObject( currentContainer ); while ( object != NULL ) { enumEntry.EntryType = objectType; enumEntry.ChangeType = GetEntryState( object ); wcscat( currentPath, L"\\" ); wcscat( currentPath, (PWCH)((PCHAR)object + objectNameOffset) ); error = EnumRoutine( Context, &enumEntry ); if ( error != NO_ERROR ) { dprintf( 0, ("EnumRoutine returned %d\n", error) ); return error; } *wcsrchr(currentPath, L'\\') = 0; object = GetNextObject( currentContainer, object ); } // // If the current container has subcontainers, recurse // into the first one. // container = GetFirstContainer( currentContainer ); if ( container != NULL ) { currentContainer = container; wcscat( currentPath, L"\\" ); wcscat( currentPath, (PWCH)((PCHAR)currentContainer + containerNameOffset) ); } else { // // The container has no subcontainers. Walk back up the // tree looking for a sibling container to process. // while ( TRUE ) { // // If the current container is the root container, we're done. // if ( currentContainer == rootContainer ) { currentContainer = NULL; break; } // // If the current container is new or deleted, call // the EnumRoutine. // if ( GetEntryState(currentContainer) != WATCH_MATCHED ) { enumEntry.EntryType = containerType; enumEntry.ChangeType = GetEntryState( currentContainer ); error = EnumRoutine( Context, &enumEntry ); if ( error != NO_ERROR ) { dprintf( 0, ("EnumRoutine returned %d\n", error) ); return error; } } // // Strip the name of the current container off of the path. // *wcsrchr(currentPath, L'\\') = 0; // // If the parent container has more subcontainers, recurse // into the next one. Otherwise, move up to the parent // container and try again. // container = GetNextContainer( currentContainer ); if ( container != NULL ) { currentContainer = container; wcscat( currentPath, L"\\" ); wcscat( currentPath, (PWCH)((PCHAR)currentContainer + containerNameOffset) ); break; } else { currentContainer = GetParent( currentContainer ); } } } } while ( currentContainer != NULL ); } // for return NO_ERROR; } // WatchEnum VOID WatchFree ( IN PVOID WatchHandle ) /*++ Routine Description: Frees the watch data structures. Arguments: WatchHandle - supplies the handle returned by WatchStart. Return Value: DWORD - Win32 status of the operation. --*/ { PROOT_ENTRY root; root = WatchHandle; // // Free the directory tree, and key tree, and the root entry. // WatchFreeChildren( (PCONTAINER_ENTRY)root->RootDirectoryEntry ); WatchFreeChildren( (PCONTAINER_ENTRY)root->RootKeyEntry ); MyFree( root ); DumpMallocStats( "After WatchFree" ); return; } // WatchFree VOID WatchFreeChildren ( IN PCONTAINER_ENTRY RootContainer ) /*++ Routine Description: Frees the children of a container (directory or key). Note that the container itself is not freed. Arguments: RootContainer - the container whose children are to be freed. Return Value: None. --*/ { PCONTAINER_ENTRY currentContainer; PCONTAINER_ENTRY container; PCONTAINER_ENTRY parent; POBJECT_ENTRY object; #if WATCH_DEBUG WCHAR currentPath[MAX_PATH + 1]; #endif DEBUG( wcscpy( currentPath, CONTAINER_NAME(RootContainer) ) ); // // Delete children starting at the root container. // currentContainer = RootContainer; do { // // Delete all objects (files or values) within the container. // object = GetFirstObject( currentContainer ); while ( object != NULL ) { dprintf( 2, ("Deleting entry for object %ws\\%ws: %s\n", currentPath, OBJECT_NAME(currentContainer,object), States[GetEntryState(object)]) ); RemoveObject( object ); MyFree( object ); object = GetFirstObject( currentContainer ); } // // If the container has subcontainers, recurse into the first one. // container = GetFirstContainer( currentContainer ); if ( container != NULL ) { currentContainer = container; DEBUG( wcscat( currentPath, L"\\" ) ); DEBUG( wcscat( currentPath, CONTAINER_NAME(currentContainer) ) ); } else { // // The container has no subcontainers. Walk back up the // tree looking for a sibling container to process. // while ( TRUE ) { // // If the current container is the root container, we're done. // if ( currentContainer == RootContainer ) { currentContainer = NULL; break; } DEBUG( dprintf( 2, ("Deleting entry for container %ws: %s\n", currentPath, States[GetEntryState(currentContainer)]) ) ); DEBUG( *wcsrchr(currentPath, L'\\') = 0 ); // // Free the current container. // parent = GetParent( currentContainer ); RemoveContainer( currentContainer ); MyFree( currentContainer ); // // If the parent container has more subcontainers, // recurse into the first one. Otherwise, move up // to the parent container and loop back to free it. // currentContainer = GetFirstContainer( parent ); if ( currentContainer != NULL ) { DEBUG( wcscat( currentPath, L"\\" ) ); DEBUG( wcscat( currentPath, CONTAINER_NAME(currentContainer) ) ); break; } else { currentContainer = parent; } } } } while ( currentContainer != NULL ); return; } // WatchFreeChildren DWORD WatchDirStart ( IN PROOT_ENTRY Root ) /*++ Routine Description: Starts watching the current user's profile directory. Captures the initial state of the directory tree. Arguments: Root - pointer to the ROOT_ENTRY allocated by WatchStart. Return Value: DWORD - Win32 status of the operation. --*/ { PDIRECTORY_ENTRY rootDirectory; PDIRECTORY_ENTRY currentDirectory; PDIRECTORY_ENTRY newDirectory; PFILE_ENTRY newFile; WIN32_FIND_DATA fileData; HANDLE findHandle; DWORD error; BOOL ok; WCHAR currentPath[MAX_PATH + 1]; // // Get the address of the root directory entry. // rootDirectory = Root->RootDirectoryEntry; currentDirectory = rootDirectory; // // Get the full path to the current user's profile directory. // ok = GetEnvironmentVariable( USERPROFILE, currentPath, MAX_PATH ); if ( !ok ) { return GetLastError(); } wcscat( currentPath, PROGRAMS ); DEBUG( if ( wcslen( rootDirectory->Name ) != 0 ) { wcscat( currentPath, TEXT("\\") ); wcscat( currentPath, rootDirectory->Name ); } ) do { // // Look for files/directories in the current directory. // wcscat( currentPath, L"\\*" ); dprintf( 2, ("FindFirst for %ws\n", currentPath) ); findHandle = FindFirstFile( currentPath, &fileData ); currentPath[wcslen(currentPath) - 2] = 0; if ( findHandle != INVALID_HANDLE_VALUE ) { do { if ( FlagOff(fileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) ) { // // The entry returned is for a file. Add it to the tree, // capturing the file's LastWriteTime. // dprintf( 2, (" found file %ws\\%ws\n", currentPath, fileData.cFileName) ); newFile = MyMalloc( sizeof(FILE_ENTRY) - sizeof(WCHAR) + ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR)) ); if ( newFile == NULL ) { FindClose( findHandle ); return ERROR_NOT_ENOUGH_MEMORY; } InitializeObject( newFile, 0 ); wcscpy( newFile->Name, fileData.cFileName ); newFile->LastWriteTime = fileData.ftLastWriteTime; InsertObject( currentDirectory, newFile ); } else if ((wcscmp(fileData.cFileName,L".") != 0) && (wcscmp(fileData.cFileName,L"..") != 0)) { // // The entry returned is for a directory. Add it to the tree. // dprintf( 2, (" found directory %ws\\%ws\n", currentPath, fileData.cFileName) ); newDirectory = MyMalloc( sizeof(DIRECTORY_ENTRY) - sizeof(WCHAR) + ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR)) ); if ( newDirectory == NULL ) { FindClose( findHandle ); return ERROR_NOT_ENOUGH_MEMORY; } InitializeContainer( newDirectory, 0, currentDirectory, TRUE ); wcscpy( newDirectory->Name, fileData.cFileName ); InsertContainer( currentDirectory, newDirectory ); } // // Find another entry in the directory. // ok = FindNextFile( findHandle, &fileData ); } while ( ok ); // // All entries found. Close the find handle. // FindClose( findHandle ); } // findHandle != INVALID_HANDLE_VALUE // // If the current directory has subdirectories, recurse into the // first one. // newDirectory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory ); if ( newDirectory != NULL ) { currentDirectory = newDirectory; wcscat( currentPath, L"\\" ); wcscat( currentPath, currentDirectory->Name ); } else { // // The directory has no subdirectories. Walk back up the // tree looking for a sibling directory to process. // while ( TRUE ) { // // If the current directory is the root directory, we're done. // if ( currentDirectory == rootDirectory ) { currentDirectory = NULL; break; } // // Strip the name of the current directory off of the path. // *wcsrchr(currentPath, L'\\') = 0; // // If the parent directory has more subdirectories, // recurse into the next one. Otherwise, move up // to the parent directory and try again. // newDirectory = (PDIRECTORY_ENTRY)GetNextContainer( currentDirectory ); if ( newDirectory != NULL ) { currentDirectory = newDirectory; wcscat( currentPath, L"\\" ); wcscat( currentPath, currentDirectory->Name ); break; } else { currentDirectory = (PDIRECTORY_ENTRY)GetParent( currentDirectory ); } } } } while ( currentDirectory != NULL ); return NO_ERROR; } // WatchDirStart DWORD WatchDirStop ( IN PROOT_ENTRY Root ) /*++ Routine Description: Stops watching the current user's profile directory. Captures the differences between the initial state and the current state. Arguments: Root - pointer to the ROOT_ENTRY allocated by WatchStart. Return Value: DWORD - Win32 status of the operation. --*/ { PDIRECTORY_ENTRY rootDirectory; PDIRECTORY_ENTRY currentDirectory; PDIRECTORY_ENTRY directory; PFILE_ENTRY file; WIN32_FIND_DATA fileData; HANDLE findHandle; DWORD error; BOOL ok; WCHAR currentPath[MAX_PATH + 1]; // // Get the address of the root directory entry. // rootDirectory = Root->RootDirectoryEntry; currentDirectory = rootDirectory; // // Get the full path to the current user's directory. // ok = GetEnvironmentVariable( USERPROFILE, currentPath, MAX_PATH ); if ( !ok ) { return GetLastError(); } wcscat( currentPath, PROGRAMS ); DEBUG( if ( wcslen( rootDirectory->Name ) != 0 ) { wcscat( currentPath, TEXT("\\") ); wcscat( currentPath, rootDirectory->Name ); } ) do { // // Look for files/directories in the current directory. // wcscat( currentPath, L"\\*" ); dprintf( 2, ("FindFirst for %ws\n", currentPath) ); findHandle = FindFirstFile( currentPath, &fileData ); currentPath[wcslen(currentPath) - 2] = 0; if ( findHandle != INVALID_HANDLE_VALUE ) { do { if ( FlagOff(fileData.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) ) { // // The entry returned is for a file. Check to see if // this file existed at the start. // dprintf( 2, (" found file %ws\\%ws\n", currentPath, fileData.cFileName) ); ok = FALSE; file = (PFILE_ENTRY)GetFirstObject( currentDirectory ); while ( file != NULL ) { if ( _wcsicmp( file->Name, fileData.cFileName ) == 0 ) { ok = TRUE; break; } file = (PFILE_ENTRY)GetNextObject( currentDirectory, file ); } if ( ok ) { // // The file existed at the start. If its LastWriteTime // hasn't changed, remove it from the watch tree. // Otherwise, mark it as changed. // if ( TIMES_EQUAL( file->LastWriteTime, fileData.ftLastWriteTime ) ) { dprintf( 2, (" Deleting entry for unchanged file %ws\\%ws\n", currentPath, file->Name) ); RemoveObject( file ); MyFree( file ); } else { dprintf( 1, (" Marking entry for changed file %ws\\%ws\n", currentPath, file->Name) ); SetEntryState( file, WATCH_CHANGED ); } } else { // // The file is new. Add it to the tree. // file = MyMalloc( sizeof(FILE_ENTRY) - sizeof(WCHAR) + ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR)) ); if ( file == NULL ) { FindClose( findHandle ); return ERROR_NOT_ENOUGH_MEMORY; } InitializeObject( file, WATCH_NEW ); wcscpy( file->Name, fileData.cFileName ); dprintf( 1, (" Adding entry for new file %ws\\%ws\n", currentPath, file->Name) ); InsertObject( currentDirectory, file ); } } else if ((wcscmp(fileData.cFileName,L".") != 0) && (wcscmp(fileData.cFileName,L"..") != 0)) { // // The entry returned is for a directory. Check to see if // this directory existed at the start. // dprintf( 2, (" found directory %ws\\%ws\n", currentPath, fileData.cFileName) ); ok = FALSE; directory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory ); while ( directory != NULL ) { if ( _wcsicmp( directory->Name, fileData.cFileName ) == 0 ) { ok = TRUE; break; } directory = (PDIRECTORY_ENTRY)GetNextContainer( directory ); } if ( ok ) { // // The directory existed at the start. Mark it as // matched. (We can't delete matched directories, // as we do files, because they need to be in the // tree for recursion.) // SetEntryState( directory, WATCH_MATCHED ); } else { // // The directory is new. Add it to the tree. // directory = MyMalloc( sizeof(DIRECTORY_ENTRY) - sizeof(WCHAR) + ((wcslen(fileData.cFileName) + 1) * sizeof(WCHAR)) ); if ( directory == NULL ) { FindClose( findHandle ); return ERROR_NOT_ENOUGH_MEMORY; } InitializeContainer( directory, WATCH_NEW, currentDirectory, TRUE ); wcscpy( directory->Name, fileData.cFileName ); dprintf( 1, (" Adding entry for new directory %ws\\%ws\n", currentPath, directory->Name) ); InsertContainer( currentDirectory, directory ); } } // // Find another entry in the directory. // ok = FindNextFile( findHandle, &fileData ); } while ( ok ); // // All entries found. Close the find handle. // FindClose( findHandle ); } // findHandle != INVALID_HANDLE_VALUE // // Any file entries in the current directory that were not removed // (because they were matched), marked as changed (because the // file time had changed), or added (for new files) represent files // that have been deleted. Mark them as such. // file = (PFILE_ENTRY)GetFirstObject( currentDirectory ); while ( file != NULL ) { if ( GetEntryState(file) == WATCH_NONE ) { dprintf( 1, (" Marking entry for deleted file %ws\\%ws\n", currentPath, file->Name) ); SetEntryState( file, WATCH_DELETED ); } file = (PFILE_ENTRY)GetNextObject( currentDirectory, file ); } // // Any subdirectory entries in the current directory that were not // marked as matched (directory still exists) or added (new directory) // represent directories that have been deleted. Mark them as such // and delete the entries for the their children -- we don't need // these entries any more. // directory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory ); while ( directory != NULL ) { if ( GetEntryState(directory) == WATCH_NONE ) { dprintf( 1, (" Marking entry for deleted directory %ws\\%ws\n", currentPath, directory->Name) ); SetEntryState( directory, WATCH_DELETED ); WatchFreeChildren( (PCONTAINER_ENTRY)directory ); } directory = (PDIRECTORY_ENTRY)GetNextContainer( directory ); } // // Find a subdirectory of the current directory that is marked as // matched. We don't need to walk the subtrees for new or deleted // directories. // directory = (PDIRECTORY_ENTRY)GetFirstContainer( currentDirectory ); while ( directory != NULL ) { if ( GetEntryState(directory) == WATCH_MATCHED ) { break; } directory = (PDIRECTORY_ENTRY)GetNextContainer( directory ); } // // If a matched subdirectory was found, recurse into it. // if ( directory != NULL ) { currentDirectory = directory; wcscat( currentPath, L"\\" ); wcscat( currentPath, currentDirectory->Name ); } else { // // The directory has no matched subdirectories. Walk back up the // tree looking for a sibling directory to process. // while ( TRUE ) { // // If the current directory is the root directory, we're done. // if ( currentDirectory == rootDirectory ) { currentDirectory = NULL; break; } // // Strip the name of the current directory off of the path. // *wcsrchr(currentPath, L'\\') = 0; // // If the parent directories has more matched subdirectories, // recurse into the next one. Otherwise, move up to the // parent directory and try again. // directory = (PDIRECTORY_ENTRY)GetNextContainer( currentDirectory ); while ( directory != NULL ) { if ( GetEntryState(directory) == WATCH_MATCHED ) { break; } directory = (PDIRECTORY_ENTRY)GetNextContainer( directory ); } if ( directory != NULL ) { currentDirectory = directory; wcscat( currentPath, L"\\" ); wcscat( currentPath, currentDirectory->Name ); break; } else { currentDirectory = (PDIRECTORY_ENTRY)GetParent( currentDirectory ); } } } } while ( currentDirectory != NULL ); return NO_ERROR; } // WatchDirStop DWORD WatchKeyStart ( IN PROOT_ENTRY Root ) /*++ Routine Description: Starts watching the current user key. Captures the initial state of the key tree. Arguments: Root - pointer to the ROOT_ENTRY allocated by WatchStart. Return Value: DWORD - Win32 status of the operation. --*/ { PKEY_ENTRY rootKey; PKEY_ENTRY currentKey; PKEY_ENTRY newKey; DWORD error; KEY_ENUM_CONTEXT context; #if WATCH_DEBUG WCHAR currentPath[MAX_PATH + 1]; #endif // // Get the address of the root key entry. // rootKey = Root->RootKeyEntry; currentKey = rootKey; DEBUG( wcscpy( currentPath, rootKey->Name ) ); do { // // Open the current key. If the current key is the root key, then // just use the HKEY_CURRENT_USER predefined key. Otherwise, open // the current key relative to the parent key. // if ( (currentKey == rootKey) #if WATCH_DEBUG && (wcslen(currentKey->Name) == 0) #endif ) { currentKey->Handle = HKEY_CURRENT_USER; } else { error = RegOpenKeyEx( #if WATCH_DEBUG currentKey == rootKey ? HKEY_CURRENT_USER : #endif ((PKEY_ENTRY)GetParent(currentKey))->Handle, currentKey->Name, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, ¤tKey->Handle ); if ( error != NO_ERROR ) { goto cleanup; } } // // Enumerate the values and subkeys of the key, adding entries // to the watch tree for each one. // context.ParentKey = currentKey; DEBUG( context.CurrentPath = currentPath ); error = EnumerateKey( currentKey->Handle, &context, AddValueAtStart, AddKeyAtStart ); if ( error != NO_ERROR ) { goto cleanup; } // // If the current key has subkeys, recurse into the first one. // newKey = (PKEY_ENTRY)GetFirstContainer( currentKey ); if ( newKey != NULL ) { currentKey = newKey; DEBUG( wcscat( currentPath, L"\\" ) ); DEBUG( wcscat( currentPath, currentKey->Name ) ); } else { // // The key has no subkeys. Walk back up the tree looking // for a sibling key to process. // while ( TRUE ) { // // Close the handle to the key. // if ( currentKey->Handle != HKEY_CURRENT_USER ) { RegCloseKey( currentKey->Handle ); } currentKey->Handle = NULL; // // If the current key is the root key, we're done. // if ( currentKey == rootKey ) { currentKey = NULL; break; } DEBUG( *wcsrchr(currentPath, L'\\') = 0 ); // // If the parent key has more subkeys, recurse into the next // one. Otherwise, move up to the parent key and try again. // newKey = (PKEY_ENTRY)GetNextContainer( currentKey ); if ( newKey != NULL ) { currentKey = newKey; DEBUG( wcscat( currentPath, L"\\" ) ); DEBUG( wcscat( currentPath, currentKey->Name ) ); break; } else { currentKey = (PKEY_ENTRY)GetParent( currentKey ); } } } } while ( currentKey != NULL ); return NO_ERROR; cleanup: // // Error cleanup. Walk back up the tree closing handles. // do { if ( (currentKey->Handle != NULL) && (currentKey->Handle != HKEY_CURRENT_USER) ) { RegCloseKey( currentKey->Handle ); } currentKey->Handle = NULL; currentKey = (PKEY_ENTRY)GetParent( currentKey ); } while ( currentKey != NULL ); return error; } // WatchKeyStart DWORD WatchKeyStop ( IN PROOT_ENTRY Root ) /*++ Routine Description: Stops watching the current user key. Captures the differences between the initial state and the current state. Arguments: Root - pointer to the ROOT_ENTRY allocated by WatchStart. Return Value: DWORD - Win32 status of the operation. --*/ { PKEY_ENTRY rootKey; PKEY_ENTRY currentKey; PKEY_ENTRY key; PVALUE_ENTRY value; DWORD error; KEY_ENUM_CONTEXT context; #if WATCH_DEBUG WCHAR currentPath[MAX_PATH + 1]; #endif // // Get the address of the root key entry. // rootKey = Root->RootKeyEntry; currentKey = rootKey; DEBUG( wcscpy( currentPath, rootKey->Name ) ); do { // // Open the current key. If the current key is the root key, then // just use the HKEY_CURRENT_USER predefined key. Otherwise, open // the current key relative to the parent key. // if ( (currentKey == rootKey) #if WATCH_DEBUG && (wcslen(currentKey->Name) == 0) #endif ) { currentKey->Handle = HKEY_CURRENT_USER; } else { error = RegOpenKeyEx( #if WATCH_DEBUG currentKey == rootKey ? HKEY_CURRENT_USER : #endif ((PKEY_ENTRY)GetParent(currentKey))->Handle, currentKey->Name, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, ¤tKey->Handle ); if ( error != NO_ERROR ) { goto cleanup; } } // // Enumerate the values and subkeys of the key, checking entries // in the watch tree for each one. // context.ParentKey = currentKey; DEBUG( context.CurrentPath = currentPath ); error = EnumerateKey( currentKey->Handle, &context, CheckValueAtStop, CheckKeyAtStop ); if ( error != NO_ERROR ) { goto cleanup; } // // Any value entries in the current key that were not removed // (because they were matched), marked as changed (because the // value data had changed), or added (for new values) represent // values that have been deleted. Mark them as such. // value = (PVALUE_ENTRY)GetFirstObject( currentKey ); while ( value != NULL ) { if ( GetEntryState(value) == WATCH_NONE ) { dprintf( 1, (" Marking entry for deleted value %ws\\%ws\n", currentPath, value->Name) ); SetEntryState( value, WATCH_DELETED ); } value = (PVALUE_ENTRY)GetNextObject( currentKey, value ); } // // Any subkey entries in the current key that were not marked as // matched (subkey still exists) or added (new subkey) represent // subkeys that have been deleted. Mark them as such and delete // the entries for the their children -- we don't need these // entries any more. // key = (PKEY_ENTRY)GetFirstContainer( currentKey ); while ( key != NULL ) { if ( GetEntryState(key) == WATCH_NONE ) { dprintf( 1, (" Marking entry for deleted key %ws\\%ws\n", currentPath, key->Name) ); SetEntryState( key, WATCH_DELETED ); WatchFreeChildren( (PCONTAINER_ENTRY)key ); } key = (PKEY_ENTRY)GetNextContainer( key ); } // // Find a subkey of the current directory that is marked as matched. // We don't need to walk the subtrees for new or deleted keys. // key = (PKEY_ENTRY)GetFirstContainer( currentKey ); while ( key != NULL ) { if ( GetEntryState(key) == WATCH_MATCHED ) { break; } key = (PKEY_ENTRY)GetNextContainer( key ); } // // If a matched subkey was found, recurse into it. // if ( key != NULL ) { currentKey = key; DEBUG( wcscat( currentPath, L"\\" ) ); DEBUG( wcscat( currentPath, currentKey->Name ) ); } else { // // The key has no matched subkeys. Walk back up the // tree looking for a sibling key to process. // while ( TRUE ) { // // Close the handle to the key. // if ( currentKey->Handle != HKEY_CURRENT_USER ) { RegCloseKey( currentKey->Handle ); } currentKey->Handle = NULL; // // If the current key is the root key, we're done. // if ( currentKey == rootKey ) { currentKey = NULL; break; } DEBUG( *wcsrchr(currentPath, L'\\') = 0 ); // // If the parent key has more matched subkeys, recurse // into the next one. Otherwise, move up to the parent // key and try again. // key = (PKEY_ENTRY)GetNextContainer( currentKey ); while ( key != NULL ) { if ( GetEntryState(key) == WATCH_MATCHED ) { break; } key = (PKEY_ENTRY)GetNextContainer( key ); } if ( key != NULL ) { currentKey = key; DEBUG( wcscat( currentPath, L"\\" ) ); DEBUG( wcscat( currentPath, currentKey->Name ) ); break; } else { currentKey = (PKEY_ENTRY)GetParent( currentKey ); } } } } while ( currentKey != NULL ); return NO_ERROR; cleanup: // // Error cleanup. Walk back up the tree closing handles. // do { if ( (currentKey->Handle != NULL) && (currentKey->Handle != HKEY_CURRENT_USER) ) { RegCloseKey( currentKey->Handle ); } currentKey->Handle = NULL; currentKey = (PKEY_ENTRY)GetParent( currentKey ); } while ( currentKey != NULL ); return error; } // WatchKeyStop DWORD EnumerateKey ( IN HKEY KeyHandle, IN PVOID Context, IN PVALUE_ENUM_ROUTINE ValueEnumRoutine OPTIONAL, IN PKEY_ENUM_ROUTINE KeyEnumRoutine OPTIONAL ) /*++ Routine Description: Enumerates the values and subkeys in a key. Calls an EnumRoutine for each value and subkey. Arguments: KeyHandle - handle to the key to be enumerated. Context - context value to be passed to EnumRoutine. ValueEnumRoutine - routine to call for each value. If omitted, values are not enumerated. KeyEnumRoutine - routine to call for each key. If omitted, keys are not enumerated. Return Value: DWORD - Win32 status of the operation. --*/ { DWORD error; DWORD keyCount; DWORD valueCount; DWORD i; DWORD type; DWORD nameLength; DWORD maxKeyNameLength; DWORD maxValueNameLength; DWORD dataLength; DWORD maxValueDataLength; PWCH nameBuffer; PVOID dataBuffer; FILETIME time; // // Query information about the key that is needed to query // its values and subkeys. // error = RegQueryInfoKey( KeyHandle, NULL, NULL, NULL, &keyCount, &maxKeyNameLength, NULL, &valueCount, &maxValueNameLength, &maxValueDataLength, NULL, NULL ); if ( error != NO_ERROR ) { return error; } if ( ValueEnumRoutine != NULL ) { // // Allocate a buffer large enough for the longest value name and // another buffer large enough for the longest value data. // nameBuffer = MyMalloc( (maxValueNameLength + 1) * sizeof(WCHAR) ); if ( nameBuffer == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } dataBuffer = MyMalloc( maxValueDataLength ); if ( dataBuffer == NULL ) { MyFree( nameBuffer ); return ERROR_NOT_ENOUGH_MEMORY; } // // Query the key's values. // for ( i = 0; i < valueCount; i++ ) { nameLength = maxValueNameLength + 1; dataLength = maxValueDataLength; error = RegEnumValue( KeyHandle, i, nameBuffer, &nameLength, NULL, &type, dataBuffer, &dataLength ); if ( error != NO_ERROR ) { MyFree( dataBuffer ); MyFree( nameBuffer ); return error; } // // Call the EnumRoutine. // error = ValueEnumRoutine( Context, nameLength, nameBuffer, type, dataBuffer, dataLength ); if ( error != NO_ERROR ) { MyFree( dataBuffer ); MyFree( nameBuffer ); return error; } } // // Free the value data and value name buffers. // MyFree( dataBuffer ); dataBuffer = NULL; MyFree( nameBuffer ); } if ( KeyEnumRoutine != NULL) { // // Allocate a buffer large enough for the longest subkey name. // nameBuffer = MyMalloc( (maxKeyNameLength + 1) * sizeof(WCHAR) ); if ( nameBuffer == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } // // Query the key's subkeys. // for ( i = 0; i < keyCount; i++ ) { nameLength = maxKeyNameLength + 1; error = RegEnumKeyEx( KeyHandle, i, nameBuffer, &nameLength, NULL, NULL, NULL, &time ); if ( error != NO_ERROR ) { MyFree( nameBuffer ); return error; } // // Call the EnumRoutine. // error = KeyEnumRoutine( Context, nameLength, nameBuffer ); if ( error != NO_ERROR ) { MyFree( nameBuffer ); return error; } } // // Free the key name buffer. // MyFree( nameBuffer ); } return NO_ERROR; } // EnumerateKey DWORD AddValueAtStart ( IN PVOID Context, IN DWORD ValueNameLength, IN PWCH ValueName, IN DWORD ValueType, IN PVOID ValueData, IN DWORD ValueDataLength ) /*++ Routine Description: Adds a value entry to the watch tree during WatchKeyStart. Arguments: Context - context value passed to EnumerateKey. ValueNameLength - length in characters of ValueName. ValueName - pointer to name of the value. ValueType - type of the value data. ValueData - pointer to value data. ValueDataLength - length in bytes of ValueData. Return Value: DWORD - Win32 status of the operation. --*/ { PKEY_ENUM_CONTEXT context = Context; PVALUE_ENTRY newValue; // // Add the value to the tree, capturing the value data. // dprintf( 2, (" found value %ws\\%ws\n", context->CurrentPath, ValueName) ); newValue = MyMalloc( sizeof(VALUE_ENTRY) - sizeof(WCHAR) + ((ValueNameLength + 1) * sizeof(WCHAR)) + ValueDataLength ); if ( newValue == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } InitializeObject( newValue, 0 ); wcscpy( newValue->Name, ValueName ); newValue->Type = ValueType; newValue->NameLength = ValueNameLength; newValue->ValueDataLength = ValueDataLength; memcpy( &newValue->Name + ValueNameLength + 1, ValueData, ValueDataLength ); InsertObject( context->ParentKey, newValue ); return NO_ERROR; } // AddValueAtStart DWORD AddKeyAtStart ( IN PVOID Context, IN DWORD KeyNameLength, IN PWCH KeyName ) /*++ Routine Description: Adds a key entry to the watch tree during WatchKeyStart. Arguments: Context - context value passed to EnumerateKey. KeyNameLength - length in characters of KeyName. KeyName - pointer to name of the key. Return Value: DWORD - Win32 status of the operation. --*/ { PKEY_ENUM_CONTEXT context = Context; PKEY_ENTRY newKey; // // Add the key to the tree. // dprintf( 2, (" found key %ws\\%ws\n", context->CurrentPath, KeyName) ); newKey = MyMalloc( sizeof(KEY_ENTRY) - sizeof(WCHAR) + ((KeyNameLength + 1) * sizeof(WCHAR)) ); if ( newKey == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } InitializeContainer( newKey, 0, context->ParentKey, FALSE ); wcscpy( newKey->Name, KeyName ); newKey->Handle = NULL; InsertContainer( context->ParentKey, newKey ); return NO_ERROR; } // AddKeyAtStart DWORD CheckValueAtStop ( IN PVOID Context, IN DWORD ValueNameLength, IN PWCH ValueName, IN DWORD ValueType, IN PVOID ValueData, IN DWORD ValueDataLength ) /*++ Routine Description: Checks the watch tree for an enumerated value during WatchKeyStop. Arguments: Context - context value passed to EnumerateKey. ValueNameLength - length in characters of ValueName. ValueName - pointer to name of the value. ValueType - type of the value data. ValueData - pointer to value data. ValueDataLength - length in bytes of ValueData. Return Value: DWORD - Win32 status of the operation. --*/ { PKEY_ENUM_CONTEXT context = Context; PVALUE_ENTRY value; BOOL ok; // // Check to see if the value existed at the start. // dprintf( 2, (" found value %ws\\%ws\n", context->CurrentPath, ValueName) ); ok = FALSE; value = (PVALUE_ENTRY)GetFirstObject( context->ParentKey ); while ( value != NULL ) { if ( _wcsicmp( value->Name, ValueName ) == 0 ) { ok = TRUE; break; } value = (PVALUE_ENTRY)GetNextObject( context->ParentKey, value ); } if ( ok ) { // // The value existed at the start. If its data hasn't changed, // remove it from the tree. Otherwise, mark it as changed. // if ( (value->Type == ValueType) && (value->ValueDataLength == ValueDataLength) && (memcmp( &value->Name + value->NameLength + 1, ValueData, ValueDataLength ) == 0) ) { dprintf( 2, ("Deleting entry for unchanged value %ws\\%ws\n", context->CurrentPath, ValueName) ); RemoveObject( value ); MyFree( value ); } else { dprintf( 1, (" Marking entry for changed value %ws\\%ws\n", context->CurrentPath, ValueName) ); SetEntryState( value, WATCH_CHANGED ); } } else { // // The value is new. Add it to the tree. // // Note that we do not bother to save the value's data here, // even though we have it in hand. The routines that // populate userdifr already have to deal with querying // value data, so the code is simpler this way. // value = MyMalloc( sizeof(VALUE_ENTRY) - sizeof(WCHAR) + ((ValueNameLength + 1) * sizeof(WCHAR)) ); if ( value == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } InitializeObject( value, WATCH_NEW ); wcscpy( value->Name, ValueName ); dprintf( 1, (" Adding entry for new value %ws\\%ws\n", context->CurrentPath, ValueName) ); InsertObject( context->ParentKey, value ); } return NO_ERROR; } // CheckValueAtStop DWORD CheckKeyAtStop ( IN PVOID Context, IN DWORD KeyNameLength, IN PWCH KeyName ) /*++ Routine Description: Checks the watch tree for an enumerated key during WatchKeyStop. Arguments: Context - context value passed to EnumerateKey. KeyNameLength - length in characters of KeyName. KeyName - pointer to name of the key. Return Value: DWORD - Win32 status of the operation. --*/ { PKEY_ENUM_CONTEXT context = Context; PKEY_ENTRY key; BOOL ok; // // Check to see if the subkey existed at the start. // dprintf( 2, (" found key %ws\\%ws\n", context->CurrentPath, KeyName) ); ok = FALSE; key = (PKEY_ENTRY)GetFirstContainer( context->ParentKey ); while ( key != NULL ) { if ( _wcsicmp( key->Name, KeyName ) == 0 ) { ok = TRUE; break; } key = (PKEY_ENTRY)GetNextContainer( key ); } if ( ok ) { // // The key existed at the start. Mark it as matched. // (We can't delete matched keys, as we do values, // because they need to be in the tree for recursion.) // SetEntryState( key, WATCH_MATCHED ); } else { // // The key is new. Add it to the tree. // key = MyMalloc( sizeof(KEY_ENTRY) - sizeof(WCHAR) + ((KeyNameLength + 1) * sizeof(WCHAR)) ); if ( key == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } InitializeContainer( key, WATCH_NEW, context->ParentKey, FALSE ); wcscpy( key->Name, KeyName ); dprintf( 1, (" Adding entry for new key %ws\\%ws\n", context->CurrentPath, KeyName) ); InsertContainer( context->ParentKey, key ); } return NO_ERROR; } // CheckKeyAtStop // // Debug code for tracking allocates and frees. // #if WATCH_DEBUG #undef MyMalloc #undef MyFree DWORD TotalAllocs = 0; DWORD TotalFrees = 0; DWORD PeakAllocs = 0; DWORD TotalAllocated = 0; DWORD TotalFreed = 0; DWORD PeakAllocated = 0; PVOID MyMallocEx ( DWORD Size ) { PVOID p = MyMalloc( Size + 8 ); if ( p == NULL ) { dprintf( 0, ("MyMallocEx: failure allocating %d bytes\n", Size) ); DumpMallocStats(""); DbgBreakPoint(); return NULL; } TotalAllocs++; if ( (TotalAllocs - TotalFrees) > PeakAllocs ) { PeakAllocs = TotalAllocs - TotalFrees; } TotalAllocated += Size; if ( (TotalAllocated - TotalFreed) > PeakAllocated ) { PeakAllocated = TotalAllocated - TotalFreed; } *(PDWORD)p = Size; return (PVOID)((PCHAR)p + 8); } VOID MyFreeEx ( PVOID p ) { PVOID pp = (PVOID)((PCHAR)p - 8); TotalFrees++; TotalFreed += *(PDWORD)pp; MyFree( pp ); } VOID DumpMallocStats ( PSZ Event ) { if ( *Event != 0 ) { dprintf( 0, ("%s\n", Event) ); } dprintf( 0, ("Allocations %d, frees %d, active allocs %d\n", TotalAllocs, TotalFrees, TotalAllocs - TotalFrees) ); dprintf( 0, ("Bytes allocated %d, bytes freed %d, active bytes %d\n", TotalAllocated, TotalFreed, TotalAllocated - TotalFreed) ); dprintf( 0, ("Peak allocs %d, peak bytes %d\n", PeakAllocs, PeakAllocated) ); return; } #endif