
/**
 * @file regevents.c
 *
 * Tento soubor obsahuje rutiny zajistujici vytvareni zaznamu k jednotlivym
 * typum registrovych operaci, jejich umisteni do hashovaci tabulky (v ramci
 * prenotifikaci) a jejich umisteni do seznamu probehlych operaci (v ramci
 * post notifikaci). Soubor take zajistuje obsluhu samotne hashovaci tabulky.
 */

#include <ntifs.h>
#include "driver.h"
#include "version.h"
#include "events.h"
#include "hash_table.h"
#include "regevents.h"


#define  REGetGeneralInfo(Event) \
   ((PREG_EVENT_GENERAL_INFO)((PCHAR)Event + sizeof(EVENT)))


typedef struct _REG_EVENT_HASH_KEY
{
  PVOID RegistryObject;
  HANDLE ThreadId;
  ULONG32 EventType;
} REG_EVENT_HASH_KEY, *PREG_EVENT_HASH_KEY;

// Uchovava jednotlive udalosti mezi Pre a Post operacemi
PHASH_TABLE EventHashTable = NULL;


/** Zahashuje klic na dany slot hashovaci tabulky.
 *
 *  @param Info Adresa zaznamu klice.
 *
 *  @return Funkce vrati hashovanou hodnotu klice.
 */
static ULONG32 EventHashTableHash(IN PVOID Info)
{
  ULONG32 Ret = 0;
  PREG_EVENT_HASH_KEY uinfo = Info;
  KdPrint(("reg_udaloct.c: EventHashTableHash(Info=0x%p)\n", Info));
	
  Ret = ((ULONG)uinfo->RegistryObject ^ ((ULONG)uinfo->ThreadId >> 4)) * uinfo->EventType;
	
  KdPrint(("regevents.c: EventHashTableHash(-):%u\n", Ret));
  return Ret;
}


/** Porovnavaci funkce pro hashovaci tabulku.
 */
static BOOLEAN EventHashTableCompare(IN PVOID Event, IN PVOID Info)
{
  BOOLEAN Ret = FALSE;
  PREG_EVENT_HASH_KEY uinfo = (PREG_EVENT_HASH_KEY)Info;
  PREG_EVENT_GENERAL_INFO oinfo = REGetGeneralInfo(Event);
  PEVENT uu = (PEVENT)Event;
  KdPrint(("regevents.c: EventHashTableCompare(Event=0x%p; Info=0x%p)\n", Event, Info));
  
  KdPrint(("regevents.c: EventHashTableCompare(): EventType: %u\n", uu->Type));
  Ret = (uinfo->EventType == uu->Type && uinfo->ThreadId == (HANDLE)oinfo->TID && uinfo->RegistryObject == (PVOID)oinfo->Object);
  
  KdPrint(("regevents.c: EventHashTableCompare(-):%u\n", Ret));
  return Ret;
}


/** Rutina volana na kazdy zbylou polozku pri mazani samotne hashovaci
 *  tabulky.
 */
static VOID EventHashTableDeleteItem(IN PVOID Event)
{
  KdPrint(("regevents.c: EventHashTableDeleteItem(Event=0x%p)\n", Event));
  
  EventFree((PEVENT)Event);
	
  KdPrint(("regevents.c: EventHashTableDeleteItem(-)\n"));
  return;
}
  

/** Zpracovani post otifikace pro vetsinu udalosti.
 *
 *  Rutina najde prislusny zaznam udalosti v hashovaci tabulce, vyjme jej
 *  odtud a vyplni jeho zbyvajici hodnoty. Nasledne tento zaznam prida do
 *  seznamu jiz probehlych udalosti, ze ktereho si je nasledne muze brat
 *  okolni svet.
 *
 *  @param OpType Typ operace.
 *  @param PostInfo Informace pridruzene k post notifikaci.
 *
 *  @return Funkce vraci hodnotu NTSTATUS indikujici uspech ci neuspech operace.
 */
NTSTATUS ParseGeneralPostEvent(REG_NOTIFY_CLASS OpType, PREG_POST_OPERATION_INFORMATION PostInfo)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PVOID Event = NULL;
  REG_EVENT_HASH_KEY uinfo;
  PREG_EVENT_GENERAL_INFO oinfo = NULL;
  KdPrint(("regevents.c: ParseGeneralPostEvent(PostInfo=%p)\n", PostInfo));

  uinfo.ThreadId = PsGetCurrentThreadId();
  uinfo.RegistryObject = PostInfo->Object;
  uinfo.EventType = 0xFFFFFFFF;
  switch (OpType) {
    case RegNtPostDeleteKey:             uinfo.EventType = REG_EVENT_DELETE_KEY;   break;
    case RegNtPostSetValueKey:           uinfo.EventType = REG_EVENT_SET_VALUE; break;
    case RegNtPostDeleteValueKey:        uinfo.EventType = REG_EVENT_DELETE_VALUE; break;
    case RegNtPostSetInformationKey:     uinfo.EventType = REG_EVENT_SET_KEY; break;
    case RegNtPostRenameKey:             uinfo.EventType = REG_EVENT_RENAME_KEY; break;
    case RegNtPostEnumerateKey:          uinfo.EventType = REG_EVENT_ENUMERATE_KEY; break;
    case RegNtPostEnumerateValueKey:     uinfo.EventType = REG_EVENT_ENUMERATE_VALUE; break;
    case RegNtPostQueryKey:              uinfo.EventType = REG_EVENT_QUERY_KEY; break;
    case RegNtPostQueryValueKey:         uinfo.EventType = REG_EVENT_QUERY_VALUE; break;
    case RegNtPostQueryMultipleValueKey: uinfo.EventType = REG_EVENT_QUERY_MULTIPLE_VALUES; break;
    case RegNtPostKeyHandleClose:        
      uinfo.EventType = REG_EVENT_CLOSE_KEY; 
      uinfo.RegistryObject = NULL;
      break;
    case RegNtPostFlushKey:              uinfo.EventType = REG_EVENT_FLUSH_KEY; break;
    case RegNtPostLoadKey:               uinfo.EventType = REG_EVENT_LOAD_HIVE; break;
    case RegNtPostUnLoadKey:             uinfo.EventType = REG_EVENT_UNLOAD_HIVE; break;
    case RegNtPostQueryKeySecurity:      uinfo.EventType = REG_EVENT_QUERY_SECURITY_KEY; break;
    case RegNtPostSetKeySecurity:        uinfo.EventType = REG_EVENT_SET_SECURITY_KEY; break;
    default: KdPrint(("regevents.c: ParseGeneralPostEvent(): Spatny typ udalosti!!\n")); break;
  }  

  Event = HashTableDelete(EventHashTable, &uinfo);
  if (Event != NULL) {
    oinfo = REGetGeneralInfo(Event);
    oinfo->ResultStatus = PostInfo->Status;
    EventAddToListLock((PEVENT)Event);
    Status = STATUS_SUCCESS;
  } else {
    KdPrint(("regevents.c: ParseGeneralPostEvent(): PRE-UDALOST NEBYLA NALEZENA!\n"));
    Status = STATUS_NOT_FOUND;
  }  

  KdPrint(("regevents.c: ParseGeneralPostEvent(-):0x%x\n", Status));
  return Status;
}


NTSTATUS ParsePostRestoreKeyEvent(PREG_RESTORE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PVOID Event = NULL;
  REG_EVENT_HASH_KEY uinfo;
  PREG_EVENT_GENERAL_INFO oinfo = NULL;
  KdPrint(("regevents.c: ParsePostRestoreKeyEvent(Info=%p)\n", Info));

  uinfo.ThreadId = PsGetCurrentThreadId();
  uinfo.RegistryObject = Info->Object;
  uinfo.EventType = REG_EVENT_RESTORE_KEY;
  Event = HashTableDelete(EventHashTable, &uinfo);
  if (Event != NULL) {
    EventAddToListLock((PEVENT)Event);
    Status = STATUS_SUCCESS;
  } else {
    KdPrint(("regevents.c: ParsePostRestoreKeyEvent(): PRE-UDALOST NEBYLA NALEZENA!\n"));
    Status = STATUS_NOT_FOUND;
  }
 
  KdPrint(("regevents.c: ParsePostRestoreKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParsePostSaveKeyEvent(PREG_SAVE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  REG_EVENT_HASH_KEY uinfo;
  PREG_EVENT_GENERAL_INFO oinfo = NULL;
  KdPrint(("regevents.c: ParsePostSaveKeyEvent(Info=%p)\n", Info));

  uinfo.ThreadId = PsGetCurrentThreadId();
  uinfo.RegistryObject = Info->Object;
  uinfo.EventType = REG_EVENT_SAVE_KEY;
  Event = (PEVENT)HashTableDelete(EventHashTable, &uinfo);
  if (Event != NULL) {
    EventAddToListLock(Event);
    Status = STATUS_SUCCESS;
  } else {
    KdPrint(("regevents.c: ParsePostSaveKeyEvent(): PRE-UDALOST NEBYLA NALEZENA!\n"));
    Status = STATUS_NOT_FOUND;
  }
 
  KdPrint(("regevents.c: ParsePostSaveKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParsePostReplaceKeyEvent(PREG_REPLACE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  REG_EVENT_HASH_KEY uinfo;
  PREG_EVENT_GENERAL_INFO oinfo = NULL;
  KdPrint(("regevents.c: ParsePostReplaceKeyEvent(Info=%p)\n", Info));

  uinfo.ThreadId = PsGetCurrentThreadId();
  uinfo.RegistryObject = Info->Object;
  uinfo.EventType = REG_EVENT_REPLACE_KEY;
  Event = (PEVENT)HashTableDelete(EventHashTable, &uinfo);
  if (Event != NULL) {
    EventAddToListLock(Event);
    Status = STATUS_SUCCESS;
  } else {
    KdPrint(("regevents.c: ParsePostReplaceKeyEvent(): PRE-UDALOST NEBYLA NALEZENA!\n"));
    Status = STATUS_NOT_FOUND;
  }
 
  KdPrint(("regevents.c: ParsePostReplaceKeyEvent(-):0x%x\n", Status));
  return Status;
}


NTSTATUS ParsePostCreateKeyEvent(PREG_POST_CREATE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  REG_EVENT_HASH_KEY uinfo;
  PREG_EVENT_GENERAL_INFO oinfo = NULL;
  KdPrint(("regevents.c: ParsePostCreateKeyEvent(Info=0x%p)\n", Info));

  uinfo.ThreadId = PsGetCurrentThreadId();
  uinfo.RegistryObject = NULL;
  uinfo.EventType = REG_EVENT_CREATE_KEY;
  Event = (PEVENT)HashTableDelete(EventHashTable, &uinfo);
  if (Event != NULL) {
    oinfo = REGetGeneralInfo(Event);
    oinfo->ResultStatus = Info->Status;
    EventAddToListLock(Event);
    Status = STATUS_SUCCESS;
  } else {
    KdPrint(("regevents.c: ParsePostCreateKeyEvent(): PRE-UDALOST NEBYLA NALEZENA!\n"));
    Status = STATUS_NOT_FOUND;
  }
 
  KdPrint(("regevents.c: ParsePostCreateKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParsePostCreateKeyExEvent(PREG_POST_OPERATION_INFORMATION  Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  REG_EVENT_HASH_KEY uinfo;
  PREG_EVENT_GENERAL_INFO oinfo = NULL;
  KdPrint(("regevents.c: ParsePostCreateKeyExEvent(Info=%p)\n", Info));

  uinfo.ThreadId = PsGetCurrentThreadId();
  uinfo.RegistryObject = NULL;
  uinfo.EventType = REG_EVENT_CREATE_KEY_EX;
  Event = (PEVENT)HashTableDelete(EventHashTable, &uinfo);
  if (Event != NULL) {
    oinfo = REGetGeneralInfo(Event);
    oinfo->ResultStatus = Info->Status;
    EventAddToListLock(Event);
    Status = STATUS_SUCCESS;
  } else {
    KdPrint(("regevents.c: ParsePostCreateKeyExEvent(): PRE-UDALOST NEBYLA NALEZENA!\n"));
    Status = STATUS_NOT_FOUND;
  }
 
  KdPrint(("regevents.c: ParsePostCreateKeyExEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParsePostOpenKeyEvent(PREG_POST_OPEN_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  REG_EVENT_HASH_KEY uinfo;
  PREG_EVENT_GENERAL_INFO oinfo = NULL;
  KdPrint(("regevents.c: ParsePostOpenKeyEvent(Info=%p)\n", Info));

  uinfo.ThreadId = PsGetCurrentThreadId();
  uinfo.RegistryObject = NULL;
  uinfo.EventType = REG_EVENT_OPEN_KEY;
  Event = (PEVENT)HashTableDelete(EventHashTable, &uinfo);
  if (Event != NULL) {
    oinfo = REGetGeneralInfo(Event);
    oinfo->ResultStatus = Info->Status;
    EventAddToListLock(Event);
    Status = STATUS_SUCCESS;
  } else {
    KdPrint(("regevents.c: ParsePostOpenKeyEvent(): PRE-UDALOST NEBYLA NALEZENA!\n"));
    Status = STATUS_NOT_FOUND;
  }  

  KdPrint(("regevents.c: ParsePostOpenKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParsePostOpenKeyExEvent(PREG_POST_OPERATION_INFORMATION  Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  REG_EVENT_HASH_KEY uinfo;
  PREG_EVENT_GENERAL_INFO oinfo = NULL;
  KdPrint(("regevents.c: ParsePostOpenKeyExEvent(Info=%p)\n", Info));

  uinfo.ThreadId = PsGetCurrentThreadId();
  uinfo.RegistryObject = NULL;
  uinfo.EventType = REG_EVENT_OPEN_KEY_EX;
  Event = (PEVENT)HashTableDelete(EventHashTable, &uinfo);
  if (Event != NULL) {
    oinfo = REGetGeneralInfo(Event);
    oinfo->ResultStatus = Info->Status;
    EventAddToListLock(Event);
    Status = STATUS_SUCCESS;
  } else {
    KdPrint(("regevents.c: ParsePostOpenKeyExEvent(): PRE-UDALOST NEBYLA NALEZENA!\n"));
    Status = STATUS_NOT_FOUND;
  }  

  KdPrint(("regevents.c: ParsePostOpenKeyExEvent(-):0x%x\n", Status));
  return Status;
}

static NTSTATUS GetObjectName(PVOID Object, PUNICODE_STRING NameString)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  POBJECT_NAME_INFORMATION oni = NULL;
  ULONG Length = 0;
  KdPrint(("regevents.c: GetObjectName(Object=0x%p; NameString=0x%p)\n", Object, NameString));

  Status = ObQueryNameString(Object, oni, Length, &Length);
  while (Status == STATUS_INFO_LENGTH_MISMATCH) {
    if (oni != NULL)
      ExFreePoolWithTag(oni, POOL_TAG);
	
    oni = ExAllocatePoolWithTag(PagedPool, Length, POOL_TAG);
    if (oni != NULL) {
      Status = ObQueryNameString(Object, oni, Length, &Length);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
  }
  
  if (NT_SUCCESS(Status)) {
    NameString->Length = oni->Name.Length;
    NameString->MaximumLength = NameString->Length;
    NameString->Buffer = ExAllocatePoolWithTag(PagedPool, NameString->Length + sizeof(WCHAR), POOL_TAG);
    if (NameString->Buffer != NULL) {
      RtlZeroMemory(NameString->Buffer, NameString->Length + sizeof(WCHAR));
      RtlCopyMemory(NameString->Buffer, oni->Name.Buffer, NameString->Length);
    } else {
      Status = STATUS_INSUFFICIENT_RESOURCES;
      RtlZeroMemory(NameString, sizeof(UNICODE_STRING));
    }
  }

  if (oni != NULL)  
    ExFreePoolWithTag(oni, POOL_TAG);

  KdPrint(("regevents.c: GetObjectName(-):0x%x\n", Status));
  return Status;
}

static PREG_EVENT_GENERAL_INFO REFillGeneralInfo(PEVENT Event, PVOID Object)
{
  PREG_EVENT_GENERAL_INFO oinfo = REGetGeneralInfo(Event);
  
  oinfo->PID = (ULONG64)PsGetCurrentProcessId();
  oinfo->TID = (ULONG64)PsGetCurrentThreadId();
  oinfo->Object = (ULONG64)Object;
  oinfo->ResultStatus = STATUS_UNSUCCESSFUL;
  
  return oinfo;
}

static NTSTATUS REAddToHashTable(PEVENT Event)
{
  REG_EVENT_HASH_KEY uinfo;
  PREG_EVENT_GENERAL_INFO oi = NULL;
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("regevents.c: REAddToHashTable(Event=0x%p)\n", Event));

  if (!WindowsXP() || Event->Type == REG_EVENT_CREATE_KEY || 
    Event->Type == REG_EVENT_OPEN_KEY) {
    oi = REGetGeneralInfo(Event);
    if (oi != NULL) {
      uinfo.RegistryObject = (PVOID)oi->Object;
      uinfo.ThreadId = PsGetCurrentThreadId();
      uinfo.EventType = Event->Type;
      Status = HashTableInsert(EventHashTable, Event, &uinfo);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
  } else {
    EventAddToList(Event);
    Status = STATUS_SUCCESS;
  }  

  KdPrint(("regevents.c: REAddToHashTable(-):0x%x\n", Status));
  return Status;
}


#define COPY_KEY_NAME(NameStart, RecordSize, Record) \
          NameStart = (PCHAR)Record + RecordSize; \
          RtlCopyMemory(NameStart, KeyName.Buffer, KeyName.Length);  \
          Record->KeyName.Length = KeyName.Length; \
          Record->KeyName.Offset = (ULONG32)((PCHAR)NameStart - (PCHAR)Record); \

#define COPY_VALUE_NAME(NameStart, Record) \
          NameStart = (PCHAR)NameStart + KeyName.Length; \
          RtlCopyMemory(NameStart, Info->ValueName->Buffer, Info->ValueName->Length); \
          Record->ValueName.Length = Info->ValueName->Length; \
          Record->ValueName.Offset = (ULONG32)((PCHAR)NameStart - (PCHAR)Record); \


NTSTATUS ParseDeleteKeyEvent(IN PREG_DELETE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PREG_EVENT_DELETE_KEY_INFO usk = NULL;
  KdPrint(("regevents.c: ParseDeleteKeyEvent(Info=0x%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_DELETE_KEY, sizeof(REG_EVENT_DELETE_KEY_INFO) + KeyName.MaximumLength);
    if (Event != NULL) {
      usk = (PREG_EVENT_DELETE_KEY_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_DELETE_KEY_INFO), usk);
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }
  
  KdPrint(("regevents.c: ParseDeleteKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseDeleteValueKeyEvent(IN PREG_DELETE_VALUE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PREG_EVENT_DELETE_VALUE_INFO ush = NULL;
  KdPrint(("regevents.c: ParseDeleteValueKeyEvent(Info=0x%p)\n", Info));  

  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_DELETE_VALUE, sizeof(REG_EVENT_DELETE_VALUE_INFO) + KeyName.MaximumLength + Info->ValueName->Length + 2*sizeof(WCHAR));
    if (Event != NULL) {
      ush = (PREG_EVENT_DELETE_VALUE_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_DELETE_VALUE_INFO), ush);
      COPY_VALUE_NAME(NameStart, ush);	  
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;

    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }

  KdPrint(("regevents.c: ParseDeleteValueKeyEvent(-):0x%x\n", Status));
  return Status;
}
 
NTSTATUS ParseSetValueKeyEvent(IN PREG_SET_VALUE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PVOID DataStart = NULL;
  PREG_EVENT_SET_VALUE_INFO uzh = NULL;
  KdPrint(("regevents.c: ParseSetValueKeyEvent(Info=%p)\n", Info));  

  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_SET_VALUE, sizeof(REG_EVENT_SET_VALUE_INFO) + KeyName.MaximumLength + Info->ValueName->Length + 2*sizeof(WCHAR) + Info->DataSize);
    if (Event != NULL) {
      uzh = (PREG_EVENT_SET_VALUE_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_SET_VALUE_INFO), uzh);
      COPY_VALUE_NAME(NameStart, uzh);
      uzh->DataType = Info->Type;
      uzh->DataSize = Info->DataSize;
      DataStart = (PCHAR)NameStart + Info->ValueName->Length;
      uzh->DataOffset = (ULONG64)DataStart - (ULONG64)uzh;
      RtlCopyMemory(DataStart, Info->Data, (SIZE_T)uzh->DataSize);
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;

    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }

  KdPrint(("regevents.c: ParseSetValueKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseSetInformationKeyEvent(IN PREG_SET_INFORMATION_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PVOID DataStart = NULL;
  PREG_EVENT_SET_KEY_INFO uzk = NULL;
  KdPrint(("regevents.c: ParseSetInformationKeyEvent(Info=0x%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_SET_KEY, sizeof(REG_EVENT_SET_KEY_INFO) + KeyName.MaximumLength + Info->KeySetInformationLength);
    if (Event != NULL) {
      uzk = (PREG_EVENT_SET_KEY_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_SET_KEY_INFO), uzk);
      uzk->DataType = Info->KeySetInformationClass;
      uzk->DataSize = Info->KeySetInformationLength;
      DataStart = (PCHAR)NameStart + KeyName.Length;
      uzk->DataOffset = (ULONG64)DataStart - (ULONG64)uzk;
      RtlCopyMemory(DataStart, Info->KeySetInformation, (SIZE_T)uzk->DataSize);
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }
  
  KdPrint(("regevents.c: ParseSetInformationKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseRenameKeyEvent(IN PREG_RENAME_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PREG_EVENT_RENAME_KEY_INFO upk = NULL;
  KdPrint(("regevents.c: ParseRenameKeyEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_RENAME_KEY, sizeof(REG_EVENT_RENAME_KEY_INFO) + KeyName.MaximumLength + Info->NewName->Length);
    if (Event != NULL) {
      upk = (PREG_EVENT_RENAME_KEY_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_RENAME_KEY_INFO), upk);
	  
      NameStart = (PCHAR)NameStart + KeyName.Length;
      upk->NewKeyName.Length = Info->NewName->Length;
      upk->NewKeyName.Offset = (ULONG32)((ULONG64)NameStart - (ULONG64)upk);
      RtlCopyMemory(NameStart, Info->NewName->Buffer, upk->NewKeyName.Length);
	  
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }
  
  KdPrint(("regevents.c: ParseRenameKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseEnumerateKeyEvent(IN PREG_ENUMERATE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PVOID DataStart = NULL;
  PREG_EVENT_ENUMERATE_KEY_INFO usk = NULL;
  KdPrint(("regevents.c: ParseEnumerateKeyEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_ENUMERATE_KEY, sizeof(REG_EVENT_ENUMERATE_KEY_INFO) + KeyName.MaximumLength + Info->Length);
    if (Event != NULL) {
      usk = (PREG_EVENT_ENUMERATE_KEY_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_ENUMERATE_KEY_INFO), usk);
      usk->DataType = Info->KeyInformationClass;
      usk->SubkeyIndex = Info->Index;
      usk->DataSize = Info->Length;
      DataStart = (PCHAR)NameStart + KeyName.Length;
      usk->DataOffset = (ULONG64)DataStart - (ULONG64)usk;
      RtlCopyMemory(DataStart, Info->KeyInformation, (SIZE_T)usk->DataSize);

      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }

  KdPrint(("regevents.c: ParseEnumerateKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseEnumerateValueKeyEvent(IN PREG_ENUMERATE_VALUE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PVOID DataStart = NULL;
  PREG_EVENT_ENUMERATE_VALUE_INFO ush = NULL;
  KdPrint(("regevents.c: ParseEnumerateValueKeyEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_ENUMERATE_VALUE, sizeof(REG_EVENT_ENUMERATE_VALUE_INFO) + KeyName.MaximumLength + Info->Length);
    if (Event != NULL) {
      ush = (PREG_EVENT_ENUMERATE_VALUE_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_ENUMERATE_VALUE_INFO), ush);
      ush->DataType = Info->KeyValueInformationClass;
      ush->ValueIndex = Info->Index;
      ush->DataSize = Info->Length;
      DataStart = (PCHAR)NameStart + KeyName.Length;
      ush->DataOffset = (ULONG64)DataStart - (ULONG64)ush;
      RtlCopyMemory(DataStart, Info->KeyValueInformation , (SIZE_T)ush->DataSize);
	  
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }

  KdPrint(("regevents.c: ParseEnumerateValueKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseQueryKeyEvent(IN PREG_QUERY_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PVOID DataStart = NULL;
  PREG_EVENT_QUERY_KEY_INFO uck = NULL;
  KdPrint(("regevents.c: ParseQueryKeyEvent(Info=0x%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_QUERY_KEY, sizeof(REG_EVENT_QUERY_KEY_INFO) + KeyName.MaximumLength + Info->Length);
    if (Event != NULL) {
      uck = (PREG_EVENT_QUERY_KEY_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_QUERY_KEY_INFO), uck);
      uck->DataType = Info->KeyInformationClass;
      uck->DataSize = Info->Length;
      DataStart = (PCHAR)NameStart + KeyName.Length;
      uck->DataOffset = (ULONG64)DataStart - (ULONG64)uck;
      RtlCopyMemory(DataStart, Info->KeyInformation, (SIZE_T)uck->DataSize);
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }

  KdPrint(("regevents.c: ParseQueryKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseQueryValueKeyEvent(IN PREG_QUERY_VALUE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PVOID DataStart = NULL;
  PREG_EVENT_QUERY_VALUE_INFO uch = NULL;
  KdPrint(("regevents.c: ParseQueryValueKeyEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_QUERY_VALUE, sizeof(REG_EVENT_QUERY_VALUE_INFO) + KeyName.MaximumLength + Info->ValueName->Length + Info->Length);
    if (Event != NULL) {
      uch = (PREG_EVENT_QUERY_VALUE_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_QUERY_VALUE_INFO), uch);
      COPY_VALUE_NAME(NameStart, uch);
      uch->DataType = Info->KeyValueInformationClass;
      uch->DataSize = Info->Length;
      DataStart = (PCHAR)NameStart + Info->ValueName->Length;
      uch->DataOffset = (ULONG64)DataStart - (ULONG64)uch;
      RtlCopyMemory(DataStart, Info->KeyValueInformation, (SIZE_T)uch->DataSize);
	  
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }

  KdPrint(("regevents.c: ParseQueryValueKeyEvent-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseQueryMultipleValueKeyEvent(IN PREG_QUERY_MULTIPLE_VALUE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PVOID DataStart = NULL;
  PREG_EVENT_QUERY_MULTIPLE_VALUES_INFO uch = NULL;
  KdPrint(("regevents.c: ParseQueryMultipleValueKeyEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_QUERY_MULTIPLE_VALUES, sizeof(REG_EVENT_QUERY_MULTIPLE_VALUES_INFO) + KeyName.MaximumLength + *Info->BufferLength + Info->EntryCount * sizeof(KEY_VALUE_ENTRY));
    if (Event != NULL) {
      uch = (PREG_EVENT_QUERY_MULTIPLE_VALUES_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_QUERY_MULTIPLE_VALUES_INFO), uch);
      uch->DataSize = *Info->BufferLength;
      DataStart = (PCHAR)NameStart + KeyName.Length;
      uch->DataOffset = (ULONG32)((ULONG64)DataStart - (ULONG64)uch);
      RtlCopyMemory(DataStart, Info->ValueBuffer, uch->DataSize);
      DataStart = (PCHAR)DataStart + uch->DataSize;
      uch->ValuesCount = Info->EntryCount;
      uch->ValuesOffset = (ULONG32)((ULONG64)DataStart - (ULONG64)uch);
      RtlCopyMemory(DataStart, Info->ValueEntries, uch->ValuesCount * sizeof(KEY_VALUE_ENTRY));
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }

  KdPrint(("regevents.c: ParseQueryMultipleValueKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseFlushKeyEvent(IN PREG_FLUSH_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PREG_EVENT_FLUSH_KEY_INFO ufk = NULL;
  KdPrint(("regevents.c: ParseFlushKeyEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_FLUSH_KEY, sizeof(REG_EVENT_FLUSH_KEY_INFO) + KeyName.MaximumLength);
    if (Event != NULL) {
      ufk = (PREG_EVENT_FLUSH_KEY_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_FLUSH_KEY_INFO), ufk);
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }
  
  KdPrint(("regevents.c: ParseFlushKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseKeyHandleCloseEvent(IN PREG_KEY_HANDLE_CLOSE_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PREG_EVENT_CLOSE_KEY_INFO uzk = NULL;
  KdPrint(("regevents.c: ParseKeyHandleCloseEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_CLOSE_KEY, sizeof(REG_EVENT_CLOSE_KEY_INFO) + KeyName.MaximumLength);
    if (Event != NULL) {
      uzk = (PREG_EVENT_CLOSE_KEY_INFO)REFillGeneralInfo(Event, NULL);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_FLUSH_KEY_INFO), uzk);
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }
  
  KdPrint(("regevents.c: ParseKeyHandleCloseEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseCreateKeyEvent(IN PREG_PRE_CREATE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  PVOID NameStart = NULL;
  PREG_EVENT_CREATE_KEY_INFO uvk = NULL;
  KdPrint(("regevents.c: ParseCreateKeyEvent(Info=%p)\n", Info));
  
  Event = EventAlloc(REG_EVENT_CREATE_KEY, sizeof(REG_EVENT_CREATE_KEY_INFO) + Info->CompleteName->Length);
  if (Event != NULL) {
    uvk = (PREG_EVENT_CREATE_KEY_INFO)REFillGeneralInfo(Event, NULL);
    NameStart = (PCHAR)uvk + sizeof(REG_EVENT_CREATE_KEY_INFO);
    uvk->KeyName.Length = Info->CompleteName->Length;
    uvk->KeyName.Offset = (ULONG32)((ULONG64)NameStart - (ULONG64)uvk);
    RtlCopyMemory(NameStart, Info->CompleteName->Buffer, uvk->KeyName.Length);
    Status = REAddToHashTable(Event);
    if (!NT_SUCCESS(Status))
      EventFree(Event);
  } else Status = STATUS_INSUFFICIENT_RESOURCES;
	  
  KdPrint(("regevents.c: ParseCreateKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseOpenKeyEvent(IN PREG_PRE_OPEN_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  PVOID NameStart = NULL;
  PREG_EVENT_OPEN_KEY_INFO uok = NULL;
  KdPrint(("regevents.c: ParseOpenKeyEvent(Info=%p)\n", Info));
  
  Event = EventAlloc(REG_EVENT_OPEN_KEY, sizeof(REG_EVENT_OPEN_KEY_INFO) + Info->CompleteName->Length);
  if (Event != NULL) {
    uok = (PREG_EVENT_OPEN_KEY_INFO)REFillGeneralInfo(Event, NULL);
    NameStart = (PCHAR)uok + sizeof(REG_EVENT_OPEN_KEY_INFO);
    uok->KeyName.Length = Info->CompleteName->Length;
    uok->KeyName.Offset = (ULONG32)((ULONG64)NameStart - (ULONG64)uok);
    RtlCopyMemory(NameStart, Info->CompleteName->Buffer, uok->KeyName.Length);
    Status = REAddToHashTable(Event);
    if (!NT_SUCCESS(Status))
      EventFree(Event);
  } else Status = STATUS_INSUFFICIENT_RESOURCES;
	  
  KdPrint(("regevents.c: ParseOpenKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseCreateKeyExEvent(IN PREG_CREATE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  PVOID NameStart = NULL;
  UNICODE_STRING KeyName;
  PREG_EVENT_CREATE_KEY_EX_INFO uvke = NULL;
  KdPrint(("regevents.c: ParseCreateKeyExEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->RootObject, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_CREATE_KEY_EX, sizeof(REG_EVENT_CREATE_KEY_EX_INFO) + Info->CompleteName->Length + KeyName.Length);
    if (Event != NULL) {
      uvke = (PREG_EVENT_CREATE_KEY_EX_INFO)REFillGeneralInfo(Event, NULL);
      NameStart = (PCHAR)uvke + sizeof(REG_EVENT_CREATE_KEY_EX_INFO);
      uvke->NewSubkeyName.Length = Info->CompleteName->Length;
      uvke->NewSubkeyName.Offset = (ULONG32)((ULONG64)NameStart - (ULONG64)uvke);
      RtlCopyMemory(NameStart, Info->CompleteName->Buffer, uvke->NewSubkeyName.Length);
      NameStart = (PCHAR)NameStart + uvke->NewSubkeyName.Length;
      uvke->RootName.Offset = (ULONG32)((ULONG64)NameStart - (ULONG64)uvke);
      uvke->RootName.Length = KeyName.Length;
      RtlCopyMemory(NameStart, KeyName.Buffer, uvke->RootName.Length);
      uvke->DesiredAccess = Info->DesiredAccess;
      uvke->GrantedAccess = Info->GrantedAccess;
      uvke->CreateOptions = Info->CreateOptions;
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
  }	
	
  KdPrint(("regevents.c: ParseCreateKeyExEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseOpenKeyExEvent(IN PREG_OPEN_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  PVOID NameStart = NULL;
  UNICODE_STRING KeyName;
  PREG_EVENT_OPEN_KEY_EX_INFO uoke = NULL;
  KdPrint(("regevents.c: ParseOpenKeyExEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->RootObject, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_OPEN_KEY_EX, sizeof(REG_EVENT_OPEN_KEY_EX_INFO) + Info->CompleteName->Length + KeyName.Length);
    if (Event != NULL) {
      uoke = (PREG_EVENT_OPEN_KEY_EX_INFO)REFillGeneralInfo(Event, NULL);
      NameStart = (PCHAR)uoke + sizeof(REG_EVENT_OPEN_KEY_EX_INFO);
      uoke->SubkeyName.Length = Info->CompleteName->Length;
      uoke->SubkeyName.Offset = (ULONG32)((ULONG64)NameStart - (ULONG64)uoke);
      RtlCopyMemory(NameStart, Info->CompleteName->Buffer, uoke->SubkeyName.Length);
      NameStart = (PCHAR)NameStart + uoke->SubkeyName.Length;
      uoke->RootKeyName.Offset = (ULONG32)((ULONG64)NameStart - (ULONG64)uoke);
      uoke->RootKeyName.Length = KeyName.Length;
      RtlCopyMemory(NameStart, KeyName.Buffer, uoke->RootKeyName.Length);
      uoke->DesiredAccess = Info->DesiredAccess;
      uoke->GrantedAccess = Info->GrantedAccess;
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
  }	
	
  KdPrint(("regevents.c: ParseOpenKeyExEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseLoadKeyEvent(IN PREG_LOAD_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PREG_EVENT_LOAD_HIVE_INFO unh = NULL;
  KdPrint(("regevents.c: ParseLoadKeyEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_LOAD_HIVE, sizeof(REG_EVENT_LOAD_HIVE_INFO) + KeyName.MaximumLength + Info->KeyName->Length + Info->SourceFile->Length);
    if (Event != NULL) {
      unh = (PREG_EVENT_LOAD_HIVE_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_LOAD_HIVE_INFO), unh);
	  
      NameStart = (PCHAR)NameStart + unh->KeyName.Length;
      unh->RootkeyName.Length = Info->KeyName->Length;
      unh->RootkeyName.Offset = (ULONG32)((ULONG64)NameStart - (ULONG64)unh);
      RtlCopyMemory(NameStart, Info->KeyName->Buffer, unh->RootkeyName.Length);
	  
      NameStart = (PCHAR)NameStart + unh->RootkeyName.Length;
      unh->FileName.Length = Info->SourceFile->Length;
      unh->FileName.Offset = (ULONG32)((ULONG64)NameStart - (ULONG64)unh);
      RtlCopyMemory(NameStart, Info->SourceFile->Buffer, unh->FileName.Length);
	  
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }
  
  KdPrint(("regevents.c: ParseLoadKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseUnLoadKeyEvent(IN PREG_UNLOAD_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PREG_EVENT_UNLOAD_HIVE_INFO uuh = NULL;
  KdPrint(("regevents.c: ParseUnLoadKeyEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_UNLOAD_HIVE, sizeof(REG_EVENT_UNLOAD_HIVE_INFO) + KeyName.MaximumLength);
    if (Event != NULL) {
      uuh = (PREG_EVENT_UNLOAD_HIVE_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_UNLOAD_HIVE_INFO), uuh);
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }
  
  KdPrint(("regevents.c: ParseUnLoadKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseQueryKeySecurityEvent(IN PREG_QUERY_KEY_SECURITY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PVOID DataStart = NULL;
  PREG_EVENT_QUERY_KEY_SECURITY_INFO ucz = NULL;
  KdPrint(("regevents.c: ParseQueryKeySecurityEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_QUERY_SECURITY_KEY, sizeof(REG_EVENT_QUERY_KEY_SECURITY_INFO) + KeyName.MaximumLength + *Info->Length);
    if (Event != NULL) {
      ucz = (PREG_EVENT_QUERY_KEY_SECURITY_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_QUERY_KEY_SECURITY_INFO), ucz);
	  
      // Toto provest az v Post handleru
      DataStart = (PCHAR)NameStart + ucz->KeyName.Length;
      ucz->SecurityDescriptorLength = *Info->Length;
      ucz->SecurityDescriptorOffset = (ULONG32)((ULONG64)DataStart - (ULONG64)ucz);
      RtlCopyMemory(DataStart, Info->SecurityDescriptor, ucz->SecurityDescriptorLength);
	  
      ucz->OperationType = *Info->SecurityInformation;
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }
  
  KdPrint(("regevents.c: ParseQueryKeySecurityEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseSetKeySecurityEvent(IN PREG_SET_KEY_SECURITY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PVOID DataStart = NULL;
  PREG_EVENT_SET_KEY_SECURITY_INFO uzz = NULL;
  KdPrint(("regevents.c: ParseSetKeySecurityEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_SET_SECURITY_KEY, sizeof(REG_EVENT_SET_KEY_SECURITY_INFO) + KeyName.MaximumLength + sizeof(SECURITY_DESCRIPTOR));
    if (Event != NULL) {
      uzz = (PREG_EVENT_SET_KEY_SECURITY_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_SET_KEY_SECURITY_INFO), uzz);
	  
      DataStart = (PCHAR)NameStart + uzz->KeyName.Length;
      uzz->SecurityDescriptorLength = sizeof(SECURITY_DESCRIPTOR);
      uzz->SecurityDescriptorOffset = (ULONG32)((ULONG64)DataStart - (ULONG64)uzz);
      RtlCopyMemory(DataStart, Info->SecurityDescriptor, uzz->SecurityDescriptorLength);
	  
      uzz->OperationType = *Info->SecurityInformation;
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }
  
  KdPrint(("regevents.c: ParseSetKeySecurityEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseRestoreKeyEvent(IN PREG_RESTORE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PREG_EVENT_RESTORE_KEY_INFO uok = NULL;
  KdPrint(("regevents.c: ParseRestoreKeyEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_RESTORE_KEY, sizeof(REG_EVENT_RESTORE_KEY_INFO) + KeyName.MaximumLength);
    if (Event != NULL) {
      uok = (PREG_EVENT_RESTORE_KEY_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_RESTORE_KEY_INFO), uok);
      uok->FileHandle = (ULONG64)Info->FileHandle;
      uok->Flags = Info->Flags;
	  
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }
  
  KdPrint(("regevents.c: ParseRestoreKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseSaveKeyEvent(IN PREG_SAVE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PREG_EVENT_SAVE_KEY_INFO uuk = NULL;
  KdPrint(("regevents.c: ParseSaveKeyEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_SAVE_KEY, sizeof(REG_EVENT_SAVE_KEY_INFO) + KeyName.MaximumLength);
    if (Event != NULL) {
      uuk = (PREG_EVENT_SAVE_KEY_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_SAVE_KEY_INFO), uuk);
      uuk->FileHandle = (ULONG64)Info->FileHandle;
	  
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }
  
  KdPrint(("regevents.c: ParseSaveKeyEvent(-):0x%x\n", Status));
  return Status;
}

NTSTATUS ParseReplaceKeyEvent(IN PREG_REPLACE_KEY_INFORMATION Info)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  PEVENT Event = NULL;
  UNICODE_STRING KeyName;
  PVOID NameStart = NULL;
  PREG_EVENT_REPLACE_KEY_INFO upk = NULL;
  KdPrint(("regevents.c: ParseReplaceKeyEvent(Info=%p)\n", Info));
  
  Status = GetObjectName(Info->Object, &KeyName);
  if (NT_SUCCESS(Status)) {
    Event = EventAlloc(REG_EVENT_REPLACE_KEY, sizeof(REG_EVENT_REPLACE_KEY_INFO) + KeyName.MaximumLength + Info->OldFileName->Length + Info->NewFileName->Length);
    if (Event != NULL) {
      upk = (PREG_EVENT_REPLACE_KEY_INFO)REFillGeneralInfo(Event, Info->Object);
      COPY_KEY_NAME(NameStart, sizeof(REG_EVENT_REPLACE_KEY_INFO), upk);

      NameStart = (PCHAR)NameStart + KeyName.Length;
      upk->FileName.Length = Info->NewFileName->Length;
      upk->FileName.Offset = (ULONG32)((ULONG64)NameStart - (ULONG64)upk);
      RtlCopyMemory(NameStart, Info->NewFileName->Buffer, upk->FileName.Length);
	  
      NameStart = (PCHAR)NameStart + Info->NewFileName->Length;
      upk->BackupFileName.Length = Info->OldFileName->Length;
      upk->BackupFileName.Offset = (ULONG32)((ULONG64)NameStart - (ULONG64)upk);
      RtlCopyMemory(NameStart, Info->OldFileName->Buffer, upk->BackupFileName.Length);
	    
      Status = REAddToHashTable(Event);
      if (!NT_SUCCESS(Status))
        EventFree(Event);
    } else Status = STATUS_INSUFFICIENT_RESOURCES;
	
    ExFreePoolWithTag(KeyName.Buffer, POOL_TAG);
  }
  
  KdPrint(("regevents.c: ParseReplaceKeyEvent(-):0x%x\n", Status));
  return Status;
}




NTSTATUS RegEventsInit(VOID)
{
  NTSTATUS Status = STATUS_UNSUCCESSFUL;
  KdPrint(("regevents.c: RegEventsInit()\n"));
  
  Status = HashTableCreate(64, EventHashTableHash, EventHashTableCompare, EventHashTableDeleteItem, &EventHashTable);
  
  KdPrint(("regevents.c: RegEventsInit(-):0x%x\n", Status));
  return Status;
}

VOID RegEventsFinit(VOID)
{
  KdPrint(("regevents.c: RegEventsFinit()\n"));

  HashTableDestroy(EventHashTable);
  
  KdPrint(("regevents.c: RegEventsFinit(-)\n"));
  return;
}
