628 lines
14 KiB
C
628 lines
14 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1996 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
dmnotify.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Contains notification support for the Configuration Database Manager
|
|||
|
|
|||
|
Each call to DmNotifyChangeKey adds a leaf to the notification tree. This
|
|||
|
tree is expected to be sparse, so each node is implemented as a linked list of
|
|||
|
subnodes and a linked list of leaves.
|
|||
|
|
|||
|
When a registry modification occurs, the tree is traversed from the root
|
|||
|
to the leaf representing the key. Any leaves along the path are candidates
|
|||
|
for reporting a notification event.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
John Vert (jvert) 9/18/1996
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
#include "dmp.h"
|
|||
|
|
|||
|
typedef struct _DM_NOTIFY_BRANCH {
|
|||
|
LIST_ENTRY SiblingList; // Links onto parent's ChildList.
|
|||
|
LIST_ENTRY ChildList; // Links onto child's SiblingList.
|
|||
|
LIST_ENTRY LeafList; // Links
|
|||
|
struct _DM_NOTIFY_BRANCH *Parent; // Parent
|
|||
|
USHORT NameLength;
|
|||
|
WCHAR KeyName[0]; // Name component (a single keyname, not a path)
|
|||
|
} DM_NOTIFY_BRANCH, *PDM_NOTIFY_BRANCH;
|
|||
|
|
|||
|
typedef struct _DM_NOTIFY_LEAF {
|
|||
|
LIST_ENTRY SiblingList; // Links onto parent branch's ChildList
|
|||
|
LIST_ENTRY KeyList; // Links onto DMKEY.NotifyList
|
|||
|
LIST_ENTRY RundownList; // Passed into DmNotifyChangeKey, used for rundown
|
|||
|
HDMKEY hKey;
|
|||
|
DWORD CompletionFilter;
|
|||
|
DM_NOTIFY_CALLBACK NotifyCallback;
|
|||
|
DWORD_PTR Context1;
|
|||
|
DWORD_PTR Context2;
|
|||
|
PDM_NOTIFY_BRANCH Parent;
|
|||
|
BOOL WatchTree;
|
|||
|
} DM_NOTIFY_LEAF, *PDM_NOTIFY_LEAF;
|
|||
|
|
|||
|
CRITICAL_SECTION NotifyLock;
|
|||
|
PDM_NOTIFY_BRANCH NotifyRoot=NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Local function prototypes
|
|||
|
//
|
|||
|
VOID
|
|||
|
DmpPruneBranch(
|
|||
|
IN PDM_NOTIFY_BRANCH Branch
|
|||
|
);
|
|||
|
|
|||
|
PDM_NOTIFY_BRANCH
|
|||
|
DmpFindKeyInBranch(
|
|||
|
IN PDM_NOTIFY_BRANCH RootBranch,
|
|||
|
IN OUT LPCWSTR *RelativeName,
|
|||
|
OUT WORD *pNameLength
|
|||
|
);
|
|||
|
|
|||
|
DWORD
|
|||
|
DmpAddNotifyLeaf(
|
|||
|
IN PDM_NOTIFY_BRANCH RootBranch,
|
|||
|
IN PDM_NOTIFY_LEAF NewLeaf,
|
|||
|
IN LPCWSTR RelativeName
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
DmpReportNotifyWorker(
|
|||
|
IN PDM_NOTIFY_BRANCH RootBranch,
|
|||
|
IN LPCWSTR RelativeName,
|
|||
|
IN LPCWSTR FullName,
|
|||
|
IN DWORD Filter
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
BOOL
|
|||
|
DmpInitNotify(
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Initializes the notification package for the DM.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE if successful
|
|||
|
|
|||
|
FALSE otherwise
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
InitializeCriticalSection(&NotifyLock);
|
|||
|
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
DmNotifyChangeKey(
|
|||
|
IN HDMKEY hKey,
|
|||
|
IN DWORD CompletionFilter,
|
|||
|
IN BOOL WatchTree,
|
|||
|
IN OPTIONAL PLIST_ENTRY ListHead,
|
|||
|
IN DM_NOTIFY_CALLBACK NotifyCallback,
|
|||
|
IN DWORD_PTR Context1,
|
|||
|
IN DWORD_PTR Context2
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Registers a notification for a specific registry key. When the
|
|||
|
notification event occurs, ApiReportRegistryNotify will be called.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hKey - Supplies the registry key handle on which the notification
|
|||
|
should be posted.
|
|||
|
|
|||
|
CompletionFilter - Supplies the registry events which should trigger
|
|||
|
the notification.
|
|||
|
|
|||
|
WatchTree - Supplies whether or not changes to the children of the specified
|
|||
|
key should trigger the notification.
|
|||
|
|
|||
|
ListHead - If present, supplies the listhead that the new notification should be
|
|||
|
queued to. This listhead should be passed to DmRundownList.
|
|||
|
|
|||
|
NotifyCallback - Supplies the notification routine that should be called
|
|||
|
when the notification occurs.
|
|||
|
|
|||
|
Context1 - Supplies the first DWORD of context to be passed to ApiReportRegistryNotify
|
|||
|
|
|||
|
Context2 - Supplies the second DWORD of context to be passed to ApiReportRegistryNotify
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful.
|
|||
|
|
|||
|
Win32 error otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDMKEY Key;
|
|||
|
PDM_NOTIFY_LEAF Leaf;
|
|||
|
DWORD Status;
|
|||
|
|
|||
|
Key = (PDMKEY)hKey;
|
|||
|
|
|||
|
Leaf = LocalAlloc(LMEM_FIXED, sizeof(DM_NOTIFY_LEAF));
|
|||
|
if (Leaf == NULL) {
|
|||
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
}
|
|||
|
Leaf->hKey = hKey;
|
|||
|
Leaf->CompletionFilter = CompletionFilter;
|
|||
|
Leaf->WatchTree = WatchTree;
|
|||
|
Leaf->NotifyCallback = NotifyCallback;
|
|||
|
Leaf->Context1 = Context1;
|
|||
|
Leaf->Context2 = Context2;
|
|||
|
|
|||
|
EnterCriticalSection(&NotifyLock);
|
|||
|
|
|||
|
if (NotifyRoot == NULL) {
|
|||
|
//
|
|||
|
// Create notify root here.
|
|||
|
//
|
|||
|
NotifyRoot = LocalAlloc(LMEM_FIXED, sizeof(DM_NOTIFY_BRANCH));
|
|||
|
if (NotifyRoot == NULL) {
|
|||
|
LeaveCriticalSection(&NotifyLock);
|
|||
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
}
|
|||
|
InitializeListHead(&NotifyRoot->SiblingList);
|
|||
|
InitializeListHead(&NotifyRoot->ChildList);
|
|||
|
InitializeListHead(&NotifyRoot->LeafList);
|
|||
|
NotifyRoot->Parent = NULL;
|
|||
|
}
|
|||
|
|
|||
|
Status = DmpAddNotifyLeaf(NotifyRoot, Leaf, Key->Name);
|
|||
|
if (Status == ERROR_SUCCESS) {
|
|||
|
InsertHeadList(&Key->NotifyList, &Leaf->KeyList);
|
|||
|
if (ARGUMENT_PRESENT(ListHead)) {
|
|||
|
InsertHeadList(ListHead, &Leaf->RundownList);
|
|||
|
} else {
|
|||
|
Leaf->RundownList.Flink = NULL;
|
|||
|
Leaf->RundownList.Blink = NULL;
|
|||
|
}
|
|||
|
} else {
|
|||
|
LocalFree(Leaf);
|
|||
|
}
|
|||
|
|
|||
|
LeaveCriticalSection(&NotifyLock);
|
|||
|
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
DmRundownList(
|
|||
|
IN PLIST_ENTRY ListHead
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Runs down a list of leaves. Used by the API when the notify port
|
|||
|
is closed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ListHead - Supplies the head of the rundown list. This is the
|
|||
|
same listhead passed to DmNotifyChangeKey
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PLIST_ENTRY ListEntry;
|
|||
|
PDM_NOTIFY_LEAF Leaf;
|
|||
|
|
|||
|
//
|
|||
|
// Remove all outstanding DM_NOTIFY_LEAF structures from this list.
|
|||
|
//
|
|||
|
EnterCriticalSection(&NotifyLock);
|
|||
|
while (!IsListEmpty(ListHead)) {
|
|||
|
ListEntry = RemoveHeadList(ListHead);
|
|||
|
Leaf = CONTAINING_RECORD(ListEntry,
|
|||
|
DM_NOTIFY_LEAF,
|
|||
|
RundownList);
|
|||
|
RemoveEntryList(&Leaf->SiblingList);
|
|||
|
RemoveEntryList(&Leaf->KeyList);
|
|||
|
|
|||
|
//
|
|||
|
// Attempt to prune this branch.
|
|||
|
//
|
|||
|
DmpPruneBranch(Leaf->Parent);
|
|||
|
|
|||
|
LocalFree(Leaf);
|
|||
|
}
|
|||
|
|
|||
|
LeaveCriticalSection(&NotifyLock);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
DmpRundownNotify(
|
|||
|
IN PDMKEY Key
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Cleans up any outstanding notifications for a key when the
|
|||
|
key is being closed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Key - Supplies the key
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PLIST_ENTRY ListEntry;
|
|||
|
PDM_NOTIFY_LEAF Leaf;
|
|||
|
|
|||
|
//
|
|||
|
// Remove all outstanding DM_NOTIFY_LEAF structures from this key.
|
|||
|
//
|
|||
|
EnterCriticalSection(&NotifyLock);
|
|||
|
while (!IsListEmpty(&Key->NotifyList)) {
|
|||
|
ListEntry = RemoveHeadList(&Key->NotifyList);
|
|||
|
Leaf = CONTAINING_RECORD(ListEntry,
|
|||
|
DM_NOTIFY_LEAF,
|
|||
|
KeyList);
|
|||
|
RemoveEntryList(&Leaf->SiblingList);
|
|||
|
if (Leaf->RundownList.Flink != NULL) {
|
|||
|
RemoveEntryList(&Leaf->RundownList);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Attempt to prune this branch.
|
|||
|
//
|
|||
|
DmpPruneBranch(Leaf->Parent);
|
|||
|
|
|||
|
LocalFree(Leaf);
|
|||
|
}
|
|||
|
|
|||
|
LeaveCriticalSection(&NotifyLock);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
DmpPruneBranch(
|
|||
|
IN PDM_NOTIFY_BRANCH Branch
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Checks to see if a branch is empty and should be pruned (freed).
|
|||
|
If the branch is empty, this routine will recursively call itself
|
|||
|
on the parent until a non-empty branch is found.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Branch - Supplies the branch to be pruned.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
if ((IsListEmpty(&Branch->ChildList)) &&
|
|||
|
(IsListEmpty(&Branch->LeafList))) {
|
|||
|
|
|||
|
//
|
|||
|
// No need to keep this branch around any more. Remove
|
|||
|
// it from its parent, then check to see if the parent
|
|||
|
// should be pruned.
|
|||
|
//
|
|||
|
if (Branch->Parent == NULL) {
|
|||
|
//
|
|||
|
// This is the root, go ahead and free it up too.
|
|||
|
//
|
|||
|
CL_ASSERT(NotifyRoot == Branch);
|
|||
|
NotifyRoot = NULL;
|
|||
|
|
|||
|
} else {
|
|||
|
RemoveEntryList(&Branch->SiblingList);
|
|||
|
DmpPruneBranch(Branch->Parent);
|
|||
|
}
|
|||
|
LocalFree(Branch);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
DWORD
|
|||
|
DmpAddNotifyLeaf(
|
|||
|
IN PDM_NOTIFY_BRANCH RootBranch,
|
|||
|
IN PDM_NOTIFY_LEAF NewLeaf,
|
|||
|
IN LPCWSTR RelativeName
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Adds a leaf to the notification key.
|
|||
|
|
|||
|
If the RelativeName is empty, a leaf is created in RootBranch.
|
|||
|
|
|||
|
If the RelativeName is not empty, look up its first component
|
|||
|
in RootBranch. If it's not there, create it. Then call ourselves
|
|||
|
recursively after stripping off the first component of RelativeName
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
RootBranch - Supplies the root where the leaf is to be added
|
|||
|
|
|||
|
NewLeaf - Supplies the new leaf structure
|
|||
|
|
|||
|
RelativeName - Supplies the relative name.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
ERROR_SUCCESS if successful.
|
|||
|
|
|||
|
Win32 error code otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PLIST_ENTRY ListEntry;
|
|||
|
PDM_NOTIFY_BRANCH Branch;
|
|||
|
USHORT NameLength;
|
|||
|
LPCWSTR NextName;
|
|||
|
|
|||
|
if (RelativeName[0] == '\0') {
|
|||
|
InsertHeadList(&RootBranch->LeafList, &NewLeaf->SiblingList);
|
|||
|
NewLeaf->Parent = RootBranch;
|
|||
|
return(ERROR_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
NextName = RelativeName;
|
|||
|
Branch = DmpFindKeyInBranch(RootBranch, &NextName, &NameLength);
|
|||
|
if (Branch == NULL) {
|
|||
|
//
|
|||
|
// No branch existed with this name. Create a new branch.
|
|||
|
//
|
|||
|
Branch = LocalAlloc(LMEM_FIXED, sizeof(DM_NOTIFY_BRANCH) + NameLength*sizeof(WCHAR));
|
|||
|
if (Branch == NULL) {
|
|||
|
return(ERROR_NOT_ENOUGH_MEMORY);
|
|||
|
}
|
|||
|
InitializeListHead(&Branch->ChildList);
|
|||
|
InitializeListHead(&Branch->LeafList);
|
|||
|
Branch->Parent = RootBranch;
|
|||
|
Branch->NameLength = NameLength;
|
|||
|
CopyMemory(Branch->KeyName, RelativeName, NameLength*sizeof(WCHAR));
|
|||
|
InsertHeadList(&RootBranch->ChildList, &Branch->SiblingList);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call ourselves recursively on the new branch.
|
|||
|
//
|
|||
|
return(DmpAddNotifyLeaf(Branch, NewLeaf, NextName));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
DmpReportNotify(
|
|||
|
IN LPCWSTR KeyName,
|
|||
|
IN DWORD Filter
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Interface to the rest of DM to report a notification event on
|
|||
|
a particular key.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Key - Supplies the key that was modified.
|
|||
|
|
|||
|
Filter - Supplies the modification type.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
if (NotifyRoot == NULL) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
EnterCriticalSection(&NotifyLock);
|
|||
|
|
|||
|
if (NotifyRoot != NULL) {
|
|||
|
DmpReportNotifyWorker(NotifyRoot,
|
|||
|
KeyName,
|
|||
|
KeyName,
|
|||
|
Filter);
|
|||
|
}
|
|||
|
|
|||
|
LeaveCriticalSection(&NotifyLock);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
DmpReportNotifyWorker(
|
|||
|
IN PDM_NOTIFY_BRANCH RootBranch,
|
|||
|
IN LPCWSTR RelativeName,
|
|||
|
IN LPCWSTR FullName,
|
|||
|
IN DWORD Filter
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Recursive worker routine that drills down through the notification
|
|||
|
tree until it reaches the supplied name. Notifications are issued
|
|||
|
for any leaves along the path that match the event.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
RootBranch - Supplies the branch of the tree to start with.
|
|||
|
|
|||
|
RelativeName - Supplies the name of the changed key, relative to Branch.
|
|||
|
|
|||
|
FullName - Supplies the full name of the changed key.
|
|||
|
|
|||
|
Filter - Supplies the type of event.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PLIST_ENTRY ListEntry;
|
|||
|
PDM_NOTIFY_LEAF Leaf;
|
|||
|
PDM_NOTIFY_BRANCH Branch;
|
|||
|
LPCWSTR NextName;
|
|||
|
WORD Dummy;
|
|||
|
|
|||
|
//
|
|||
|
// First, issue notifies for any leaves at this node
|
|||
|
//
|
|||
|
ListEntry = RootBranch->LeafList.Flink;
|
|||
|
while (ListEntry != &RootBranch->LeafList) {
|
|||
|
Leaf = CONTAINING_RECORD(ListEntry,
|
|||
|
DM_NOTIFY_LEAF,
|
|||
|
SiblingList);
|
|||
|
if (Leaf->CompletionFilter & Filter) {
|
|||
|
if ( Leaf->WatchTree ||
|
|||
|
(RelativeName[0] == '\0')) {
|
|||
|
|
|||
|
(Leaf->NotifyCallback)(Leaf->Context1,
|
|||
|
Leaf->Context2,
|
|||
|
Filter,
|
|||
|
RelativeName);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
ListEntry = ListEntry->Flink;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now search the child list for a subkey that matches the next component
|
|||
|
// of the key name. If there isn't one, we are done. If there is one,
|
|||
|
// call ourselves recursively on it.
|
|||
|
//
|
|||
|
if (RelativeName[0] == '\0') {
|
|||
|
return;
|
|||
|
}
|
|||
|
NextName = RelativeName;
|
|||
|
Branch = DmpFindKeyInBranch(RootBranch, &NextName, &Dummy);
|
|||
|
if (Branch != NULL) {
|
|||
|
DmpReportNotifyWorker(Branch, NextName, FullName, Filter);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PDM_NOTIFY_BRANCH
|
|||
|
DmpFindKeyInBranch(
|
|||
|
IN PDM_NOTIFY_BRANCH RootBranch,
|
|||
|
IN OUT LPCWSTR *RelativeName,
|
|||
|
OUT WORD *pNameLength
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Finds the next component of a key name in a branch.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
RootBranch - Supplies the branch to search.
|
|||
|
|
|||
|
RelativeName - Supplies the relative name of the key.
|
|||
|
Returns the remaining name
|
|||
|
|
|||
|
NameLength - Returns the length of the next component.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Pointer to the found branch if successful.
|
|||
|
|
|||
|
NULL otherwise.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDM_NOTIFY_BRANCH Branch;
|
|||
|
USHORT NameLength;
|
|||
|
LPCWSTR NextName;
|
|||
|
PLIST_ENTRY ListEntry;
|
|||
|
|
|||
|
//
|
|||
|
// Find the first component of the relative name.
|
|||
|
//
|
|||
|
NextName = wcschr(*RelativeName, '\\');
|
|||
|
if (NextName==NULL) {
|
|||
|
NameLength = (USHORT)lstrlenW(*RelativeName);
|
|||
|
NextName = *RelativeName + NameLength;
|
|||
|
} else {
|
|||
|
NameLength = (USHORT)(NextName - *RelativeName);
|
|||
|
++NextName;
|
|||
|
}
|
|||
|
*pNameLength = NameLength;
|
|||
|
|
|||
|
//
|
|||
|
// Search through the root's children to try and find a match on the
|
|||
|
// first component.
|
|||
|
//
|
|||
|
ListEntry = RootBranch->ChildList.Flink;
|
|||
|
while (ListEntry != &RootBranch->ChildList) {
|
|||
|
Branch = CONTAINING_RECORD(ListEntry,
|
|||
|
DM_NOTIFY_BRANCH,
|
|||
|
SiblingList);
|
|||
|
if ((NameLength == Branch->NameLength) &&
|
|||
|
(wcsncmp(*RelativeName, Branch->KeyName, NameLength)==0)) {
|
|||
|
|
|||
|
//
|
|||
|
// We have matched an existing branch. Return success.
|
|||
|
//
|
|||
|
*RelativeName = NextName;
|
|||
|
return(Branch);
|
|||
|
}
|
|||
|
ListEntry = ListEntry->Flink;
|
|||
|
}
|
|||
|
*RelativeName = NextName;
|
|||
|
|
|||
|
return(NULL);
|
|||
|
|
|||
|
}
|