/*
 * COPYRIGHT:   See COPYING in the top level directory
 * LICENSE:     See LGPL.txt in the top level directory
 * PROJECT:     ReactOS system libraries
 * FILE:        reactos/lib/epsapi/enum/module.c
 * PURPOSE:     Enumerate process modules
 * PROGRAMMER:  KJK::Hyperion <noog@libero.it>
 * UPDATE HISTORY:
 *              10/06/2002: Created
 *              29/08/2002: Generalized the interface to improve reusability,
 *                          more efficient use of memory operations
 *              12/02/2003: malloc and free renamed to PsaiMalloc and PsaiFree,
 *                          for better reusability
 *              02/04/2003: System modules enumeration moved into its own file
 *              12/04/2003: internal PSAPI renamed EPSAPI (Extended PSAPI) and
 *                          isolated in its own library to clear the confusion
 *                          and improve reusability
 */

#include "precomp.h"

#include <ndk/mmfuncs.h>
#include <ndk/psfuncs.h>
#include <ndk/rtlfuncs.h>

#define NDEBUG
#include <debug.h>

NTSTATUS NTAPI
PsaEnumerateProcessModules(IN HANDLE ProcessHandle,
                           IN PPROCMOD_ENUM_ROUTINE Callback,
                           IN OUT PVOID CallbackContext)
{
  NTSTATUS Status;

  /* current process - use direct memory copy */
  /* FIXME - compare process id instead of a handle */
  if(ProcessHandle == NtCurrentProcess())
  {
    PLIST_ENTRY ListHead, Current;

#if 0
    __try
    {
#endif
      ListHead = &(NtCurrentPeb()->Ldr->InLoadOrderModuleList);
      Current = ListHead->Flink;
 
      while(Current != ListHead)
      {
        PLDR_DATA_TABLE_ENTRY LoaderModule = CONTAINING_RECORD(Current, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
   
        /* return the current module to the callback */
        Status = Callback(ProcessHandle, LoaderModule, CallbackContext);
    
        if(!NT_SUCCESS(Status))
        {
          goto Failure;
        }
    
        Current = LoaderModule->InLoadOrderLinks.Flink;
      }
#if 0
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
      return GetExceptionCode();
    }
#endif
  }
  else
  {
    PROCESS_BASIC_INFORMATION BasicInformation;
    PPEB_LDR_DATA LoaderData;
    LDR_DATA_TABLE_ENTRY LoaderModule;
    PLIST_ENTRY ListHead, Current;
 
    /* query the process basic information (includes the PEB address) */
    Status = NtQueryInformationProcess(ProcessHandle,
                                       ProcessBasicInformation,
                                       &BasicInformation,
                                       sizeof(BasicInformation),
                                       NULL);
 
    if(!NT_SUCCESS(Status))
    {
      DPRINT(FAILED_WITH_STATUS, "NtQueryInformationProcess", Status);
      goto Failure;
    }
 
    /* get the address of the PE Loader data */
    Status = NtReadVirtualMemory(ProcessHandle,
                                 &(BasicInformation.PebBaseAddress->Ldr),
                                 &LoaderData,
                                 sizeof(LoaderData),
                                 NULL);
 
    if(!NT_SUCCESS(Status))
    {
      DPRINT(FAILED_WITH_STATUS, "NtReadVirtualMemory", Status);
      goto Failure;
    }
 
    /* head of the module list: the last element in the list will point to this */
    ListHead = &LoaderData->InLoadOrderModuleList;
 
    /* get the address of the first element in the list */
    Status = NtReadVirtualMemory(ProcessHandle,
                                 &(LoaderData->InLoadOrderModuleList.Flink),
                                 &Current,
                                 sizeof(Current),
                                 NULL);
 
    while(Current != ListHead)
    {
      /* read the current module */
      Status = NtReadVirtualMemory(ProcessHandle,
                                   CONTAINING_RECORD(Current, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks),
                                   &LoaderModule,
                                   sizeof(LoaderModule),
                                   NULL);
 
      if(!NT_SUCCESS(Status))
      {
        DPRINT(FAILED_WITH_STATUS, "NtReadVirtualMemory", Status);
        goto Failure;
      }

      /* return the current module to the callback */
      Status = Callback(ProcessHandle, &LoaderModule, CallbackContext);
   
      if(!NT_SUCCESS(Status))
      {
        goto Failure;
      }
    
      /* address of the next module in the list */
      Current = LoaderModule.InLoadOrderLinks.Flink;
    }
  }

  return STATUS_SUCCESS;

Failure:
  return Status;
}

/* EOF */