/*++

 Copyright (c) 1996 Microsoft Corporation

Module Name:

    tlhelp32.c

Abstract:

    NT implementation of win95 ToolHelp API's

Author:

    John Daly

Environment:

    NT Only

Notes:

    Version 1.0
    structure definitions/documentation in tlhelp32.h


Revision History:

    John Daly (johndaly) 5-Apr-1996
        initial implementation

--*/

#include "basedll.h"
#pragma hdrstop

#include "tlhelp32.h"

#define BUFFER_SIZE 64*1024
#define ARRAYSIZE(x)        (sizeof(x) / sizeof(x[0]))

/*
 snapshot structure

 This is mapped onto the beginning of the memory we use to hold the information.

*/
typedef struct tagSNAPSHOTSTATE {
    /* item list counts */
    ULONG    HeapListCount;
    ULONG    ProcessCount;
    ULONG    ModuleCount;
    ULONG    ThreadCount;
    /* item list head pointers */
    PHEAPLIST32         HeapListHead;
    PPROCESSENTRY32W    ProcessListHead;
    PMODULEENTRY32W     ModuleListHead;
    PTHREADENTRY32      ThreadListHead;
    /* item list current indexes */
    ULONG   HeapListIndex;
    ULONG   ProcessListIndex;
    ULONG   ModuleListIndex;
    ULONG   ThreadListIndex;
    /* data begins here... */
    UCHAR    DataBegin;
}SNAPSHOTSTATE;
typedef SNAPSHOTSTATE * PSNAPSHOTSTATE;


//
// private functions
//

NTSTATUS
ThpCreateRawSnap(
    IN ULONG dwFlags,
    IN ULONG th32ProcessID,
    PUCHAR *RawProcess,
    PRTL_DEBUG_INFORMATION *RawModule,
    PRTL_DEBUG_INFORMATION *RawDebugInfo);

NTSTATUS
ThpAllocateSnapshotSection(
    OUT PHANDLE SnapSection,
    IN DWORD dwFlags,
    IN DWORD th32ProcessID,
    PUCHAR RawProcess,
    PRTL_DEBUG_INFORMATION RawModule,
    PRTL_DEBUG_INFORMATION RawDebugInfo);

NTSTATUS
ThpProcessToSnap(
    IN DWORD dwFlags,
    IN DWORD th32ProcessID,
    IN HANDLE SnapSection,
    PUCHAR RawProcess,
    PRTL_DEBUG_INFORMATION RawModule,
    PRTL_DEBUG_INFORMATION RawDebugInfo);

HANDLE
WINAPI
CreateToolhelp32Snapshot(
    IN DWORD dwFlags,
    IN DWORD th32ProcessID)
/*++

Routine Description:

    Takes a snapshot of the Win32 processes, heaps, modules, and threads used
    by the Win32 processes. Returns an open handle to the specified snapshot if
    successful or  -1 otherwise.

    NOTE that all of the snapshots are global except for the heap and module
    lists which are process specific. To enumerate the heap or module state for
    all WIN32 processes call with TH32CS_SNAPALL and the current process. Then
    for each process in the TH32CS_SNAPPROCESS list that isn't the current
    process, do a call with just TH32CS_SNAPHEAPLIST and/or TH32CS_SNAPMODULE.

    Use CloseHandle to destroy the snapshot

    This function is not multi-thread safe.  All of the other functions are.

Arguments:

    dwFlags - Supplies switches to specify action as follows:
        TH32CS_INHERIT  Indicates that the snapshot handle is to be inheritable.
        TH32CS_SNAPALL  Equivalent to specifying the TH32CS_SNAPHEAPLIST,
                        TH32CS_SNAPMODULE, TH32CS_SNAPPROCESS, and
                        TH32CS_SNAPTHREAD values.
        TH32CS_SNAPHEAPLIST     Includes the heap list of the specified Win32
                                process in the snapshot.
        TH32CS_SNAPMODULE       Includes the module list of the specified Win32
                                process in the snapshot.
        TH32CS_SNAPPROCESS      Includes the Win32 process list in the snapshot.
        TH32CS_SNAPTHREAD       Includes the Win32 thread list in the snapshot.

    th32ProcessID - Supplies a Win32 process identifier. This parameter can be
        zero to indicate the current process. This parameter is used when the
        TH32CS_SNAPHEAPLIST or TH32CS_SNAPMODULE value is specified. Otherwise,
        it is ignored. The snapshot taken by this function is examined by the
        other tool help functions to provide their results.  Access to the
        snapshot is read only. The snapshot handle acts like a Win32 object
        handle and is subject to the same rules regarding which processes and
        threads it is valid in.

Return Value:

    Returns an open handle to the specified snapshot if successful or -1 if not.
    To retrieve an extended error status code generated by this function, use
    the GetLastError function.
    To destroy the snapshot, use the CloseHandle function.

--*/
{
    HANDLE SnapSection;
    PUCHAR RawProcess;
    PRTL_DEBUG_INFORMATION RawModule;
    PRTL_DEBUG_INFORMATION RawDebugInfo;
    NTSTATUS Status = 0;

    if (th32ProcessID == 0) {
        th32ProcessID = GetCurrentProcessId();
    }

    //
    // process the requested data types
    //

    Status = ThpCreateRawSnap(dwFlags,
                              th32ProcessID,
                              &RawProcess,
                              &RawModule,
                              &RawDebugInfo);

    if (!NT_SUCCESS(Status)) {
        BaseSetLastNTError(Status);
        return (HANDLE)-1;
    }

    Status = ThpAllocateSnapshotSection(&SnapSection,
                                        dwFlags,
                                        th32ProcessID,
                                        RawProcess,
                                        RawModule,
                                        RawDebugInfo);

    if (!NT_SUCCESS(Status)) {
        BaseSetLastNTError(Status);
        return (HANDLE)-1;
    }

    Status = ThpProcessToSnap(dwFlags,
                              th32ProcessID,
                              SnapSection,
                              RawProcess,
                              RawModule,
                              RawDebugInfo);

    if (!NT_SUCCESS(Status)) {
        CloseHandle(SnapSection);
        BaseSetLastNTError(Status);
        return (HANDLE)-1;
    }

    return SnapSection;
}


BOOL
WINAPI
Heap32ListFirst(
   IN HANDLE SnapSection,
   IN OUT LPHEAPLIST32 lphl)
/*++

Routine Description:

    Retrieves information about the first heap that has been allocated by a
    specified Win32 process.

Arguments:

    SnapSection - Supplies the handle of the snapshot returned from a previous
        call to the CreateToolhelp32Snapshot function.

    lphl - Returns a HEAPLIST32 structure.  The calling application must set
        the dwSize member of HEAPLIST32 to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if the first entry of the heap list has been copied to the buffer
    or FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the
    GetLastError function when no heap list exists or the snapshot does not contain
    heap list information.

--*/
{
    PSNAPSHOTSTATE SnapshotBase;
    LARGE_INTEGER SectionOffset;
    SIZE_T ViewSize;
    NTSTATUS Status = 0;
    BOOL retv = FALSE;

    if (!lphl || lphl->dwSize != sizeof(HEAPLIST32)) {
        BaseSetLastNTError(STATUS_INFO_LENGTH_MISMATCH);
        return FALSE;
    }

    SectionOffset.LowPart = 0;
    SectionOffset.HighPart = 0;
    ViewSize = 0;
    SnapshotBase = 0;

    Status = NtMapViewOfSection(SnapSection,
                NtCurrentProcess(),
                &SnapshotBase,
                0L,
                0L,
                &SectionOffset,
                &ViewSize,
                ViewShare,
                0L,
                PAGE_READWRITE);

    if (!NT_SUCCESS(Status)) {
        BaseSetLastNTError(Status);
        return FALSE;
    }

    if (SnapshotBase->HeapListCount == 0) {
        RtlZeroMemory((PUCHAR)lphl + sizeof(SIZE_T), (lphl->dwSize - sizeof(SIZE_T)));
        SetLastError(ERROR_NO_MORE_FILES);
    } else {
        memcpy(lphl,
               (LPHEAPLIST32)((ULONG_PTR)SnapshotBase + (ULONG_PTR)SnapshotBase->HeapListHead),
               sizeof(HEAPLIST32));
        retv = TRUE;
        SnapshotBase->HeapListIndex = 1;
    }

    NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)SnapshotBase);
    return retv;
}


BOOL
WINAPI
Heap32ListNext(
   IN HANDLE SnapSection,
   IN OUT LPHEAPLIST32 lphl)
/*++

Routine Description:

    Retrieves information about the next heap that has been allocated by a
    Win32 process.

Arguments:

    SnapSection - Supplies the handle of the snapshot returned from a previous
        call to the CreateToolhelp32Snapshot function.

   lphl - Returns a HEAPLIST32 structure. The calling application must set the
        dwSize member of HEAPLIST32 to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if the next entry of the heap list has been copied to the buffer or
    FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the
    GetLastError function when no more entries in the heap list exist.

--*/
{
    PSNAPSHOTSTATE SnapshotBase;
    BOOL retv = FALSE;
    LARGE_INTEGER SectionOffset;
    SIZE_T ViewSize;
    NTSTATUS Status = 0;

    if (!lphl || lphl->dwSize != sizeof(HEAPLIST32)) {
        BaseSetLastNTError(STATUS_INFO_LENGTH_MISMATCH);
        return FALSE;
    }

    SectionOffset.LowPart = 0;
    SectionOffset.HighPart = 0;
    ViewSize = 0;
    SnapshotBase = 0;

    Status = NtMapViewOfSection(SnapSection,
                NtCurrentProcess(),
                &SnapshotBase,
                0L,
                0L,
                &SectionOffset,
                &ViewSize,
                ViewShare,
                0L,
                PAGE_READWRITE);

    if (!NT_SUCCESS(Status)) {
        BaseSetLastNTError(Status);
        return FALSE;
    }

    if (SnapshotBase->HeapListIndex < SnapshotBase->HeapListCount) {
        memcpy(lphl,
               (LPHEAPLIST32)((ULONG_PTR)SnapshotBase + (ULONG_PTR)(&SnapshotBase->HeapListHead[SnapshotBase->HeapListIndex++])),
               sizeof(HEAPLIST32));
        retv = TRUE;
    } else {
        SetLastError(ERROR_NO_MORE_FILES);
    }

    NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)SnapshotBase);

    return(retv);
}


BOOL
WINAPI
Heap32First(
   IN OUT LPHEAPENTRY32 lphe,
   IN DWORD th32ProcessID,
   IN ULONG_PTR th32HeapID)
/*++

Routine Description:

    Retrieves information about the first block of a heap that has been
    allocated by a Win32 process.  Also, create a snapshot of that heap so
    the Heap32Next functions can walk them.

Arguments:

    lphe - Returns a HEAPENTRY32 structure.  The calling application must set
        the dwSize member to the size, in bytes, of the structure.

    th32ProcessID - Supplies the identifier of the Win32 process context that
        owns the heap.

    th32HeapID - Supplies the identifier of the heap to enumerate.

Return Value:

    Returns TRUE if information for the first heap block has been copied to the buffer
    or FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the
    GetLastError function if the heap is invalid or empty.

Notes:

    since there is no way to save heap snapshots or delete heap snapshots in any way that
    makes sense (because this info is not associated with snapshots etc), the information
    needs to be generated completely for each call.  This is very expensive, it would be
    a good idea to redesign this API...
    We could try and cheat by preserving snapshots until they are completyely iterated or
    until a new one is taken, etc, but we will just end up with a bunch of memory leaks

--*/
{
    PRTL_DEBUG_INFORMATION ThRawHeapDebugInfo;
    ULONG HeapListCount;
    PRTL_HEAP_ENTRY p;
    PRTL_HEAP_INFORMATION HeapInfo;
    ULONG HeapEntryAddress;
    NTSTATUS Status = 0;
    BOOL retv = FALSE;

    if (!lphe || lphe->dwSize != sizeof(HEAPENTRY32)) {
        BaseSetLastNTError(STATUS_INFO_LENGTH_MISMATCH);
        return FALSE;
    }

    //
    // take snapshot
    //

    ThRawHeapDebugInfo = RtlCreateQueryDebugBuffer(0, FALSE);
    if(ThRawHeapDebugInfo == 0)
    {
        return STATUS_UNSUCCESSFUL;
    }
    Status = RtlQueryProcessDebugInformation((HANDLE)LongToHandle(th32ProcessID),
                                          RTL_QUERY_PROCESS_HEAP_SUMMARY |
                                          RTL_QUERY_PROCESS_HEAP_ENTRIES,
                                          ThRawHeapDebugInfo);
    if (!NT_SUCCESS(Status))    {
        RtlDestroyQueryDebugBuffer(ThRawHeapDebugInfo);
        BaseSetLastNTError(Status);
        return FALSE;
    }

    //
    // query snapshot
    //

    for (HeapListCount = 0;
         HeapListCount < ThRawHeapDebugInfo->Heaps->NumberOfHeaps;
         HeapListCount++) {

        HeapInfo = &ThRawHeapDebugInfo->Heaps->Heaps[HeapListCount];

        if ((ULONG_PTR)HeapInfo->BaseAddress == th32HeapID) {

            p = HeapInfo->Entries;

            lphe->dwResvd = 0;
            lphe->dwLockCount = 0;
            lphe->th32HeapID = th32HeapID;
            lphe->th32ProcessID = th32ProcessID;
            lphe->hHandle = (HANDLE)th32HeapID;// try this way

            // walk up to first non-segment block (I am assuming there is always one)
            // skip segments - can you have 2 in a row?
            // is first block always a segment?
            // We translate the heap flags to the most appropriate LF32_xxx values
            while(RTL_HEAP_SEGMENT & p->Flags)  {
                lphe->dwAddress = (ULONG_PTR)p->u.s2.FirstBlock + ThRawHeapDebugInfo->Heaps->Heaps[HeapListCount].EntryOverhead;    // reset this
                ++lphe->dwResvd;
                ++p;
            }

            //
            // munge flags
            //

            //----------------------------------------------
            if ((p->Flags & RTL_HEAP_BUSY)                ||
                (p->Flags & RTL_HEAP_SETTABLE_VALUE)      ||
                (p->Flags & RTL_HEAP_SETTABLE_FLAG2)      ||
                (p->Flags & RTL_HEAP_SETTABLE_FLAG3)      ||
                (p->Flags & RTL_HEAP_SETTABLE_FLAGS)      ||
                (p->Flags & RTL_HEAP_PROTECTED_ENTRY)
                ) {
                lphe->dwFlags = LF32_FIXED;
            }
            else if ( p->Flags & RTL_HEAP_SETTABLE_FLAG1) {
                lphe->dwFlags = LF32_MOVEABLE;
            }
            else if ( p->Flags & RTL_HEAP_UNCOMMITTED_RANGE) {
                lphe->dwFlags = LF32_FREE;
            }
            //----------------------------------------------

            lphe->dwBlockSize = p->Size;
            retv = TRUE;
            break;
        }
    }

    //
    // free snapshot
    //

    RtlDestroyQueryDebugBuffer(ThRawHeapDebugInfo);

    return retv;

}


BOOL
WINAPI
Heap32Next(
   IN OUT LPHEAPENTRY32 lphe)
/*++

Routine Description:

    Retrieves information about the next block of a heap that has been
    allocated by a Win32 process.

Arguments:

    lphe - Returns a HEAPENTRY32 structure.  The calling application must set
        the dwSize member to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if information about the next block in the heap has been
    copied to the buffer or FALSE otherwise. The ERROR_NO_MORE_FILES error
    value is returned by the GetLastError function when no more objects in
    the heap exist.

note:

    this function can be prone to error since the heap can change between
    calls to get heaplists, Heap32First, etc.  There is no good way to
    manage a snapshot using this model, so we just get to live with it.

--*/
{
    PRTL_DEBUG_INFORMATION ThRawHeapDebugInfo;
    PRTL_HEAP_ENTRY p;
    PRTL_HEAP_INFORMATION HeapInfo;
    ULONG HeapListCount;
    BOOL retv = FALSE;
    BOOL hit_seg = FALSE;
    NTSTATUS Status = 0;

    if (!lphe || lphe->dwSize != sizeof(HEAPENTRY32)) {
        BaseSetLastNTError(STATUS_INFO_LENGTH_MISMATCH);
        return FALSE;
    }

    //
    // take snapshot
    //

    ThRawHeapDebugInfo = RtlCreateQueryDebugBuffer(0, FALSE);
    if(ThRawHeapDebugInfo == 0)
    {
        return STATUS_UNSUCCESSFUL;
    }
    Status = RtlQueryProcessDebugInformation((HANDLE)LongToHandle(lphe->th32ProcessID),
                                          RTL_QUERY_PROCESS_HEAP_SUMMARY |
                                          RTL_QUERY_PROCESS_HEAP_ENTRIES,
                                          ThRawHeapDebugInfo);
    if (!NT_SUCCESS(Status))    {
        RtlDestroyQueryDebugBuffer(ThRawHeapDebugInfo);
        BaseSetLastNTError(Status);
        return FALSE;
    }

    //
    // get index to correct heap list heap list - th32HeapID / baseadress
    //

    for (HeapListCount = 0; HeapListCount < ThRawHeapDebugInfo->Heaps->NumberOfHeaps; ++HeapListCount)
    {
        if((ULONG_PTR)ThRawHeapDebugInfo->Heaps->Heaps[HeapListCount].BaseAddress == lphe->th32HeapID)
        {
            break;
        }
    }

    //
    // ran out of heaps
    //

    if(HeapListCount >= ThRawHeapDebugInfo->Heaps->NumberOfHeaps)
    {
        RtlDestroyQueryDebugBuffer(ThRawHeapDebugInfo);
        SetLastError(ERROR_NO_MORE_FILES);
        return FALSE;
    }

    //
    // check for last entry
    //

    ++lphe->dwResvd;    // point to next one

    if(lphe->dwResvd >= ThRawHeapDebugInfo->Heaps->Heaps[HeapListCount].NumberOfEntries)
    {
        RtlDestroyQueryDebugBuffer(ThRawHeapDebugInfo);
        SetLastError(ERROR_NO_MORE_FILES);
        return FALSE;
    }

    //
    // point to correct heap entry - index of this is kept in lphe->dwResvd
    //
    p = (PRTL_HEAP_ENTRY)&ThRawHeapDebugInfo->Heaps->Heaps[HeapListCount].Entries[lphe->dwResvd];

    // keep segments in lphe->hHandle
    while(RTL_HEAP_SEGMENT & p->Flags)  {
        lphe->dwAddress = (ULONG_PTR)p->u.s2.FirstBlock + ThRawHeapDebugInfo->Heaps->Heaps[HeapListCount].EntryOverhead;// reset this
        if(lphe->dwResvd >= ThRawHeapDebugInfo->Heaps->Heaps[HeapListCount].NumberOfEntries)
        {
            RtlDestroyQueryDebugBuffer(ThRawHeapDebugInfo);
            SetLastError(ERROR_NO_MORE_FILES);
            return FALSE;
        }
        ++lphe->dwResvd;
        ++p;
        hit_seg = TRUE;
    }

    //
    // calculate the address
    // normally, we could add the size of the previous block to the existing (last) address
    // to get the current address, but this is only for blocks that do not follow a segment
    // I am assuming that there will always be a segment first
    // current address = last address() + last size
    // by the time we reach this point, either we have just exited from scannin a segment, or
    // p-> is pointing at a non-segment entry.
    //

    if(hit_seg == FALSE)
    {
        lphe->dwAddress += lphe->dwBlockSize;
    }

    lphe->dwBlockSize = p->Size;

    //
    // munge flags
    //

    // We translate the heap flags to the most appropriate LF32_xxx values
    //----------------------------------------------
    if( (p->Flags & RTL_HEAP_BUSY)                ||
        (p->Flags & RTL_HEAP_SETTABLE_VALUE)      ||
        (p->Flags & RTL_HEAP_SETTABLE_FLAG2)      ||
        (p->Flags & RTL_HEAP_SETTABLE_FLAG3)      ||
        (p->Flags & RTL_HEAP_SETTABLE_FLAGS)      ||
        (p->Flags & RTL_HEAP_PROTECTED_ENTRY)
        ) {
        lphe->dwFlags = LF32_FIXED;
    }
    else if( p->Flags & RTL_HEAP_SETTABLE_FLAG1) {
        lphe->dwFlags = LF32_MOVEABLE;
    }
    else if( p->Flags & RTL_HEAP_UNCOMMITTED_RANGE) {
        lphe->dwFlags = LF32_FREE;
    }
    //----------------------------------------------

    retv = TRUE;

    //
    // free snapshot
    //

    RtlDestroyQueryDebugBuffer(ThRawHeapDebugInfo);

    return(retv);
}


BOOL
WINAPI
Toolhelp32ReadProcessMemory(
   IN DWORD th32ProcessID,
   IN LPCVOID lpBaseAddress,
   OUT PUCHAR lpBuffer,
   IN SIZE_T cbRead,
   OUT SIZE_T *lpNumberOfBytesRead)
/*++

Routine Description:

    Copies memory allocated to another process into an application-supplied
    buffer.  This function is for people who are to lazy to open
    the process themselves.

Arguments:

    th32ProcessID - Supplies the Identifier of the Win32 process whose memory
        is being copied. This parameter can be zero to copy the memory of the
        current process.

    lpBaseAddress - Supplies the base address in the specified process to read.
        Before transferring any data, the system verifies that all data in the
        base address and memory of the specified size is accessible for read
        access. If this is the case, the function proceeds. Otherwise, the
        function fails.

    lpBuffer - Returns the requested data

    cbRead - Supplies the number of bytes to read from the specified process.

    lpNumberOfBytesRead - Returns the number of bytes copied to the buffer. If
        this parameter is NULL, it is ignored.

Return Value:

    Returns TRUE if successful.

--*/
{
    HANDLE hProcess;
    BOOL RetVal;

    hProcess = OpenProcess(PROCESS_VM_READ, FALSE, th32ProcessID);
    if (hProcess == NULL) {
        return FALSE;
    }

    RetVal = ReadProcessMemory(hProcess,
                               lpBaseAddress,
                               lpBuffer,
                               cbRead,
                               lpNumberOfBytesRead);

    CloseHandle(hProcess);
    return RetVal;
}


BOOL
WINAPI
Process32FirstW(
   IN HANDLE SnapSection,
   IN OUT LPPROCESSENTRY32W lppe)
/*++

Routine Description:

    Retrieves information about the first Win32 process encountered in a system
    snapshot.

Arguments:

    SnapSection - Supplies the handle of a snapshot returned from a previous call
        to the CreateToolhelp32Snapshot function.

    lppe - Returns a PROCESSENTRY32W structure.  The caller must set the dwSize
        member to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if the first entry of the process list has been copied to the buffer
    or FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the
    GetLastError function if no processes exist or the snapshot does not contain
    process information.

--*/
{
    PSNAPSHOTSTATE SnapshotBase;
    LARGE_INTEGER SectionOffset;
    SIZE_T ViewSize;
    NTSTATUS Status = 0;
    BOOL    retv = FALSE;

    if (!lppe || lppe->dwSize != sizeof(PROCESSENTRY32W)) {
        BaseSetLastNTError(STATUS_INFO_LENGTH_MISMATCH);
        return FALSE;
    }

    SectionOffset.LowPart = 0;
    SectionOffset.HighPart = 0;
    ViewSize = 0;
    SnapshotBase = 0;

    Status = NtMapViewOfSection(SnapSection,
                NtCurrentProcess(),
                &SnapshotBase,
                0L,
                0L,
                &SectionOffset,
                &ViewSize,
                ViewShare,
                0L,
                PAGE_READWRITE);

    if (!NT_SUCCESS(Status)) {
        BaseSetLastNTError(Status);
        return FALSE;
    }

    if (SnapshotBase->ProcessCount == 0) {
        memset((PUCHAR)lppe + 4, 0, lppe->dwSize - 4);
        SetLastError(ERROR_NO_MORE_FILES);
    } else {
        memcpy(lppe,
               (LPPROCESSENTRY32W)((ULONG_PTR)SnapshotBase + (ULONG_PTR)SnapshotBase->ProcessListHead),
               sizeof(PROCESSENTRY32W));
        SnapshotBase->ProcessListIndex = 1;
        retv = TRUE;
    }

    NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)SnapshotBase);

    return retv;
}

BOOL
WINAPI
Process32First(
   IN HANDLE SnapSection,
   IN OUT LPPROCESSENTRY32 lppe)
/*++

Routine Description:

    ANSI version of Process32FirstW.

    Retrieves information about the first Win32 process encountered in a system
    snapshot.

Arguments:

    SnapSection - Supplies the handle of a snapshot returned from a previous call
        to the CreateToolhelp32Snapshot function.

    lppe - Returns a PROCESSENTRY32 structure.  The caller must set the dwSize
        member to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if the first entry of the process list has been copied to the buffer
    or FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the
    GetLastError function if no processes exist or the snapshot does not contain
    process information.

--*/
{
    PROCESSENTRY32W pe32w;
    BOOL b;

    if (lppe == NULL || (lppe->dwSize < sizeof(PROCESSENTRY32))) {
        BaseSetLastNTError(STATUS_INFO_LENGTH_MISMATCH);
        return FALSE;
    }

    // Thunk to Process32FirstW
    pe32w.dwSize = sizeof(pe32w);
    b = Process32FirstW(SnapSection,&pe32w);

    WideCharToMultiByte(CP_ACP, 0,
                        pe32w.szExeFile, -1,
                        lppe->szExeFile, ARRAYSIZE(lppe->szExeFile),
                        0, 0);

    lppe->cntUsage = pe32w.cntUsage;    // meaningless on NT, copy anyway
    lppe->th32ProcessID = pe32w.th32ProcessID;
    lppe->th32DefaultHeapID = pe32w.th32DefaultHeapID;
    lppe->th32ModuleID = pe32w.th32ModuleID;
    lppe->cntThreads = pe32w.cntThreads;
    lppe->th32ParentProcessID = pe32w.th32ParentProcessID;
    lppe->pcPriClassBase = pe32w.pcPriClassBase;
    lppe->dwFlags = pe32w.dwFlags;

    return b;
}


BOOL
WINAPI
Process32NextW(
   IN HANDLE SnapSection,
   IN OUT LPPROCESSENTRY32W lppe)
/*++

Routine Description:

    Retrieves information about the next Win32 process recorded in a system snapshot.

Arguments:

    SnapSection - Supplies the handle of a snapshot returned from a previous call
        to the CreateToolhelp32Snapshot function.

    lppe - Returns a PROCESSENTRY32W structure.  The caller must set the dwSize
        member to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if the next entry of the process list has been copied to the buffer or
    FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the GetLastError
    function if no processes exist or the snapshot does not contain process information.

--*/
{
    PSNAPSHOTSTATE SnapshotBase;
    BOOL    retv = FALSE;
    LARGE_INTEGER SectionOffset;
    SIZE_T ViewSize;
    NTSTATUS Status = 0;

    if (!lppe || lppe->dwSize != sizeof(PROCESSENTRY32W)) {
        BaseSetLastNTError(STATUS_INFO_LENGTH_MISMATCH);
        return FALSE;
    }

    SectionOffset.LowPart = 0;
    SectionOffset.HighPart = 0;
    ViewSize = 0;
    SnapshotBase = 0;

    Status = NtMapViewOfSection(SnapSection,
                NtCurrentProcess(),
                &SnapshotBase,
                0L,
                0L,
                &SectionOffset,
                &ViewSize,
                ViewShare,
                0L,
                PAGE_READWRITE);

    if (!NT_SUCCESS(Status)) {
        BaseSetLastNTError(Status);
        return FALSE;
    }

    if (SnapshotBase->ProcessListIndex < SnapshotBase->ProcessCount) {
        memcpy(lppe,
               (LPPROCESSENTRY32W)((ULONG_PTR)SnapshotBase + (ULONG_PTR)(&SnapshotBase->ProcessListHead[SnapshotBase->ProcessListIndex++])),
               sizeof(PROCESSENTRY32W));
        retv = TRUE;
    } else {
        SetLastError(ERROR_NO_MORE_FILES);
    }

    NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)SnapshotBase);

    return retv;
}

BOOL
WINAPI
Process32Next(
   IN HANDLE SnapSection,
   IN OUT LPPROCESSENTRY32 lppe)
/*++

Routine Description:

    ANSI version of Process32NextW

    Retrieves information about the next Win32 process recorded in a system snapshot.

Arguments:

    SnapSection - Supplies the handle of a snapshot returned from a previous call
        to the CreateToolhelp32Snapshot function.

    lppe - Returns a PROCESSENTRY32 structure.  The caller must set the dwSize
        member to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if the next entry of the process list has been copied to the buffer or
    FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the GetLastError
    function if no processes exist or the snapshot does not contain process information.

--*/
{
    PROCESSENTRY32W pe32w;
    BOOL b;

    if (lppe == NULL || (lppe->dwSize < sizeof(PROCESSENTRY32))) {
        BaseSetLastNTError(STATUS_INFO_LENGTH_MISMATCH);
        return FALSE;
    }

    // Thunk to Process32NextW
    pe32w.dwSize = sizeof(pe32w);
    b = Process32NextW(SnapSection,&pe32w);

    WideCharToMultiByte(CP_ACP, 0,
                        pe32w.szExeFile, -1,
                        lppe->szExeFile, ARRAYSIZE(lppe->szExeFile),
                        0, 0);

    lppe->cntUsage = pe32w.cntUsage;    // meaningless on NT, copy anyway
    lppe->th32ProcessID = pe32w.th32ProcessID;
    lppe->th32DefaultHeapID = pe32w.th32DefaultHeapID;
    lppe->th32ModuleID = pe32w.th32ModuleID;
    lppe->cntThreads = pe32w.cntThreads;
    lppe->th32ParentProcessID = pe32w.th32ParentProcessID;
    lppe->pcPriClassBase = pe32w.pcPriClassBase;
    lppe->dwFlags = pe32w.dwFlags;

    return b;
}


BOOL
WINAPI
Thread32First(
   IN HANDLE SnapSection,
   IN OUT LPTHREADENTRY32 lpte)
/*++

Routine Description:

    Retrieves information about the first thread of any Win32 process
    encountered in a system snapshot.

Arguments:

    SnapSection - Supplies the handle of a snapshot returned from a previous
        call to the CreateToolhelp32Snapshot function.

    lpte - Returns a THREADENTRY32 structure.  The caller must set the dwSize
        member to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if the first entry of the thread list has been copied to the buffer or
    FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the GetLastError
    function if no threads exist or the snapshot does not contain thread information.

--*/
{
    PSNAPSHOTSTATE SnapshotBase;
    LARGE_INTEGER SectionOffset;
    SIZE_T ViewSize;
    NTSTATUS Status = 0;
    BOOL    retv = FALSE;

    if (!lpte || lpte->dwSize != sizeof(THREADENTRY32)) {
        BaseSetLastNTError(STATUS_INFO_LENGTH_MISMATCH);
        return FALSE;
    }

    SectionOffset.LowPart = 0;
    SectionOffset.HighPart = 0;
    ViewSize = 0;
    SnapshotBase = 0;

    Status = NtMapViewOfSection(SnapSection,
                NtCurrentProcess(),
                &SnapshotBase,
                0L,
                0L,
                &SectionOffset,
                &ViewSize,
                ViewShare,
                0L,
                PAGE_READWRITE);

    if (!NT_SUCCESS(Status)) {
        BaseSetLastNTError(Status);
        return FALSE;
    }

    if (SnapshotBase->ThreadCount == 0) {
        memset((PUCHAR)lpte + 4, 0, lpte->dwSize - 4);
        SetLastError(ERROR_NO_MORE_FILES);
    } else {
        memcpy(lpte,
               (LPTHREADENTRY32)((ULONG_PTR)SnapshotBase + (ULONG_PTR)SnapshotBase->ThreadListHead),
               sizeof(THREADENTRY32));
               SnapshotBase->ThreadListIndex = 1;
        retv = TRUE;
    }

    NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)SnapshotBase);

    return(retv);
}


BOOL
WINAPI
Thread32Next(
   IN HANDLE SnapSection,
   IN OUT LPTHREADENTRY32 lpte)
/*++

Routine Description:

    Retrieves information about the next thread of any Win32 process encountered in the
    system memory snapshot.

Arguments:

    SnapSection - Supplies the handle of a snapshot returned from a previous call
        to the CreateToolhelp32Snapshot function.

    lpte - Reeturns a THREADENTRY32 structure.  The caller must set the dwSize
        member to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if the next entry of the thread list has been copied to the buffer or
    FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the GetLastError
    function if no threads exist or the snapshot does not contain thread information.

--*/
{
    PSNAPSHOTSTATE SnapshotBase;
    BOOL    retv = FALSE;
    LARGE_INTEGER SectionOffset;
    SIZE_T ViewSize;
    NTSTATUS Status = 0;

    if (!lpte || lpte->dwSize != sizeof(THREADENTRY32)) {
        BaseSetLastNTError(STATUS_INFO_LENGTH_MISMATCH);
        return FALSE;
    }

    SectionOffset.LowPart = 0;
    SectionOffset.HighPart = 0;
    ViewSize = 0;
    SnapshotBase = 0;

    Status = NtMapViewOfSection(SnapSection,
                NtCurrentProcess(),
                &SnapshotBase,
                0L,
                0L,
                &SectionOffset,
                &ViewSize,
                ViewShare,
                0L,
                PAGE_READWRITE);

    if (!NT_SUCCESS(Status)) {
        BaseSetLastNTError(Status);
        return FALSE;
    }

    if (SnapshotBase->ThreadListIndex < SnapshotBase->ThreadCount) {
        memcpy(lpte,
               (PTHREADENTRY32)((ULONG_PTR)SnapshotBase + (ULONG_PTR)(&SnapshotBase->ThreadListHead[SnapshotBase->ThreadListIndex++])),
               sizeof(THREADENTRY32));
        retv = TRUE;
    } else {
        SetLastError(ERROR_NO_MORE_FILES);
    }

    NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)SnapshotBase);

    return(retv);
}


BOOL
WINAPI
Module32FirstW(
   IN HANDLE SnapSection,
   IN OUT LPMODULEENTRY32W lpme)
/*++

Routine Description:

    Retrieves information about the first module associated with a Win32 process.

Arguments:

    SnapSection - Supplies the handle of a snapshot returned from a previous call
        to the CreateToolhelp32Snapshot function.

    lpme - Returns a buffer containing a MODULEENTRY32W structure.  The caller
        must set the dwSize member to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if the first entry of the module list has been copied to the buffer or
    FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the GetLastError
    function if no modules exist or the snapshot does not contain module information.

--*/
{
    PSNAPSHOTSTATE SnapshotBase;
    LARGE_INTEGER SectionOffset;
    SIZE_T ViewSize;
    NTSTATUS Status = 0;
    BOOL    retv = FALSE;

    if (!lpme || lpme->dwSize != sizeof(MODULEENTRY32W)) {
        BaseSetLastNTError(STATUS_INFO_LENGTH_MISMATCH);
        return FALSE;
    }

    SectionOffset.LowPart = 0;
    SectionOffset.HighPart = 0;
    ViewSize = 0;
    SnapshotBase = 0;

    Status = NtMapViewOfSection(SnapSection,
                NtCurrentProcess(),
                &SnapshotBase,
                0L,
                0L,
                &SectionOffset,
                &ViewSize,
                ViewShare,
                0L,
                PAGE_READWRITE);

    if (!NT_SUCCESS(Status)) {
        BaseSetLastNTError(Status);
        return FALSE;
    }

    if (SnapshotBase->ModuleCount == 0) {
        memset((PUCHAR)lpme + 4, 0, lpme->dwSize - 4);
        SetLastError(ERROR_NO_MORE_FILES);
    } else {
        memcpy(lpme,
               (PMODULEENTRY32W)((ULONG_PTR)SnapshotBase + (ULONG_PTR)SnapshotBase->ModuleListHead),
               sizeof(MODULEENTRY32W));
        SnapshotBase->ModuleListIndex = 1;
        retv = TRUE;
    }

    NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)SnapshotBase);

    return retv;
}

BOOL
WINAPI
Module32First(
   IN HANDLE SnapSection,
   IN OUT LPMODULEENTRY32 lpme)
/*++

Routine Description:

    ANSI version of Module32FirstW.

    Retrieves information about the first module associated with a Win32 process.

Arguments:

    SnapSection - Supplies the handle of the snapshot returned from a previous call
        to the CreateToolhelp32Snapshot function.

    lpme - Returns a buffer containing a MODULEENTRY32 structure.  The caller
        must set the dwSize member to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if the first entry of the module list has been copied to the buffer or
    FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the GetLastError
    function if no modules exist or the snapshot does not contain module information.

--*/
{
    MODULEENTRY32W me32w;
    DWORD dwSizeToCopy;
    BOOL b;

    if (lpme == NULL || (lpme->dwSize < sizeof(MODULEENTRY32))) {
         SetLastError(ERROR_INVALID_PARAMETER);
         return FALSE;
    }

    // Thunk to Module32FirstW
    me32w.dwSize = sizeof(me32w);
    b = Module32FirstW(SnapSection,&me32w);

    WideCharToMultiByte(CP_ACP, 0,
                        me32w.szExePath, -1,
                        lpme->szExePath, ARRAYSIZE(lpme->szExePath),
                        0, 0);

    WideCharToMultiByte(CP_ACP, 0,
                        me32w.szModule, -1,
                        lpme->szModule, ARRAYSIZE(lpme->szModule),
                        0, 0);

    lpme->th32ModuleID  = me32w.th32ModuleID;
    lpme->th32ProcessID = me32w.th32ProcessID;
    lpme->GlblcntUsage  = me32w.GlblcntUsage;
    lpme->ProccntUsage  = me32w.ProccntUsage;
    lpme->modBaseAddr   = me32w.modBaseAddr;
    lpme->modBaseSize   = me32w.modBaseSize;
    lpme->hModule       = me32w.hModule;

    return b;
}


BOOL
WINAPI
Module32NextW(
   IN HANDLE SnapSection,
   IN OUT LPMODULEENTRY32W lpme)
/*++

Routine Description:

    Retrieves information about the next module associated with a Win32 process or thread.

Arguments:

    SnapSection - Supplies the handle of a snapshot returned from a previous call
        to CreateToolhelp32Snapshot.

    lpme - Returns a MODULEENTRY32W structure.  The calling application must set
        the dwSize member to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if the next entry of the module list has been copied to the buffer or
    FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the GetLastError
    function if no more modules exist.

--*/
{
    PSNAPSHOTSTATE SnapshotBase;
    BOOL    retv = FALSE;
    LARGE_INTEGER SectionOffset;
    SIZE_T ViewSize;
    NTSTATUS Status = 0;

    if (!lpme || lpme->dwSize != sizeof(MODULEENTRY32W)) {
        BaseSetLastNTError(STATUS_INFO_LENGTH_MISMATCH);
        return FALSE;
    }

    SectionOffset.LowPart = 0;
    SectionOffset.HighPart = 0;
    ViewSize = 0;
    SnapshotBase = 0;

    Status = NtMapViewOfSection(SnapSection,
                NtCurrentProcess(),
                &SnapshotBase,
                0L,
                0L,
                &SectionOffset,
                &ViewSize,
                ViewShare,
                0L,
                PAGE_READWRITE);

    if (!NT_SUCCESS(Status)) {
        BaseSetLastNTError(Status);
        return FALSE;
    }

    if (SnapshotBase->ModuleListIndex < SnapshotBase->ModuleCount) {
        memcpy(lpme,
               (LPMODULEENTRY32W)((ULONG_PTR)SnapshotBase + (ULONG_PTR)(&SnapshotBase->ModuleListHead[SnapshotBase->ModuleListIndex++])),
               sizeof(MODULEENTRY32W));
        retv = TRUE;
    } else {
        SetLastError(ERROR_NO_MORE_FILES);
    }

    NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)SnapshotBase);

    return(retv);
}


BOOL
WINAPI
Module32Next(
   IN HANDLE SnapSection,
   IN OUT LPMODULEENTRY32 lpme)
/*++

Routine Description:

    ANSI version of Module32NextW.

    Retrieves information about the next module associated with a Win32 process or thread.

Arguments:

    SnapSection - Supplies the handle of a snapshot returned from a previous call
        to CreateToolhelp32Snapshot.

    lpme - Returns a MODULEENTRY32 structure.  The calling application must set
        the dwSize member to the size, in bytes, of the structure.

Return Value:

    Returns TRUE if the next entry of the module list has been copied to the buffer or
    FALSE otherwise. The ERROR_NO_MORE_FILES error value is returned by the GetLastError
    function if no more modules exist.

--*/
{
    MODULEENTRY32W me32w;
    BOOL b;

    if (lpme == NULL || (lpme->dwSize < sizeof(MODULEENTRY32))) {
         SetLastError(ERROR_INVALID_DATA);
         return FALSE;
    }

    // Thunk to Module32NextW
    me32w.dwSize = sizeof(me32w);
    b = Module32NextW(SnapSection,&me32w);

    WideCharToMultiByte(CP_ACP, 0,
                        me32w.szModule, -1,
                        lpme->szModule, ARRAYSIZE(lpme->szModule),
                        0, 0);

    WideCharToMultiByte(CP_ACP, 0,
                        me32w.szExePath, -1,
                        lpme->szExePath, ARRAYSIZE(lpme->szExePath),
                        0, 0);

    lpme->th32ModuleID  = me32w.th32ModuleID;
    lpme->GlblcntUsage  = me32w.GlblcntUsage;
    lpme->ProccntUsage  = me32w.ProccntUsage;
    lpme->modBaseAddr   = me32w.modBaseAddr;
    lpme->modBaseSize   = me32w.modBaseSize;
    lpme->hModule       = me32w.hModule;

    return b;
}

NTSTATUS
ThpCreateRawSnap(
    IN DWORD dwFlags,
    IN DWORD th32ProcessID,
    PUCHAR *RawProcess,
    PRTL_DEBUG_INFORMATION *RawModule,
    PRTL_DEBUG_INFORMATION *RawDebugInfo)
/*++

Routine Description:

    This function gets raw snapshots for the data types specified by dwFlags.

Arguments:

    th32ProcessID - Supplies a WIN32 process ID.  See CreateToolhelp32Snapshot
        for full description.

    dwFlags - Supplies switches requesting various data.  See
        CreateToolhelp32Snapshot for full description

Return Value:

    NTSTATUS as appropriate

--*/
{
    NTSTATUS Status = 0;
    ULONG BufferSize = BUFFER_SIZE;
    SIZE_T stBufferSize = BUFFER_SIZE;

    //
    // get process/thread/module/heap info
    //

    *RawProcess = NULL;
    *RawModule = NULL;
    *RawDebugInfo = NULL;

    if((dwFlags & TH32CS_SNAPPROCESS) || (dwFlags & TH32CS_SNAPTHREAD)){
        do {
            try {
                stBufferSize = BufferSize;
                Status = NtAllocateVirtualMemory(NtCurrentProcess(),
                                                 RawProcess,
                                                 0,
                                                 &stBufferSize,
                                                 MEM_COMMIT,
                                                 PAGE_READWRITE);
            }
            except( EXCEPTION_EXECUTE_HANDLER ) {
                Status = GetExceptionCode();
            }

            if (!NT_SUCCESS(Status)) {
                break;
            }

            BufferSize = (ULONG)stBufferSize;
            //
            // get all of the status information */
            //
            Status = NtQuerySystemInformation(SystemProcessInformation,
                      *RawProcess,
                      BufferSize,
                      NULL);

            if (Status == STATUS_INFO_LENGTH_MISMATCH)   {
                NtFreeVirtualMemory(NtCurrentProcess(),
                                    RawProcess,
                                    &stBufferSize,
                                    MEM_RELEASE);
                *RawProcess = NULL;
                BufferSize += 8192;
            }

        } while(Status == STATUS_INFO_LENGTH_MISMATCH);
    }

    //
    // get module information
    //

    if((dwFlags & TH32CS_SNAPMODULE) || (dwFlags & TH32CS_SNAPMODULE32))
    {
        if (NT_SUCCESS(Status))    {
            *RawModule = RtlCreateQueryDebugBuffer(0, FALSE);
            if (!*RawModule) {
                Status = STATUS_UNSUCCESSFUL;
            }
        }

        if (NT_SUCCESS(Status)) {
            Status = RtlQueryProcessDebugInformation((HANDLE)LongToHandle(th32ProcessID),
                                                     RTL_QUERY_PROCESS_NONINVASIVE |
                                                     ((dwFlags & TH32CS_SNAPMODULE) ? RTL_QUERY_PROCESS_MODULES : 0) |
                                                     ((dwFlags & TH32CS_SNAPMODULE32) ? RTL_QUERY_PROCESS_MODULES32 : 0),
                                                      *RawModule);
        }
    }

    //
    // get the heap summary information for the specified process */
    //

    if (dwFlags & TH32CS_SNAPHEAPLIST)   {
        if (NT_SUCCESS(Status))    {

            *RawDebugInfo = RtlCreateQueryDebugBuffer(0, FALSE);
            if (!*RawDebugInfo) {
                Status = STATUS_UNSUCCESSFUL;
            }
        }
        if (NT_SUCCESS(Status)) {
            Status = RtlQueryProcessDebugInformation((HANDLE)LongToHandle(th32ProcessID),
                                                      RTL_QUERY_PROCESS_HEAP_SUMMARY,
                                                      *RawDebugInfo);
        }
    }


    if (!NT_SUCCESS(Status))    {
        if (*RawProcess) {
            SIZE_T Size = 0;
            NtFreeVirtualMemory(NtCurrentProcess(),
                                RawProcess,
                                &Size,
                                MEM_RELEASE);
            *RawProcess = NULL;
        }
        if (*RawModule) {
            RtlDestroyQueryDebugBuffer(*RawModule);
            *RawModule = NULL;
        }
        if (*RawDebugInfo) {
            RtlDestroyQueryDebugBuffer(*RawDebugInfo);
            *RawDebugInfo = NULL;
        }
    }

    return Status;
}


NTSTATUS
ThpAllocateSnapshotSection(
    OUT PHANDLE SnapSection,
    IN DWORD dwFlags,
    IN DWORD th32ProcessID,
    PUCHAR RawProcess,
    PRTL_DEBUG_INFORMATION RawModule,
    PRTL_DEBUG_INFORMATION RawDebugInfo)
/*++

Routine Description:

    This function calculates the size of the snapshot and allocates a
    file mapping object for it in the pagefile.
    Also, initialize snapshot information in the header

Arguments:

    th32ProcessID - Supplies a WIN32 process ID.  See CreateToolhelp32Snapshot
        for full description.

    dwFlags - Supplies switches describing requested data.  See
        CreateToolhelp32Snapshot for full description.

    th32ProcessID -

    RawProcess -

    RawDebugInfo -

Return Value:

    Handle to to the mapping object if successful, -1 otherwise

--*/
{
    NTSTATUS Status = 0;
    PSNAPSHOTSTATE SnapshotBase;
    SECURITY_ATTRIBUTES SecurityAttributes;
    LPSECURITY_ATTRIBUTES lpSecurityAttributes;
    ULONG SnapShotSize;
    ULONG Offset1;
    PSYSTEM_PROCESS_INFORMATION ProcessInfo;
    OBJECT_ATTRIBUTES Obja;
    POBJECT_ATTRIBUTES pObja;
    LARGE_INTEGER SectionOffset;
    LARGE_INTEGER SectionSize;
    SIZE_T ViewSize;
    SIZE_T Size;
    ULONG ProcessCount = 0;
    ULONG HeapListCount = 0;
    ULONG ModuleCount = 0;
    ULONG ThreadCount = 0;

    SnapShotSize = sizeof(SNAPSHOTSTATE);

    Offset1 = 0;

    //
    // calculate the required snapshot size
    //

    if ((dwFlags & TH32CS_SNAPPROCESS) || (dwFlags & TH32CS_SNAPTHREAD)) {
        do {
            ProcessCount++;
            ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&RawProcess[Offset1];
            Offset1 += ProcessInfo->NextEntryOffset;
            ThreadCount += ProcessInfo->NumberOfThreads;
        } while (ProcessInfo->NextEntryOffset != 0);

        if (dwFlags & TH32CS_SNAPPROCESS) {
            SnapShotSize += ProcessCount * sizeof(PROCESSENTRY32W);
        }
        if (dwFlags & TH32CS_SNAPTHREAD) {
            SnapShotSize += ThreadCount * sizeof(THREADENTRY32);
        }
    }

    if (dwFlags & TH32CS_SNAPMODULE) {
        SnapShotSize += RawModule->Modules->NumberOfModules * sizeof(MODULEENTRY32W);
        ModuleCount = RawModule->Modules->NumberOfModules;
    }

    if (dwFlags & TH32CS_SNAPHEAPLIST)   {
        SnapShotSize += RawDebugInfo->Heaps->NumberOfHeaps * sizeof(HEAPLIST32);
        HeapListCount = RawDebugInfo->Heaps->NumberOfHeaps;
    }

    //
    // Create a security object if needed
    //

    if (dwFlags & TH32CS_INHERIT) {
        SecurityAttributes.lpSecurityDescriptor = NULL;
        SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
        SecurityAttributes.bInheritHandle = TRUE;
        lpSecurityAttributes = &SecurityAttributes;
    } else {
        lpSecurityAttributes = NULL;
    }

    //
    // create a pagefile section to contain the snapshot
    //

    pObja = BaseFormatObjectAttributes(&Obja, lpSecurityAttributes, NULL);

    SectionSize.LowPart = SnapShotSize;
    SectionSize.HighPart = 0;

    Status = NtCreateSection(SnapSection,
                STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_READ | SECTION_MAP_WRITE,
                pObja,
                &SectionSize,
                PAGE_READWRITE,
                SEC_COMMIT,
                NULL);

    if ( !NT_SUCCESS(Status) ) {
        return Status;
    }

    SectionOffset.LowPart = 0;
    SectionOffset.HighPart = 0;
    ViewSize = 0;
    SnapshotBase = 0;

    Status = NtMapViewOfSection(*SnapSection,
                NtCurrentProcess(),
                &SnapshotBase,
                0L,
                0L,
                &SectionOffset,
                &ViewSize,
                ViewShare,
                0L,
                PAGE_READWRITE);

    //
    // free all memory if failure
    //

    if ( !NT_SUCCESS(Status) ) {
        CloseHandle(*SnapSection);

        if ((dwFlags & TH32CS_SNAPTHREAD) || (dwFlags & TH32CS_SNAPPROCESS)){

        Size = 0;
        NtFreeVirtualMemory(NtCurrentProcess(),
                            &RawProcess,
                            &Size,
                            MEM_RELEASE);

        }

        if (dwFlags & TH32CS_SNAPPROCESS) {
            RtlDestroyQueryDebugBuffer(RawModule);
        }

        if (dwFlags & TH32CS_SNAPHEAPLIST) {
            RtlDestroyQueryDebugBuffer(RawDebugInfo);
        }

        return Status;
    }

    SnapshotBase->ProcessCount  = ProcessCount;
    SnapshotBase->HeapListCount = HeapListCount;
    SnapshotBase->ModuleCount   = ModuleCount;
    SnapshotBase->ThreadCount   = ThreadCount;

    //
    // return resources
    //

    NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)SnapshotBase);

    return STATUS_SUCCESS;
}


NTSTATUS
ThpCopyAnsiToUnicode(
    PWCHAR Dest,
    PUCHAR Src,
    USHORT Max)
{
    UNICODE_STRING UnicodeString;
    ANSI_STRING AnsiString;

    UnicodeString.Buffer = Dest;
    UnicodeString.MaximumLength = Max;

    RtlInitAnsiString(&AnsiString, Src);

    return RtlAnsiStringToUnicodeString( &UnicodeString, &AnsiString, FALSE );
}


NTSTATUS
ThpProcessToSnap(
    IN DWORD dwFlags,
    IN DWORD th32ProcessID,
    IN HANDLE SnapSection,
    PUCHAR RawProcess,
    PRTL_DEBUG_INFORMATION RawModule,
    PRTL_DEBUG_INFORMATION RawDebugInfo)
/*++

Routine Description:

    This function processes the data in the raw dumps specified by dwFlage into
    a mapped file.

Arguments:

    dwFlags - Supplies switches describing the data requested.  See
        CreateToolhelp32Snapshot for full description.

    th32ProcessID - Supplies a WIN32 process ID. See CreateToolhelp32Snapshot
        for full description.

    SnapSection - Supplies handle to section allocated by ThpAllocateSnapshotSection.

    RawProcess -

    RawDebugInfo -

Return Value:

    TRUE if successful, FALSE if there was a problem encountered

--*/
{
    PSNAPSHOTSTATE SnapshotBase;
    PUCHAR BufferWriteAddr;  /* working pointer into out process data - usually points at end */
    LARGE_INTEGER SectionOffset;
    SIZE_T ViewSize;
    NTSTATUS Status = 0;
    SIZE_T Size;

    SectionOffset.LowPart = 0;
    SectionOffset.HighPart = 0;
    ViewSize = 0;
    SnapshotBase = 0;

    Status = NtMapViewOfSection(SnapSection,
                NtCurrentProcess(),
                &SnapshotBase,
                0L,
                0L,
                &SectionOffset,
                &ViewSize,
                ViewShare,
                0L,
                PAGE_READWRITE);

    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    BufferWriteAddr = &SnapshotBase->DataBegin;

    //
    // write heap list to snapshot
    // some of this code adapted from dh.c
    //
    if (dwFlags & TH32CS_SNAPHEAPLIST) {
        ULONG HeapListCount = 0;
        ULONG HeapEntryCount = 0;
        LPHEAPLIST32 pHeapList;

        SnapshotBase->HeapListHead = (PHEAPLIST32)(BufferWriteAddr - (PUCHAR)SnapshotBase);
        pHeapList = (LPHEAPLIST32)BufferWriteAddr;

        // heaplist
        for (HeapListCount = 0; HeapListCount < SnapshotBase->HeapListCount; HeapListCount++){
            pHeapList->dwSize = sizeof(HEAPLIST32);
            pHeapList->th32ProcessID = th32ProcessID;
            /* handle = baseaddress = ID we will use internally */
            pHeapList->th32HeapID = (ULONG_PTR)RawDebugInfo->Heaps->Heaps[HeapListCount].BaseAddress;
            pHeapList->dwFlags = RawDebugInfo->Heaps->Heaps[HeapListCount].Flags;
            ++pHeapList;
        }
        // update the pointer to the write area
        BufferWriteAddr = (PCHAR)(BufferWriteAddr + HeapListCount * sizeof(HEAPLIST32));
        RtlDestroyQueryDebugBuffer(RawDebugInfo);
    }

    //
    // write module list to snapshot
    //
    if (dwFlags & TH32CS_SNAPMODULE) {
        LPMODULEENTRY32W pModule;
        ULONG Offset1 = 0;
        ULONG mCount = 0;
        PRTL_PROCESS_MODULE_INFORMATION ModuleInfo;

        SnapshotBase->ModuleListHead = (PMODULEENTRY32W)(BufferWriteAddr - (PUCHAR)SnapshotBase);

        //
        // get module info from buffer
        //

        pModule = (LPMODULEENTRY32W)(BufferWriteAddr);
        ModuleInfo = &RawModule->Modules->Modules[ 0 ];
        for (mCount = 0; mCount < RawModule->Modules->NumberOfModules; mCount++)   {

            pModule->dwSize = sizeof(MODULEENTRY32W);

            pModule->th32ProcessID = th32ProcessID;

            //
            // base == handle
            //

            pModule->hModule = ModuleInfo->ImageBase;

            //
            // Base address of module in th32ProcessID's context
            //

            pModule->modBaseAddr = ModuleInfo->ImageBase;

            //
            // Path
            //

            ThpCopyAnsiToUnicode(pModule->szExePath,
                                 ModuleInfo->FullPathName,
                                 sizeof(pModule->szExePath));

            //
            // module name
            //

            ThpCopyAnsiToUnicode(pModule->szModule,
                                 &ModuleInfo->FullPathName[ModuleInfo->OffsetToFileName],
                                 sizeof(pModule->szModule));

            //
            // Size in bytes of module starting at modBaseAddr
            //

            pModule->modBaseSize = ModuleInfo->ImageSize;


            //
            // these are meaningless on NT
            // but some apps may care... Gruntz (bugid 327009)
            // was failing because th32ModuleID was 0, so
            // now we stick in the address of the Module descriptor
            //
            // However it turns out that a pointer doesn't fit in a DWORD,
            // so we stick in the value 1 instead.
            //

            pModule->th32ModuleID = 1;
            pModule->GlblcntUsage = ModuleInfo->LoadCount;  // will be 0xffff
            pModule->ProccntUsage = ModuleInfo->LoadCount;  // will be 0xffff

            ++ModuleInfo;
            ++pModule;
        }

        //
        // update the pointer to the write area
        //
        BufferWriteAddr = (PCHAR)(BufferWriteAddr + mCount * sizeof(MODULEENTRY32W));
        RtlDestroyQueryDebugBuffer(RawModule);
    }

    //
    // write process list to snapshot
    //
    if (dwFlags & TH32CS_SNAPPROCESS) {

        PSYSTEM_PROCESS_INFORMATION ProcessInfo;
        LPPROCESSENTRY32W pEntry;
        ULONG cProcess = 0;
        ULONG Offset1 = 0;

        SnapshotBase->ProcessListHead = (PPROCESSENTRY32W)(BufferWriteAddr - (PUCHAR)SnapshotBase);
        pEntry = (LPPROCESSENTRY32W)(BufferWriteAddr + cProcess * sizeof(PROCESSENTRY32W));

        do {
            /* get process info from buffer */
            ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&RawProcess[Offset1];

            pEntry->dwSize              = sizeof(PROCESSENTRY32W);
            pEntry->th32ProcessID       = HandleToUlong(ProcessInfo->UniqueProcessId);
            pEntry->pcPriClassBase      = ProcessInfo->BasePriority;
            pEntry->cntThreads          = ProcessInfo->NumberOfThreads;
            pEntry->th32ParentProcessID = HandleToUlong(ProcessInfo->InheritedFromUniqueProcessId);
            pEntry->cntUsage            = 0;
            pEntry->th32DefaultHeapID   = 0;
            pEntry->th32ModuleID        = 0;
            pEntry->dwFlags             = 0;

            // Path
            if (ProcessInfo->ImageName.Buffer == NULL) {
                lstrcpyW(pEntry->szExeFile, L"[System Process]");
            } else {
                if (ProcessInfo->ImageName.Length >= ARRAYSIZE(pEntry->szExeFile)) {
                    ProcessInfo->ImageName.Length = ARRAYSIZE(pEntry->szExeFile)-1;
                }
                memcpy(pEntry->szExeFile, ProcessInfo->ImageName.Buffer, ProcessInfo->ImageName.Length);
                pEntry->szExeFile[ProcessInfo->ImageName.Length] = TEXT('\0');
            }

            Offset1 += ProcessInfo->NextEntryOffset;
            ++cProcess;
            ++pEntry;

        } while (ProcessInfo->NextEntryOffset != 0);

        // update the pointer to the write area
        BufferWriteAddr = (PCHAR)(BufferWriteAddr + cProcess * sizeof(PROCESSENTRY32W));
    }

    //
    // write thread list to snapshot
    //

    if (dwFlags & TH32CS_SNAPTHREAD) {
        PSYSTEM_PROCESS_INFORMATION ProcessInfo;
        PSYSTEM_THREAD_INFORMATION ThreadInfo;
        LPTHREADENTRY32 tEntry;
        ULONG Offset1 = 0;
        ULONG cThread = 0;

        SnapshotBase->ThreadListHead = (PTHREADENTRY32)(BufferWriteAddr - (PUCHAR)SnapshotBase);
        tEntry = (LPTHREADENTRY32)(BufferWriteAddr + cThread * sizeof(THREADENTRY32));

        do {
            ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&RawProcess[Offset1];
            ThreadInfo = (PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);

            for (cThread = 0; cThread < ProcessInfo->NumberOfThreads; cThread++) {

                tEntry->dwSize              = sizeof(THREADENTRY32);
                tEntry->th32ThreadID        = HandleToUlong(ThreadInfo->ClientId.UniqueThread);
                tEntry->th32OwnerProcessID  = HandleToUlong(ThreadInfo->ClientId.UniqueProcess);
                tEntry->tpBasePri           = ThreadInfo->BasePriority;
                tEntry->tpDeltaPri          = 0;
                tEntry->cntUsage            = 0;
                tEntry->dwFlags             = 0;

            ++ThreadInfo;
            ++tEntry;
            }

            Offset1 += ProcessInfo->NextEntryOffset;

        } while (ProcessInfo->NextEntryOffset != 0);

        BufferWriteAddr = (PUCHAR)(BufferWriteAddr + cThread * sizeof(THREADENTRY32)); // update the pointer to the write area
    }

    if ((dwFlags & TH32CS_SNAPTHREAD) || (dwFlags & TH32CS_SNAPPROCESS)){

        Size = 0;
        NtFreeVirtualMemory(NtCurrentProcess(),
                            &RawProcess,
                            &Size,
                            MEM_RELEASE);
    }

    NtUnmapViewOfSection(NtCurrentProcess(), (PVOID)SnapshotBase);

    return STATUS_SUCCESS;
}
