mirror of https://github.com/tongzx/nt5src
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.
2307 lines
61 KiB
2307 lines
61 KiB
/*++
|
|
|
|
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 = GetSpecialFolderPath ( CSIDL_PROGRAMS, currentPath );
|
|
if ( !ok ) {
|
|
return GetLastError();
|
|
}
|
|
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( (DWORD)(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( (DWORD)(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 = GetSpecialFolderPath ( CSIDL_PROGRAMS, currentPath );
|
|
if ( !ok ) {
|
|
return GetLastError();
|
|
}
|
|
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( (DWORD)(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( (DWORD)(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
|
|
|
|
|
|
typedef HRESULT (*PFNSHGETFOLDERPATH)(HWND hwnd, int csidl, HANDLE hToken, DWORD dwType, LPTSTR pszPath);
|
|
|
|
BOOL
|
|
GetSpecialFolderPath (
|
|
IN INT csidl,
|
|
IN LPWSTR lpPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Gets the path to the requested special folder.
|
|
(This function was copied from userenv.dll)
|
|
|
|
Arguments:
|
|
|
|
csid - CSIDL of the special folder
|
|
lpPath - Path to place result in assumed to be MAX_PATH in size
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful
|
|
FALSE if an error occurs
|
|
|
|
--*/
|
|
{
|
|
HRESULT hResult;
|
|
HINSTANCE hInstShell32;
|
|
PFNSHGETFOLDERPATH pfnSHGetFolderPath;
|
|
|
|
|
|
//
|
|
// Load the function we need
|
|
//
|
|
|
|
hInstShell32 = LoadLibrary(L"shell32.dll");
|
|
|
|
if (!hInstShell32) {
|
|
SetupDebugPrint1( L"SETUP: GetSpecialFolderPath() failed to load shell32. Error = %d.", GetLastError() );
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
pfnSHGetFolderPath = (PFNSHGETFOLDERPATH)GetProcAddress (hInstShell32, "SHGetFolderPathW");
|
|
|
|
if (!pfnSHGetFolderPath) {
|
|
SetupDebugPrint1( L"SETUP: GetSpecialFolderPath() failed to find SHGetFolderPath(). Error = %d.", GetLastError() );
|
|
FreeLibrary (hInstShell32);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// Ask the shell for the folder location
|
|
//
|
|
|
|
hResult = pfnSHGetFolderPath (
|
|
NULL,
|
|
csidl | CSIDL_FLAG_CREATE,
|
|
(HANDLE) -1, // this specifies .Default
|
|
0,
|
|
lpPath);
|
|
if (S_OK != hResult) {
|
|
SetupDebugPrint1( L"SETUP: GetSpecialFolderPath: SHGetFolderPath() returned %d.", hResult );
|
|
}
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
|
|
FreeLibrary (hInstShell32);
|
|
return (S_OK == hResult);
|
|
}
|
|
|