#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright (c) 2009-2014, Mario Vilas
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#     * Redistributions of source code must retain the above copyright notice,
#       this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above copyright
#       notice,this list of conditions and the following disclaimer in the
#       documentation and/or other materials provided with the distribution.
#     * Neither the name of the copyright holder nor the names of its
#       contributors may be used to endorse or promote products derived from
#       this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""
Wrapper for dbghelp.dll in ctypes.
"""

__revision__ = "$Id$"

from winappdbg.win32.defines import *
from winappdbg.win32.version import *
from winappdbg.win32.kernel32 import *

# DbgHelp versions and features list:
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms679294(v=vs.85).aspx

# ------------------------------------------------------------------------------
# Tries to load the newest version of dbghelp.dll if available.


def _load_latest_dbghelp_dll():
    from os import getenv
    from os.path import join, exists

    program_files_location = getenv("ProgramFiles")
    if not program_files_location:
        program_files_location = "C:\\Program Files"

    program_files_x86_location = getenv("ProgramFiles(x86)")

    if arch == ARCH_AMD64:
        if wow64:
            pathname = join(program_files_x86_location or program_files_location, "Debugging Tools for Windows (x86)", "dbghelp.dll")
        else:
            pathname = join(program_files_location, "Debugging Tools for Windows (x64)", "dbghelp.dll")
    elif arch == ARCH_I386:
        pathname = join(program_files_location, "Debugging Tools for Windows (x86)", "dbghelp.dll")
    else:
        pathname = None

    if pathname and exists(pathname):
        try:
            _dbghelp = ctypes.windll.LoadLibrary(pathname)
            ctypes.windll.dbghelp = _dbghelp
        except Exception:
            pass


_load_latest_dbghelp_dll()

# Recover the old binding of the "os" symbol.
# XXX FIXME not sure if I really need to do this!
##from version import os

# ------------------------------------------------------------------------------

# ==============================================================================
# This is used later on to calculate the list of exported symbols.
_all = None
_all = set(vars().keys())
# ==============================================================================

# SymGetHomeDirectory "type" values
hdBase = 0
hdSym = 1
hdSrc = 2

UNDNAME_32_BIT_DECODE = 0x0800
UNDNAME_COMPLETE = 0x0000
UNDNAME_NAME_ONLY = 0x1000
UNDNAME_NO_ACCESS_SPECIFIERS = 0x0080
UNDNAME_NO_ALLOCATION_LANGUAGE = 0x0010
UNDNAME_NO_ALLOCATION_MODEL = 0x0008
UNDNAME_NO_ARGUMENTS = 0x2000
UNDNAME_NO_CV_THISTYPE = 0x0040
UNDNAME_NO_FUNCTION_RETURNS = 0x0004
UNDNAME_NO_LEADING_UNDERSCORES = 0x0001
UNDNAME_NO_MEMBER_TYPE = 0x0200
UNDNAME_NO_MS_KEYWORDS = 0x0002
UNDNAME_NO_MS_THISTYPE = 0x0020
UNDNAME_NO_RETURN_UDT_MODEL = 0x0400
UNDNAME_NO_SPECIAL_SYMS = 0x4000
UNDNAME_NO_THISTYPE = 0x0060
UNDNAME_NO_THROW_SIGNATURES = 0x0100

# --- IMAGEHLP_MODULE structure and related ------------------------------------

SYMOPT_ALLOW_ABSOLUTE_SYMBOLS = 0x00000800
SYMOPT_ALLOW_ZERO_ADDRESS = 0x01000000
SYMOPT_AUTO_PUBLICS = 0x00010000
SYMOPT_CASE_INSENSITIVE = 0x00000001
SYMOPT_DEBUG = 0x80000000
SYMOPT_DEFERRED_LOADS = 0x00000004
SYMOPT_DISABLE_SYMSRV_AUTODETECT = 0x02000000
SYMOPT_EXACT_SYMBOLS = 0x00000400
SYMOPT_FAIL_CRITICAL_ERRORS = 0x00000200
SYMOPT_FAVOR_COMPRESSED = 0x00800000
SYMOPT_FLAT_DIRECTORY = 0x00400000
SYMOPT_IGNORE_CVREC = 0x00000080
SYMOPT_IGNORE_IMAGEDIR = 0x00200000
SYMOPT_IGNORE_NT_SYMPATH = 0x00001000
SYMOPT_INCLUDE_32BIT_MODULES = 0x00002000
SYMOPT_LOAD_ANYTHING = 0x00000040
SYMOPT_LOAD_LINES = 0x00000010
SYMOPT_NO_CPP = 0x00000008
SYMOPT_NO_IMAGE_SEARCH = 0x00020000
SYMOPT_NO_PROMPTS = 0x00080000
SYMOPT_NO_PUBLICS = 0x00008000
SYMOPT_NO_UNQUALIFIED_LOADS = 0x00000100
SYMOPT_OVERWRITE = 0x00100000
SYMOPT_PUBLICS_ONLY = 0x00004000
SYMOPT_SECURE = 0x00040000
SYMOPT_UNDNAME = 0x00000002

##SSRVOPT_DWORD
##SSRVOPT_DWORDPTR
##SSRVOPT_GUIDPTR
##
##SSRVOPT_CALLBACK
##SSRVOPT_DOWNSTREAM_STORE
##SSRVOPT_FLAT_DEFAULT_STORE
##SSRVOPT_FAVOR_COMPRESSED
##SSRVOPT_NOCOPY
##SSRVOPT_OVERWRITE
##SSRVOPT_PARAMTYPE
##SSRVOPT_PARENTWIN
##SSRVOPT_PROXY
##SSRVOPT_RESET
##SSRVOPT_SECURE
##SSRVOPT_SETCONTEXT
##SSRVOPT_TRACE
##SSRVOPT_UNATTENDED

#    typedef enum
#    {
#        SymNone = 0,
#        SymCoff,
#        SymCv,
#        SymPdb,
#        SymExport,
#        SymDeferred,
#        SymSym,
#        SymDia,
#        SymVirtual,
#        NumSymTypes
#    } SYM_TYPE;
SymNone = 0
SymCoff = 1
SymCv = 2
SymPdb = 3
SymExport = 4
SymDeferred = 5
SymSym = 6
SymDia = 7
SymVirtual = 8
NumSymTypes = 9

#    typedef struct _IMAGEHLP_MODULE64 {
#      DWORD    SizeOfStruct;
#      DWORD64  BaseOfImage;
#      DWORD    ImageSize;
#      DWORD    TimeDateStamp;
#      DWORD    CheckSum;
#      DWORD    NumSyms;
#      SYM_TYPE SymType;
#      TCHAR    ModuleName[32];
#      TCHAR    ImageName[256];
#      TCHAR    LoadedImageName[256];
#      TCHAR    LoadedPdbName[256];
#      DWORD    CVSig;
#      TCHAR    CVData[MAX_PATH*3];
#      DWORD    PdbSig;
#      GUID     PdbSig70;
#      DWORD    PdbAge;
#      BOOL     PdbUnmatched;
#      BOOL     DbgUnmatched;
#      BOOL     LineNumbers;
#      BOOL     GlobalSymbols;
#      BOOL     TypeInfo;
#      BOOL     SourceIndexed;
#      BOOL     Publics;
#    } IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64;


class IMAGEHLP_MODULE(Structure):
    _fields_ = [
        ("SizeOfStruct", DWORD),
        ("BaseOfImage", DWORD),
        ("ImageSize", DWORD),
        ("TimeDateStamp", DWORD),
        ("CheckSum", DWORD),
        ("NumSyms", DWORD),
        ("SymType", DWORD),  # SYM_TYPE
        ("ModuleName", CHAR * 32),
        ("ImageName", CHAR * 256),
        ("LoadedImageName", CHAR * 256),
    ]


PIMAGEHLP_MODULE = POINTER(IMAGEHLP_MODULE)


class IMAGEHLP_MODULE64(Structure):
    _fields_ = [
        ("SizeOfStruct", DWORD),
        ("BaseOfImage", DWORD64),
        ("ImageSize", DWORD),
        ("TimeDateStamp", DWORD),
        ("CheckSum", DWORD),
        ("NumSyms", DWORD),
        ("SymType", DWORD),  # SYM_TYPE
        ("ModuleName", CHAR * 32),
        ("ImageName", CHAR * 256),
        ("LoadedImageName", CHAR * 256),
        ("LoadedPdbName", CHAR * 256),
        ("CVSig", DWORD),
        ("CVData", CHAR * (MAX_PATH * 3)),
        ("PdbSig", DWORD),
        ("PdbSig70", GUID),
        ("PdbAge", DWORD),
        ("PdbUnmatched", BOOL),
        ("DbgUnmatched", BOOL),
        ("LineNumbers", BOOL),
        ("GlobalSymbols", BOOL),
        ("TypeInfo", BOOL),
        ("SourceIndexed", BOOL),
        ("Publics", BOOL),
    ]


PIMAGEHLP_MODULE64 = POINTER(IMAGEHLP_MODULE64)


class IMAGEHLP_MODULEW(Structure):
    _fields_ = [
        ("SizeOfStruct", DWORD),
        ("BaseOfImage", DWORD),
        ("ImageSize", DWORD),
        ("TimeDateStamp", DWORD),
        ("CheckSum", DWORD),
        ("NumSyms", DWORD),
        ("SymType", DWORD),  # SYM_TYPE
        ("ModuleName", WCHAR * 32),
        ("ImageName", WCHAR * 256),
        ("LoadedImageName", WCHAR * 256),
    ]


PIMAGEHLP_MODULEW = POINTER(IMAGEHLP_MODULEW)


class IMAGEHLP_MODULEW64(Structure):
    _fields_ = [
        ("SizeOfStruct", DWORD),
        ("BaseOfImage", DWORD64),
        ("ImageSize", DWORD),
        ("TimeDateStamp", DWORD),
        ("CheckSum", DWORD),
        ("NumSyms", DWORD),
        ("SymType", DWORD),  # SYM_TYPE
        ("ModuleName", WCHAR * 32),
        ("ImageName", WCHAR * 256),
        ("LoadedImageName", WCHAR * 256),
        ("LoadedPdbName", WCHAR * 256),
        ("CVSig", DWORD),
        ("CVData", WCHAR * (MAX_PATH * 3)),
        ("PdbSig", DWORD),
        ("PdbSig70", GUID),
        ("PdbAge", DWORD),
        ("PdbUnmatched", BOOL),
        ("DbgUnmatched", BOOL),
        ("LineNumbers", BOOL),
        ("GlobalSymbols", BOOL),
        ("TypeInfo", BOOL),
        ("SourceIndexed", BOOL),
        ("Publics", BOOL),
    ]


PIMAGEHLP_MODULEW64 = POINTER(IMAGEHLP_MODULEW64)

# --- dbghelp.dll --------------------------------------------------------------

# XXX the ANSI versions of these functions don't end in "A" as expected!


# BOOL WINAPI MakeSureDirectoryPathExists(
#   _In_  PCSTR DirPath
# );
def MakeSureDirectoryPathExistsA(DirPath):
    _MakeSureDirectoryPathExists = windll.dbghelp.MakeSureDirectoryPathExists
    _MakeSureDirectoryPathExists.argtypes = [LPSTR]
    _MakeSureDirectoryPathExists.restype = bool
    _MakeSureDirectoryPathExists.errcheck = RaiseIfZero
    return _MakeSureDirectoryPathExists(DirPath)


MakeSureDirectoryPathExistsW = MakeWideVersion(MakeSureDirectoryPathExistsA)
MakeSureDirectoryPathExists = GuessStringType(MakeSureDirectoryPathExistsA, MakeSureDirectoryPathExistsW)


# BOOL WINAPI SymInitialize(
#   __in      HANDLE hProcess,
#   __in_opt  PCTSTR UserSearchPath,
#   __in      BOOL fInvadeProcess
# );
def SymInitializeA(hProcess, UserSearchPath=None, fInvadeProcess=False):
    _SymInitialize = windll.dbghelp.SymInitialize
    _SymInitialize.argtypes = [HANDLE, LPSTR, BOOL]
    _SymInitialize.restype = bool
    _SymInitialize.errcheck = RaiseIfZero
    if not UserSearchPath:
        UserSearchPath = None
    _SymInitialize(hProcess, UserSearchPath, fInvadeProcess)


SymInitializeW = MakeWideVersion(SymInitializeA)
SymInitialize = GuessStringType(SymInitializeA, SymInitializeW)


# BOOL WINAPI SymCleanup(
#   __in  HANDLE hProcess
# );
def SymCleanup(hProcess):
    _SymCleanup = windll.dbghelp.SymCleanup
    _SymCleanup.argtypes = [HANDLE]
    _SymCleanup.restype = bool
    _SymCleanup.errcheck = RaiseIfZero
    _SymCleanup(hProcess)


# BOOL WINAPI SymRefreshModuleList(
#   __in  HANDLE hProcess
# );
def SymRefreshModuleList(hProcess):
    _SymRefreshModuleList = windll.dbghelp.SymRefreshModuleList
    _SymRefreshModuleList.argtypes = [HANDLE]
    _SymRefreshModuleList.restype = bool
    _SymRefreshModuleList.errcheck = RaiseIfZero
    _SymRefreshModuleList(hProcess)


# BOOL WINAPI SymSetParentWindow(
#   __in  HWND hwnd
# );
def SymSetParentWindow(hwnd):
    _SymSetParentWindow = windll.dbghelp.SymSetParentWindow
    _SymSetParentWindow.argtypes = [HWND]
    _SymSetParentWindow.restype = bool
    _SymSetParentWindow.errcheck = RaiseIfZero
    _SymSetParentWindow(hwnd)


# DWORD WINAPI SymSetOptions(
#   __in  DWORD SymOptions
# );
def SymSetOptions(SymOptions):
    _SymSetOptions = windll.dbghelp.SymSetOptions
    _SymSetOptions.argtypes = [DWORD]
    _SymSetOptions.restype = DWORD
    _SymSetOptions.errcheck = RaiseIfZero
    _SymSetOptions(SymOptions)


# DWORD WINAPI SymGetOptions(void);
def SymGetOptions():
    _SymGetOptions = windll.dbghelp.SymGetOptions
    _SymGetOptions.argtypes = []
    _SymGetOptions.restype = DWORD
    return _SymGetOptions()


# DWORD WINAPI SymLoadModule(
#   __in      HANDLE hProcess,
#   __in_opt  HANDLE hFile,
#   __in_opt  PCSTR ImageName,
#   __in_opt  PCSTR ModuleName,
#   __in      DWORD BaseOfDll,
#   __in      DWORD SizeOfDll
# );
def SymLoadModuleA(hProcess, hFile=None, ImageName=None, ModuleName=None, BaseOfDll=None, SizeOfDll=None):
    _SymLoadModule = windll.dbghelp.SymLoadModule
    _SymLoadModule.argtypes = [HANDLE, HANDLE, LPSTR, LPSTR, DWORD, DWORD]
    _SymLoadModule.restype = DWORD

    if not ImageName:
        ImageName = None
    if not ModuleName:
        ModuleName = None
    if not BaseOfDll:
        BaseOfDll = 0
    if not SizeOfDll:
        SizeOfDll = 0
    SetLastError(ERROR_SUCCESS)
    lpBaseAddress = _SymLoadModule(hProcess, hFile, ImageName, ModuleName, BaseOfDll, SizeOfDll)
    if lpBaseAddress == NULL:
        dwErrorCode = GetLastError()
        if dwErrorCode != ERROR_SUCCESS:
            raise ctypes.WinError(dwErrorCode)
    return lpBaseAddress


SymLoadModuleW = MakeWideVersion(SymLoadModuleA)
SymLoadModule = GuessStringType(SymLoadModuleA, SymLoadModuleW)


# DWORD64 WINAPI SymLoadModule64(
#   __in      HANDLE hProcess,
#   __in_opt  HANDLE hFile,
#   __in_opt  PCSTR ImageName,
#   __in_opt  PCSTR ModuleName,
#   __in      DWORD64 BaseOfDll,
#   __in      DWORD SizeOfDll
# );
def SymLoadModule64A(hProcess, hFile=None, ImageName=None, ModuleName=None, BaseOfDll=None, SizeOfDll=None):
    _SymLoadModule64 = windll.dbghelp.SymLoadModule64
    _SymLoadModule64.argtypes = [HANDLE, HANDLE, LPSTR, LPSTR, DWORD64, DWORD]
    _SymLoadModule64.restype = DWORD64

    if not ImageName:
        ImageName = None
    if not ModuleName:
        ModuleName = None
    if not BaseOfDll:
        BaseOfDll = 0
    if not SizeOfDll:
        SizeOfDll = 0
    SetLastError(ERROR_SUCCESS)
    lpBaseAddress = _SymLoadModule64(hProcess, hFile, ImageName, ModuleName, BaseOfDll, SizeOfDll)
    if lpBaseAddress == NULL:
        dwErrorCode = GetLastError()
        if dwErrorCode != ERROR_SUCCESS:
            raise ctypes.WinError(dwErrorCode)
    return lpBaseAddress


SymLoadModule64W = MakeWideVersion(SymLoadModule64A)
SymLoadModule64 = GuessStringType(SymLoadModule64A, SymLoadModule64W)


# BOOL WINAPI SymUnloadModule(
#   __in  HANDLE hProcess,
#   __in  DWORD BaseOfDll
# );
def SymUnloadModule(hProcess, BaseOfDll):
    _SymUnloadModule = windll.dbghelp.SymUnloadModule
    _SymUnloadModule.argtypes = [HANDLE, DWORD]
    _SymUnloadModule.restype = bool
    _SymUnloadModule.errcheck = RaiseIfZero
    _SymUnloadModule(hProcess, BaseOfDll)


# BOOL WINAPI SymUnloadModule64(
#   __in  HANDLE hProcess,
#   __in  DWORD64 BaseOfDll
# );
def SymUnloadModule64(hProcess, BaseOfDll):
    _SymUnloadModule64 = windll.dbghelp.SymUnloadModule64
    _SymUnloadModule64.argtypes = [HANDLE, DWORD64]
    _SymUnloadModule64.restype = bool
    _SymUnloadModule64.errcheck = RaiseIfZero
    _SymUnloadModule64(hProcess, BaseOfDll)


# BOOL WINAPI SymGetModuleInfo(
#   __in   HANDLE hProcess,
#   __in   DWORD dwAddr,
#   __out  PIMAGEHLP_MODULE ModuleInfo
# );
def SymGetModuleInfoA(hProcess, dwAddr):
    _SymGetModuleInfo = windll.dbghelp.SymGetModuleInfo
    _SymGetModuleInfo.argtypes = [HANDLE, DWORD, PIMAGEHLP_MODULE]
    _SymGetModuleInfo.restype = bool
    _SymGetModuleInfo.errcheck = RaiseIfZero

    ModuleInfo = IMAGEHLP_MODULE()
    ModuleInfo.SizeOfStruct = sizeof(ModuleInfo)
    _SymGetModuleInfo(hProcess, dwAddr, byref(ModuleInfo))
    return ModuleInfo


def SymGetModuleInfoW(hProcess, dwAddr):
    _SymGetModuleInfoW = windll.dbghelp.SymGetModuleInfoW
    _SymGetModuleInfoW.argtypes = [HANDLE, DWORD, PIMAGEHLP_MODULEW]
    _SymGetModuleInfoW.restype = bool
    _SymGetModuleInfoW.errcheck = RaiseIfZero

    ModuleInfo = IMAGEHLP_MODULEW()
    ModuleInfo.SizeOfStruct = sizeof(ModuleInfo)
    _SymGetModuleInfoW(hProcess, dwAddr, byref(ModuleInfo))
    return ModuleInfo


SymGetModuleInfo = GuessStringType(SymGetModuleInfoA, SymGetModuleInfoW)


# BOOL WINAPI SymGetModuleInfo64(
#   __in   HANDLE hProcess,
#   __in   DWORD64 dwAddr,
#   __out  PIMAGEHLP_MODULE64 ModuleInfo
# );
def SymGetModuleInfo64A(hProcess, dwAddr):
    _SymGetModuleInfo64 = windll.dbghelp.SymGetModuleInfo64
    _SymGetModuleInfo64.argtypes = [HANDLE, DWORD64, PIMAGEHLP_MODULE64]
    _SymGetModuleInfo64.restype = bool
    _SymGetModuleInfo64.errcheck = RaiseIfZero

    ModuleInfo = IMAGEHLP_MODULE64()
    ModuleInfo.SizeOfStruct = sizeof(ModuleInfo)
    _SymGetModuleInfo64(hProcess, dwAddr, byref(ModuleInfo))
    return ModuleInfo


def SymGetModuleInfo64W(hProcess, dwAddr):
    _SymGetModuleInfo64W = windll.dbghelp.SymGetModuleInfo64W
    _SymGetModuleInfo64W.argtypes = [HANDLE, DWORD64, PIMAGEHLP_MODULE64W]
    _SymGetModuleInfo64W.restype = bool
    _SymGetModuleInfo64W.errcheck = RaiseIfZero

    ModuleInfo = IMAGEHLP_MODULE64W()
    ModuleInfo.SizeOfStruct = sizeof(ModuleInfo)
    _SymGetModuleInfo64W(hProcess, dwAddr, byref(ModuleInfo))
    return ModuleInfo


SymGetModuleInfo64 = GuessStringType(SymGetModuleInfo64A, SymGetModuleInfo64W)

# BOOL CALLBACK SymEnumerateModulesProc(
#   __in      PCTSTR ModuleName,
#   __in      DWORD BaseOfDll,
#   __in_opt  PVOID UserContext
# );
PSYM_ENUMMODULES_CALLBACK = WINFUNCTYPE(BOOL, LPSTR, DWORD, PVOID)
PSYM_ENUMMODULES_CALLBACKW = WINFUNCTYPE(BOOL, LPWSTR, DWORD, PVOID)

# BOOL CALLBACK SymEnumerateModulesProc64(
#   __in      PCTSTR ModuleName,
#   __in      DWORD64 BaseOfDll,
#   __in_opt  PVOID UserContext
# );
PSYM_ENUMMODULES_CALLBACK64 = WINFUNCTYPE(BOOL, LPSTR, DWORD64, PVOID)
PSYM_ENUMMODULES_CALLBACKW64 = WINFUNCTYPE(BOOL, LPWSTR, DWORD64, PVOID)


# BOOL WINAPI SymEnumerateModules(
#   __in      HANDLE hProcess,
#   __in      PSYM_ENUMMODULES_CALLBACK EnumModulesCallback,
#   __in_opt  PVOID UserContext
# );
def SymEnumerateModulesA(hProcess, EnumModulesCallback, UserContext=None):
    _SymEnumerateModules = windll.dbghelp.SymEnumerateModules
    _SymEnumerateModules.argtypes = [HANDLE, PSYM_ENUMMODULES_CALLBACK, PVOID]
    _SymEnumerateModules.restype = bool
    _SymEnumerateModules.errcheck = RaiseIfZero

    EnumModulesCallback = PSYM_ENUMMODULES_CALLBACK(EnumModulesCallback)
    if UserContext:
        UserContext = ctypes.pointer(UserContext)
    else:
        UserContext = LPVOID(NULL)
    _SymEnumerateModules(hProcess, EnumModulesCallback, UserContext)


def SymEnumerateModulesW(hProcess, EnumModulesCallback, UserContext=None):
    _SymEnumerateModulesW = windll.dbghelp.SymEnumerateModulesW
    _SymEnumerateModulesW.argtypes = [HANDLE, PSYM_ENUMMODULES_CALLBACKW, PVOID]
    _SymEnumerateModulesW.restype = bool
    _SymEnumerateModulesW.errcheck = RaiseIfZero

    EnumModulesCallback = PSYM_ENUMMODULES_CALLBACKW(EnumModulesCallback)
    if UserContext:
        UserContext = ctypes.pointer(UserContext)
    else:
        UserContext = LPVOID(NULL)
    _SymEnumerateModulesW(hProcess, EnumModulesCallback, UserContext)


SymEnumerateModules = GuessStringType(SymEnumerateModulesA, SymEnumerateModulesW)


# BOOL WINAPI SymEnumerateModules64(
#   __in      HANDLE hProcess,
#   __in      PSYM_ENUMMODULES_CALLBACK64 EnumModulesCallback,
#   __in_opt  PVOID UserContext
# );
def SymEnumerateModules64A(hProcess, EnumModulesCallback, UserContext=None):
    _SymEnumerateModules64 = windll.dbghelp.SymEnumerateModules64
    _SymEnumerateModules64.argtypes = [HANDLE, PSYM_ENUMMODULES_CALLBACK64, PVOID]
    _SymEnumerateModules64.restype = bool
    _SymEnumerateModules64.errcheck = RaiseIfZero

    EnumModulesCallback = PSYM_ENUMMODULES_CALLBACK64(EnumModulesCallback)
    if UserContext:
        UserContext = ctypes.pointer(UserContext)
    else:
        UserContext = LPVOID(NULL)
    _SymEnumerateModules64(hProcess, EnumModulesCallback, UserContext)


def SymEnumerateModules64W(hProcess, EnumModulesCallback, UserContext=None):
    _SymEnumerateModules64W = windll.dbghelp.SymEnumerateModules64W
    _SymEnumerateModules64W.argtypes = [HANDLE, PSYM_ENUMMODULES_CALLBACK64W, PVOID]
    _SymEnumerateModules64W.restype = bool
    _SymEnumerateModules64W.errcheck = RaiseIfZero

    EnumModulesCallback = PSYM_ENUMMODULES_CALLBACK64W(EnumModulesCallback)
    if UserContext:
        UserContext = ctypes.pointer(UserContext)
    else:
        UserContext = LPVOID(NULL)
    _SymEnumerateModules64W(hProcess, EnumModulesCallback, UserContext)


SymEnumerateModules64 = GuessStringType(SymEnumerateModules64A, SymEnumerateModules64W)

# BOOL CALLBACK SymEnumerateSymbolsProc(
#   __in      PCTSTR SymbolName,
#   __in      DWORD SymbolAddress,
#   __in      ULONG SymbolSize,
#   __in_opt  PVOID UserContext
# );
PSYM_ENUMSYMBOLS_CALLBACK = WINFUNCTYPE(BOOL, LPSTR, DWORD, ULONG, PVOID)
PSYM_ENUMSYMBOLS_CALLBACKW = WINFUNCTYPE(BOOL, LPWSTR, DWORD, ULONG, PVOID)

# BOOL CALLBACK SymEnumerateSymbolsProc64(
#   __in      PCTSTR SymbolName,
#   __in      DWORD64 SymbolAddress,
#   __in      ULONG SymbolSize,
#   __in_opt  PVOID UserContext
# );
PSYM_ENUMSYMBOLS_CALLBACK64 = WINFUNCTYPE(BOOL, LPSTR, DWORD64, ULONG, PVOID)
PSYM_ENUMSYMBOLS_CALLBACKW64 = WINFUNCTYPE(BOOL, LPWSTR, DWORD64, ULONG, PVOID)


# BOOL WINAPI SymEnumerateSymbols(
#   __in      HANDLE hProcess,
#   __in      ULONG BaseOfDll,
#   __in      PSYM_ENUMSYMBOLS_CALLBACK EnumSymbolsCallback,
#   __in_opt  PVOID UserContext
# );
def SymEnumerateSymbolsA(hProcess, BaseOfDll, EnumSymbolsCallback, UserContext=None):
    _SymEnumerateSymbols = windll.dbghelp.SymEnumerateSymbols
    _SymEnumerateSymbols.argtypes = [HANDLE, ULONG, PSYM_ENUMSYMBOLS_CALLBACK, PVOID]
    _SymEnumerateSymbols.restype = bool
    _SymEnumerateSymbols.errcheck = RaiseIfZero

    EnumSymbolsCallback = PSYM_ENUMSYMBOLS_CALLBACK(EnumSymbolsCallback)
    if UserContext:
        UserContext = ctypes.pointer(UserContext)
    else:
        UserContext = LPVOID(NULL)
    _SymEnumerateSymbols(hProcess, BaseOfDll, EnumSymbolsCallback, UserContext)


def SymEnumerateSymbolsW(hProcess, BaseOfDll, EnumSymbolsCallback, UserContext=None):
    _SymEnumerateSymbolsW = windll.dbghelp.SymEnumerateSymbolsW
    _SymEnumerateSymbolsW.argtypes = [HANDLE, ULONG, PSYM_ENUMSYMBOLS_CALLBACKW, PVOID]
    _SymEnumerateSymbolsW.restype = bool
    _SymEnumerateSymbolsW.errcheck = RaiseIfZero

    EnumSymbolsCallback = PSYM_ENUMSYMBOLS_CALLBACKW(EnumSymbolsCallback)
    if UserContext:
        UserContext = ctypes.pointer(UserContext)
    else:
        UserContext = LPVOID(NULL)
    _SymEnumerateSymbolsW(hProcess, BaseOfDll, EnumSymbolsCallback, UserContext)


SymEnumerateSymbols = GuessStringType(SymEnumerateSymbolsA, SymEnumerateSymbolsW)


# BOOL WINAPI SymEnumerateSymbols64(
#   __in      HANDLE hProcess,
#   __in      ULONG64 BaseOfDll,
#   __in      PSYM_ENUMSYMBOLS_CALLBACK64 EnumSymbolsCallback,
#   __in_opt  PVOID UserContext
# );
def SymEnumerateSymbols64A(hProcess, BaseOfDll, EnumSymbolsCallback, UserContext=None):
    _SymEnumerateSymbols64 = windll.dbghelp.SymEnumerateSymbols64
    _SymEnumerateSymbols64.argtypes = [HANDLE, ULONG64, PSYM_ENUMSYMBOLS_CALLBACK64, PVOID]
    _SymEnumerateSymbols64.restype = bool
    _SymEnumerateSymbols64.errcheck = RaiseIfZero

    EnumSymbolsCallback = PSYM_ENUMSYMBOLS_CALLBACK64(EnumSymbolsCallback)
    if UserContext:
        UserContext = ctypes.pointer(UserContext)
    else:
        UserContext = LPVOID(NULL)
    _SymEnumerateSymbols64(hProcess, BaseOfDll, EnumSymbolsCallback, UserContext)


def SymEnumerateSymbols64W(hProcess, BaseOfDll, EnumSymbolsCallback, UserContext=None):
    _SymEnumerateSymbols64W = windll.dbghelp.SymEnumerateSymbols64W
    _SymEnumerateSymbols64W.argtypes = [HANDLE, ULONG64, PSYM_ENUMSYMBOLS_CALLBACK64W, PVOID]
    _SymEnumerateSymbols64W.restype = bool
    _SymEnumerateSymbols64W.errcheck = RaiseIfZero

    EnumSymbolsCallback = PSYM_ENUMSYMBOLS_CALLBACK64W(EnumSymbolsCallback)
    if UserContext:
        UserContext = ctypes.pointer(UserContext)
    else:
        UserContext = LPVOID(NULL)
    _SymEnumerateSymbols64W(hProcess, BaseOfDll, EnumSymbolsCallback, UserContext)


SymEnumerateSymbols64 = GuessStringType(SymEnumerateSymbols64A, SymEnumerateSymbols64W)


# DWORD WINAPI UnDecorateSymbolName(
#   __in   PCTSTR DecoratedName,
#   __out  PTSTR UnDecoratedName,
#   __in   DWORD UndecoratedLength,
#   __in   DWORD Flags
# );
def UnDecorateSymbolNameA(DecoratedName, Flags=UNDNAME_COMPLETE):
    _UnDecorateSymbolNameA = windll.dbghelp.UnDecorateSymbolName
    _UnDecorateSymbolNameA.argtypes = [LPSTR, LPSTR, DWORD, DWORD]
    _UnDecorateSymbolNameA.restype = DWORD
    _UnDecorateSymbolNameA.errcheck = RaiseIfZero

    UndecoratedLength = _UnDecorateSymbolNameA(DecoratedName, None, 0, Flags)
    UnDecoratedName = ctypes.create_string_buffer("", UndecoratedLength + 1)
    _UnDecorateSymbolNameA(DecoratedName, UnDecoratedName, UndecoratedLength, Flags)
    return UnDecoratedName.value


def UnDecorateSymbolNameW(DecoratedName, Flags=UNDNAME_COMPLETE):
    _UnDecorateSymbolNameW = windll.dbghelp.UnDecorateSymbolNameW
    _UnDecorateSymbolNameW.argtypes = [LPWSTR, LPWSTR, DWORD, DWORD]
    _UnDecorateSymbolNameW.restype = DWORD
    _UnDecorateSymbolNameW.errcheck = RaiseIfZero

    UndecoratedLength = _UnDecorateSymbolNameW(DecoratedName, None, 0, Flags)
    UnDecoratedName = ctypes.create_unicode_buffer("", UndecoratedLength + 1)
    _UnDecorateSymbolNameW(DecoratedName, UnDecoratedName, UndecoratedLength, Flags)
    return UnDecoratedName.value


UnDecorateSymbolName = GuessStringType(UnDecorateSymbolNameA, UnDecorateSymbolNameW)


# BOOL WINAPI SymGetSearchPath(
#   __in   HANDLE hProcess,
#   __out  PTSTR SearchPath,
#   __in   DWORD SearchPathLength
# );
def SymGetSearchPathA(hProcess):
    _SymGetSearchPath = windll.dbghelp.SymGetSearchPath
    _SymGetSearchPath.argtypes = [HANDLE, LPSTR, DWORD]
    _SymGetSearchPath.restype = bool
    _SymGetSearchPath.errcheck = RaiseIfZero

    SearchPathLength = MAX_PATH
    SearchPath = ctypes.create_string_buffer("", SearchPathLength)
    _SymGetSearchPath(hProcess, SearchPath, SearchPathLength)
    return SearchPath.value


def SymGetSearchPathW(hProcess):
    _SymGetSearchPathW = windll.dbghelp.SymGetSearchPathW
    _SymGetSearchPathW.argtypes = [HANDLE, LPWSTR, DWORD]
    _SymGetSearchPathW.restype = bool
    _SymGetSearchPathW.errcheck = RaiseIfZero

    SearchPathLength = MAX_PATH
    SearchPath = ctypes.create_unicode_buffer("", SearchPathLength)
    _SymGetSearchPathW(hProcess, SearchPath, SearchPathLength)
    return SearchPath.value


SymGetSearchPath = GuessStringType(SymGetSearchPathA, SymGetSearchPathW)


# BOOL WINAPI SymSetSearchPath(
#   __in      HANDLE hProcess,
#   __in_opt  PCTSTR SearchPath
# );
def SymSetSearchPathA(hProcess, SearchPath=None):
    _SymSetSearchPath = windll.dbghelp.SymSetSearchPath
    _SymSetSearchPath.argtypes = [HANDLE, LPSTR]
    _SymSetSearchPath.restype = bool
    _SymSetSearchPath.errcheck = RaiseIfZero
    if not SearchPath:
        SearchPath = None
    _SymSetSearchPath(hProcess, SearchPath)


def SymSetSearchPathW(hProcess, SearchPath=None):
    _SymSetSearchPathW = windll.dbghelp.SymSetSearchPathW
    _SymSetSearchPathW.argtypes = [HANDLE, LPWSTR]
    _SymSetSearchPathW.restype = bool
    _SymSetSearchPathW.errcheck = RaiseIfZero
    if not SearchPath:
        SearchPath = None
    _SymSetSearchPathW(hProcess, SearchPath)


SymSetSearchPath = GuessStringType(SymSetSearchPathA, SymSetSearchPathW)


# PTCHAR WINAPI SymGetHomeDirectory(
#   __in   DWORD type,
#   __out  PTSTR dir,
#   __in   size_t size
# );
def SymGetHomeDirectoryA(type):
    _SymGetHomeDirectoryA = windll.dbghelp.SymGetHomeDirectoryA
    _SymGetHomeDirectoryA.argtypes = [DWORD, LPSTR, SIZE_T]
    _SymGetHomeDirectoryA.restype = LPSTR
    _SymGetHomeDirectoryA.errcheck = RaiseIfZero

    size = MAX_PATH
    dir = ctypes.create_string_buffer("", size)
    _SymGetHomeDirectoryA(type, dir, size)
    return dir.value


def SymGetHomeDirectoryW(type):
    _SymGetHomeDirectoryW = windll.dbghelp.SymGetHomeDirectoryW
    _SymGetHomeDirectoryW.argtypes = [DWORD, LPWSTR, SIZE_T]
    _SymGetHomeDirectoryW.restype = LPWSTR
    _SymGetHomeDirectoryW.errcheck = RaiseIfZero

    size = MAX_PATH
    dir = ctypes.create_unicode_buffer("", size)
    _SymGetHomeDirectoryW(type, dir, size)
    return dir.value


SymGetHomeDirectory = GuessStringType(SymGetHomeDirectoryA, SymGetHomeDirectoryW)


# PTCHAR WINAPI SymSetHomeDirectory(
#   __in      HANDLE hProcess,
#   __in_opt  PCTSTR dir
# );
def SymSetHomeDirectoryA(hProcess, dir=None):
    _SymSetHomeDirectoryA = windll.dbghelp.SymSetHomeDirectoryA
    _SymSetHomeDirectoryA.argtypes = [HANDLE, LPSTR]
    _SymSetHomeDirectoryA.restype = LPSTR
    _SymSetHomeDirectoryA.errcheck = RaiseIfZero
    if not dir:
        dir = None
    _SymSetHomeDirectoryA(hProcess, dir)
    return dir


def SymSetHomeDirectoryW(hProcess, dir=None):
    _SymSetHomeDirectoryW = windll.dbghelp.SymSetHomeDirectoryW
    _SymSetHomeDirectoryW.argtypes = [HANDLE, LPWSTR]
    _SymSetHomeDirectoryW.restype = LPWSTR
    _SymSetHomeDirectoryW.errcheck = RaiseIfZero
    if not dir:
        dir = None
    _SymSetHomeDirectoryW(hProcess, dir)
    return dir


SymSetHomeDirectory = GuessStringType(SymSetHomeDirectoryA, SymSetHomeDirectoryW)

# --- DbgHelp 5+ support, patch by Neitsa --------------------------------------

# XXX TODO
# + use the GuessStringType decorator for ANSI/Wide versions
# + replace hardcoded struct sizes with sizeof() calls
# + StackWalk64 should raise on error, but something has to be done about it
#   not setting the last error code (maybe we should call SetLastError
#   ourselves with a default error code?)
# /Mario

# maximum length of a symbol name
MAX_SYM_NAME = 2000


class SYM_INFO(Structure):
    _fields_ = [
        ("SizeOfStruct", ULONG),
        ("TypeIndex", ULONG),
        ("Reserved", ULONG64 * 2),
        ("Index", ULONG),
        ("Size", ULONG),
        ("ModBase", ULONG64),
        ("Flags", ULONG),
        ("Value", ULONG64),
        ("Address", ULONG64),
        ("Register", ULONG),
        ("Scope", ULONG),
        ("Tag", ULONG),
        ("NameLen", ULONG),
        ("MaxNameLen", ULONG),
        ("Name", CHAR * (MAX_SYM_NAME + 1)),
    ]


PSYM_INFO = POINTER(SYM_INFO)


class SYM_INFOW(Structure):
    _fields_ = [
        ("SizeOfStruct", ULONG),
        ("TypeIndex", ULONG),
        ("Reserved", ULONG64 * 2),
        ("Index", ULONG),
        ("Size", ULONG),
        ("ModBase", ULONG64),
        ("Flags", ULONG),
        ("Value", ULONG64),
        ("Address", ULONG64),
        ("Register", ULONG),
        ("Scope", ULONG),
        ("Tag", ULONG),
        ("NameLen", ULONG),
        ("MaxNameLen", ULONG),
        ("Name", WCHAR * (MAX_SYM_NAME + 1)),
    ]


PSYM_INFOW = POINTER(SYM_INFOW)


# ===============================================================================
# BOOL WINAPI SymFromName(
#  __in     HANDLE hProcess,
#  __in     PCTSTR Name,
#  __inout  PSYMBOL_INFO Symbol
# );
# ===============================================================================
def SymFromName(hProcess, Name):
    _SymFromNameA = windll.dbghelp.SymFromName
    _SymFromNameA.argtypes = [HANDLE, LPSTR, PSYM_INFO]
    _SymFromNameA.restype = bool
    _SymFromNameA.errcheck = RaiseIfZero

    SymInfo = SYM_INFO()
    SymInfo.SizeOfStruct = 88  # *don't modify*: sizeof(SYMBOL_INFO) in C.
    SymInfo.MaxNameLen = MAX_SYM_NAME

    _SymFromNameA(hProcess, Name, byref(SymInfo))

    return SymInfo


def SymFromNameW(hProcess, Name):
    _SymFromNameW = windll.dbghelp.SymFromNameW
    _SymFromNameW.argtypes = [HANDLE, LPWSTR, PSYM_INFOW]
    _SymFromNameW.restype = bool
    _SymFromNameW.errcheck = RaiseIfZero

    SymInfo = SYM_INFOW()
    SymInfo.SizeOfStruct = 88  # *don't modify*: sizeof(SYMBOL_INFOW) in C.
    SymInfo.MaxNameLen = MAX_SYM_NAME

    _SymFromNameW(hProcess, Name, byref(SymInfo))

    return SymInfo


# ===============================================================================
# BOOL WINAPI SymFromAddr(
#  __in       HANDLE hProcess,
#  __in       DWORD64 Address,
#  __out_opt  PDWORD64 Displacement,
#  __inout    PSYMBOL_INFO Symbol
# );
# ===============================================================================
def SymFromAddr(hProcess, Address):
    _SymFromAddr = windll.dbghelp.SymFromAddr
    _SymFromAddr.argtypes = [HANDLE, DWORD64, PDWORD64, PSYM_INFO]
    _SymFromAddr.restype = bool
    _SymFromAddr.errcheck = RaiseIfZero

    SymInfo = SYM_INFO()
    SymInfo.SizeOfStruct = 88  # *don't modify*: sizeof(SYMBOL_INFO) in C.
    SymInfo.MaxNameLen = MAX_SYM_NAME

    Displacement = DWORD64(0)
    _SymFromAddr(hProcess, Address, byref(Displacement), byref(SymInfo))

    return (Displacement.value, SymInfo)


def SymFromAddrW(hProcess, Address):
    _SymFromAddr = windll.dbghelp.SymFromAddrW
    _SymFromAddr.argtypes = [HANDLE, DWORD64, PDWORD64, PSYM_INFOW]
    _SymFromAddr.restype = bool
    _SymFromAddr.errcheck = RaiseIfZero

    SymInfo = SYM_INFOW()
    SymInfo.SizeOfStruct = 88  # *don't modify*: sizeof(SYMBOL_INFOW) in C.
    SymInfo.MaxNameLen = MAX_SYM_NAME

    Displacement = DWORD64(0)
    _SymFromAddr(hProcess, Address, byref(Displacement), byref(SymInfo))

    return (Displacement.value, SymInfo)


# ===============================================================================
# typedef struct _IMAGEHLP_SYMBOL64 {
#  DWORD   SizeOfStruct;
#  DWORD64 Address;
#  DWORD   Size;
#  DWORD   Flags;
#  DWORD   MaxNameLength;
#  CHAR   Name[1];
# } IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64;
# ===============================================================================
class IMAGEHLP_SYMBOL64(Structure):
    _fields_ = [
        ("SizeOfStruct", DWORD),
        ("Address", DWORD64),
        ("Size", DWORD),
        ("Flags", DWORD),
        ("MaxNameLength", DWORD),
        ("Name", CHAR * (MAX_SYM_NAME + 1)),
    ]


PIMAGEHLP_SYMBOL64 = POINTER(IMAGEHLP_SYMBOL64)


# ===============================================================================
# typedef struct _IMAGEHLP_SYMBOLW64 {
#  DWORD   SizeOfStruct;
#  DWORD64 Address;
#  DWORD   Size;
#  DWORD   Flags;
#  DWORD   MaxNameLength;
#  WCHAR   Name[1];
# } IMAGEHLP_SYMBOLW64, *PIMAGEHLP_SYMBOLW64;
# ===============================================================================
class IMAGEHLP_SYMBOLW64(Structure):
    _fields_ = [
        ("SizeOfStruct", DWORD),
        ("Address", DWORD64),
        ("Size", DWORD),
        ("Flags", DWORD),
        ("MaxNameLength", DWORD),
        ("Name", WCHAR * (MAX_SYM_NAME + 1)),
    ]


PIMAGEHLP_SYMBOLW64 = POINTER(IMAGEHLP_SYMBOLW64)


# ===============================================================================
# BOOL WINAPI SymGetSymFromAddr64(
#  __in       HANDLE hProcess,
#  __in       DWORD64 Address,
#  __out_opt  PDWORD64 Displacement,
#  __inout    PIMAGEHLP_SYMBOL64 Symbol
# );
# ===============================================================================
def SymGetSymFromAddr64(hProcess, Address):
    _SymGetSymFromAddr64 = windll.dbghelp.SymGetSymFromAddr64
    _SymGetSymFromAddr64.argtypes = [HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64]
    _SymGetSymFromAddr64.restype = bool
    _SymGetSymFromAddr64.errcheck = RaiseIfZero

    imagehlp_symbol64 = IMAGEHLP_SYMBOL64()
    imagehlp_symbol64.SizeOfStruct = 32  # *don't modify*: sizeof(IMAGEHLP_SYMBOL64) in C.
    imagehlp_symbol64.MaxNameLen = MAX_SYM_NAME

    Displacement = DWORD64(0)
    _SymGetSymFromAddr64(hProcess, Address, byref(Displacement), byref(imagehlp_symbol64))

    return (Displacement.value, imagehlp_symbol64)


# TODO: check for the 'W' version of SymGetSymFromAddr64()


# ===============================================================================
# typedef struct API_VERSION {
#  USHORT MajorVersion;
#  USHORT MinorVersion;
#  USHORT Revision;
#  USHORT Reserved;
# } API_VERSION, *LPAPI_VERSION;
# ===============================================================================
class API_VERSION(Structure):
    _fields_ = [
        ("MajorVersion", USHORT),
        ("MinorVersion", USHORT),
        ("Revision", USHORT),
        ("Reserved", USHORT),
    ]


PAPI_VERSION = POINTER(API_VERSION)
LPAPI_VERSION = PAPI_VERSION


# ===============================================================================
# LPAPI_VERSION WINAPI ImagehlpApiVersion(void);
# ===============================================================================
def ImagehlpApiVersion():
    _ImagehlpApiVersion = windll.dbghelp.ImagehlpApiVersion
    _ImagehlpApiVersion.restype = LPAPI_VERSION

    api_version = _ImagehlpApiVersion()
    return api_version.contents


# ===============================================================================
# LPAPI_VERSION WINAPI ImagehlpApiVersionEx(
#  __in  LPAPI_VERSION AppVersion
# );
# ===============================================================================
def ImagehlpApiVersionEx(MajorVersion, MinorVersion, Revision):
    _ImagehlpApiVersionEx = windll.dbghelp.ImagehlpApiVersionEx
    _ImagehlpApiVersionEx.argtypes = [LPAPI_VERSION]
    _ImagehlpApiVersionEx.restype = LPAPI_VERSION

    api_version = API_VERSION(MajorVersion, MinorVersion, Revision, 0)

    ret_api_version = _ImagehlpApiVersionEx(byref(api_version))

    return ret_api_version.contents


# ===============================================================================
# typedef enum {
#     AddrMode1616,
#     AddrMode1632,
#     AddrModeReal,
#     AddrModeFlat
# } ADDRESS_MODE;
# ===============================================================================
AddrMode1616 = 0
AddrMode1632 = 1
AddrModeReal = 2
AddrModeFlat = 3

ADDRESS_MODE = DWORD  # needed for the size of an ADDRESS_MODE (see ADDRESS64)


# ===============================================================================
# typedef struct _tagADDRESS64 {
#  DWORD64      Offset;
#  WORD         Segment;
#  ADDRESS_MODE Mode;
# } ADDRESS64, *LPADDRESS64;
# ===============================================================================
class ADDRESS64(Structure):
    _fields_ = [
        ("Offset", DWORD64),
        ("Segment", WORD),
        ("Mode", ADDRESS_MODE),  # it's a member of the ADDRESS_MODE enum.
    ]


LPADDRESS64 = POINTER(ADDRESS64)


# ===============================================================================
# typedef struct _KDHELP64 {
#    DWORD64   Thread;
#    DWORD   ThCallbackStack;
#    DWORD   ThCallbackBStore;
#    DWORD   NextCallback;
#    DWORD   FramePointer;
#    DWORD64   KiCallUserMode;
#    DWORD64   KeUserCallbackDispatcher;
#    DWORD64   SystemRangeStart;
#    DWORD64   KiUserExceptionDispatcher;
#    DWORD64   StackBase;
#    DWORD64   StackLimit;
#    DWORD64   Reserved[5];
# } KDHELP64, *PKDHELP64;
# ===============================================================================
class KDHELP64(Structure):
    _fields_ = [
        ("Thread", DWORD64),
        ("ThCallbackStack", DWORD),
        ("ThCallbackBStore", DWORD),
        ("NextCallback", DWORD),
        ("FramePointer", DWORD),
        ("KiCallUserMode", DWORD64),
        ("KeUserCallbackDispatcher", DWORD64),
        ("SystemRangeStart", DWORD64),
        ("KiUserExceptionDispatcher", DWORD64),
        ("StackBase", DWORD64),
        ("StackLimit", DWORD64),
        ("Reserved", DWORD64 * 5),
    ]


PKDHELP64 = POINTER(KDHELP64)


# ===============================================================================
# typedef struct _tagSTACKFRAME64 {
#  ADDRESS64 AddrPC;
#  ADDRESS64 AddrReturn;
#  ADDRESS64 AddrFrame;
#  ADDRESS64 AddrStack;
#  ADDRESS64 AddrBStore;
#  PVOID     FuncTableEntry;
#  DWORD64   Params[4];
#  BOOL      Far;
#  BOOL      Virtual;
#  DWORD64   Reserved[3];
#  KDHELP64  KdHelp;
# } STACKFRAME64, *LPSTACKFRAME64;
# ===============================================================================
class STACKFRAME64(Structure):
    _fields_ = [
        ("AddrPC", ADDRESS64),
        ("AddrReturn", ADDRESS64),
        ("AddrFrame", ADDRESS64),
        ("AddrStack", ADDRESS64),
        ("AddrBStore", ADDRESS64),
        ("FuncTableEntry", PVOID),
        ("Params", DWORD64 * 4),
        ("Far", BOOL),
        ("Virtual", BOOL),
        ("Reserved", DWORD64 * 3),
        ("KdHelp", KDHELP64),
    ]


LPSTACKFRAME64 = POINTER(STACKFRAME64)

# ===============================================================================
# BOOL CALLBACK ReadProcessMemoryProc64(
#  __in   HANDLE hProcess,
#  __in   DWORD64 lpBaseAddress,
#  __out  PVOID lpBuffer,
#  __in   DWORD nSize,
#  __out  LPDWORD lpNumberOfBytesRead
# );
# ===============================================================================
PREAD_PROCESS_MEMORY_ROUTINE64 = WINFUNCTYPE(BOOL, HANDLE, DWORD64, PVOID, DWORD, LPDWORD)

# ===============================================================================
# PVOID CALLBACK FunctionTableAccessProc64(
#  __in  HANDLE hProcess,
#  __in  DWORD64 AddrBase
# );
# ===============================================================================
PFUNCTION_TABLE_ACCESS_ROUTINE64 = WINFUNCTYPE(PVOID, HANDLE, DWORD64)

# ===============================================================================
# DWORD64 CALLBACK GetModuleBaseProc64(
#  __in  HANDLE hProcess,
#  __in  DWORD64 Address
# );
# ===============================================================================
PGET_MODULE_BASE_ROUTINE64 = WINFUNCTYPE(DWORD64, HANDLE, DWORD64)

# ===============================================================================
# DWORD64 CALLBACK GetModuleBaseProc64(
#  __in  HANDLE hProcess,
#  __in  DWORD64 Address
# );
# ===============================================================================
PTRANSLATE_ADDRESS_ROUTINE64 = WINFUNCTYPE(DWORD64, HANDLE, DWORD64)

# Valid machine types for StackWalk64 function
IMAGE_FILE_MACHINE_I386 = 0x014C  # Intel x86
IMAGE_FILE_MACHINE_IA64 = 0x0200  # Intel Itanium Processor Family (IPF)
IMAGE_FILE_MACHINE_AMD64 = 0x8664  # x64 (AMD64 or EM64T)


# ===============================================================================
# BOOL WINAPI StackWalk64(
#  __in      DWORD MachineType,
#  __in      HANDLE hProcess,
#  __in      HANDLE hThread,
#  __inout   LPSTACKFRAME64 StackFrame,
#  __inout   PVOID ContextRecord,
#  __in_opt  PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine,
#  __in_opt  PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
#  __in_opt  PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
#  __in_opt  PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress
# );
# ===============================================================================
def StackWalk64(
    MachineType,
    hProcess,
    hThread,
    StackFrame,
    ContextRecord=None,
    ReadMemoryRoutine=None,
    FunctionTableAccessRoutine=None,
    GetModuleBaseRoutine=None,
    TranslateAddress=None,
):
    _StackWalk64 = windll.dbghelp.StackWalk64
    _StackWalk64.argtypes = [
        DWORD,
        HANDLE,
        HANDLE,
        LPSTACKFRAME64,
        PVOID,
        PREAD_PROCESS_MEMORY_ROUTINE64,
        PFUNCTION_TABLE_ACCESS_ROUTINE64,
        PGET_MODULE_BASE_ROUTINE64,
        PTRANSLATE_ADDRESS_ROUTINE64,
    ]
    _StackWalk64.restype = bool

    pReadMemoryRoutine = None
    if ReadMemoryRoutine:
        pReadMemoryRoutine = PREAD_PROCESS_MEMORY_ROUTINE64(ReadMemoryRoutine)
    else:
        pReadMemoryRoutine = ctypes.cast(None, PREAD_PROCESS_MEMORY_ROUTINE64)

    pFunctionTableAccessRoutine = None
    if FunctionTableAccessRoutine:
        pFunctionTableAccessRoutine = PFUNCTION_TABLE_ACCESS_ROUTINE64(FunctionTableAccessRoutine)
    else:
        pFunctionTableAccessRoutine = ctypes.cast(None, PFUNCTION_TABLE_ACCESS_ROUTINE64)

    pGetModuleBaseRoutine = None
    if GetModuleBaseRoutine:
        pGetModuleBaseRoutine = PGET_MODULE_BASE_ROUTINE64(GetModuleBaseRoutine)
    else:
        pGetModuleBaseRoutine = ctypes.cast(None, PGET_MODULE_BASE_ROUTINE64)

    pTranslateAddress = None
    if TranslateAddress:
        pTranslateAddress = PTRANSLATE_ADDRESS_ROUTINE64(TranslateAddress)
    else:
        pTranslateAddress = ctypes.cast(None, PTRANSLATE_ADDRESS_ROUTINE64)

    pContextRecord = None
    if ContextRecord is None:
        ContextRecord = GetThreadContext(hThread, raw=True)
    pContextRecord = PCONTEXT(ContextRecord)

    # this function *DOESN'T* set last error [GetLastError()] properly most of the time.
    ret = _StackWalk64(
        MachineType,
        hProcess,
        hThread,
        byref(StackFrame),
        pContextRecord,
        pReadMemoryRoutine,
        pFunctionTableAccessRoutine,
        pGetModuleBaseRoutine,
        pTranslateAddress,
    )

    return ret


# ==============================================================================
# This calculates the list of exported symbols.
_all = set(vars().keys()).difference(_all)
__all__ = [_x for _x in _all if not _x.startswith("_")]
__all__.sort()
# ==============================================================================
