#!/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 kernel32.dll in ctypes.
"""

__revision__ = "$Id$"

import warnings

from winappdbg.win32.defines import *

from winappdbg.win32 import context_i386
from winappdbg.win32 import context_amd64

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

from winappdbg.win32.version import *

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


# This can't be defined in defines.py because it calls GetLastError().
def RaiseIfLastError(result, func=None, arguments=()):
    """
    Error checking for Win32 API calls with no error-specific return value.

    Regardless of the return value, the function calls GetLastError(). If the
    code is not C{ERROR_SUCCESS} then a C{WindowsError} exception is raised.

    For this to work, the user MUST call SetLastError(ERROR_SUCCESS) prior to
    calling the API. Otherwise an exception may be raised even on success,
    since most API calls don't clear the error status code.
    """
    code = GetLastError()
    if code != ERROR_SUCCESS:
        raise ctypes.WinError(code)
    return result


# --- CONTEXT structure and constants ------------------------------------------

ContextArchMask = 0x0FFF0000  # just guessing here! seems to work, though

if arch == ARCH_I386:
    from winappdbg.win32.context_i386 import *
elif arch == ARCH_AMD64:
    if bits == 64:
        from winappdbg.win32.context_amd64 import *
    else:
        from winappdbg.win32.context_i386 import *
else:
    warnings.warn("Unknown or unsupported architecture: %s" % arch)

# --- Constants ----------------------------------------------------------------

STILL_ACTIVE = 259

WAIT_TIMEOUT = 0x102
WAIT_FAILED = -1
WAIT_OBJECT_0 = 0

EXCEPTION_NONCONTINUABLE = 0x1  # Noncontinuable exception
EXCEPTION_MAXIMUM_PARAMETERS = 15  # maximum number of exception parameters
MAXIMUM_WAIT_OBJECTS = 64  # Maximum number of wait objects
MAXIMUM_SUSPEND_COUNT = 0x7F  # Maximum times thread can be suspended

FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000

GR_GDIOBJECTS = 0
GR_USEROBJECTS = 1

PROCESS_NAME_NATIVE = 1

MAXINTATOM = 0xC000

STD_INPUT_HANDLE = 0xFFFFFFF6  # (DWORD)-10
STD_OUTPUT_HANDLE = 0xFFFFFFF5  # (DWORD)-11
STD_ERROR_HANDLE = 0xFFFFFFF4  # (DWORD)-12

ATTACH_PARENT_PROCESS = 0xFFFFFFFF  # (DWORD)-1

# LoadLibraryEx constants
DONT_RESOLVE_DLL_REFERENCES = 0x00000001
LOAD_LIBRARY_AS_DATAFILE = 0x00000002
LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008
LOAD_IGNORE_CODE_AUTHZ_LEVEL = 0x00000010
LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE = 0x00000040

# SetSearchPathMode flags
# TODO I couldn't find these constants :(
##BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE     = ???
##BASE_SEARCH_PATH_DISABLE_SAFE_SEARCHMODE    = ???
##BASE_SEARCH_PATH_PERMANENT                  = ???

# Console control events
CTRL_C_EVENT = 0
CTRL_BREAK_EVENT = 1
CTRL_CLOSE_EVENT = 2
CTRL_LOGOFF_EVENT = 5
CTRL_SHUTDOWN_EVENT = 6

# Heap flags
HEAP_NO_SERIALIZE = 0x00000001
HEAP_GENERATE_EXCEPTIONS = 0x00000004
HEAP_ZERO_MEMORY = 0x00000008
HEAP_CREATE_ENABLE_EXECUTE = 0x00040000

# Standard access rights
DELETE = long(0x00010000)
READ_CONTROL = long(0x00020000)
WRITE_DAC = long(0x00040000)
WRITE_OWNER = long(0x00080000)
SYNCHRONIZE = long(0x00100000)
STANDARD_RIGHTS_REQUIRED = long(0x000F0000)
STANDARD_RIGHTS_READ = READ_CONTROL
STANDARD_RIGHTS_WRITE = READ_CONTROL
STANDARD_RIGHTS_EXECUTE = READ_CONTROL
STANDARD_RIGHTS_ALL = long(0x001F0000)
SPECIFIC_RIGHTS_ALL = long(0x0000FFFF)

# Mutex access rights
MUTEX_ALL_ACCESS = 0x1F0001
MUTEX_MODIFY_STATE = 1

# Event access rights
EVENT_ALL_ACCESS = 0x1F0003
EVENT_MODIFY_STATE = 2

# Semaphore access rights
SEMAPHORE_ALL_ACCESS = 0x1F0003
SEMAPHORE_MODIFY_STATE = 2

# Timer access rights
TIMER_ALL_ACCESS = 0x1F0003
TIMER_MODIFY_STATE = 2
TIMER_QUERY_STATE = 1

# Process access rights for OpenProcess
PROCESS_TERMINATE = 0x0001
PROCESS_CREATE_THREAD = 0x0002
PROCESS_SET_SESSIONID = 0x0004
PROCESS_VM_OPERATION = 0x0008
PROCESS_VM_READ = 0x0010
PROCESS_VM_WRITE = 0x0020
PROCESS_DUP_HANDLE = 0x0040
PROCESS_CREATE_PROCESS = 0x0080
PROCESS_SET_QUOTA = 0x0100
PROCESS_SET_INFORMATION = 0x0200
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_SUSPEND_RESUME = 0x0800
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000

# Thread access rights for OpenThread
THREAD_TERMINATE = 0x0001
THREAD_SUSPEND_RESUME = 0x0002
THREAD_ALERT = 0x0004
THREAD_GET_CONTEXT = 0x0008
THREAD_SET_CONTEXT = 0x0010
THREAD_SET_INFORMATION = 0x0020
THREAD_QUERY_INFORMATION = 0x0040
THREAD_SET_THREAD_TOKEN = 0x0080
THREAD_IMPERSONATE = 0x0100
THREAD_DIRECT_IMPERSONATION = 0x0200
THREAD_SET_LIMITED_INFORMATION = 0x0400
THREAD_QUERY_LIMITED_INFORMATION = 0x0800

# The values of PROCESS_ALL_ACCESS and THREAD_ALL_ACCESS were changed in Vista/2008
PROCESS_ALL_ACCESS_NT = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF
PROCESS_ALL_ACCESS_VISTA = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFFF
THREAD_ALL_ACCESS_NT = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3FF
THREAD_ALL_ACCESS_VISTA = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFFF
if NTDDI_VERSION < NTDDI_VISTA:
    PROCESS_ALL_ACCESS = PROCESS_ALL_ACCESS_NT
    THREAD_ALL_ACCESS = THREAD_ALL_ACCESS_NT
else:
    PROCESS_ALL_ACCESS = PROCESS_ALL_ACCESS_VISTA
    THREAD_ALL_ACCESS = THREAD_ALL_ACCESS_VISTA

# Process priority classes

IDLE_PRIORITY_CLASS = 0x00000040
BELOW_NORMAL_PRIORITY_CLASS = 0x00004000
NORMAL_PRIORITY_CLASS = 0x00000020
ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000
HIGH_PRIORITY_CLASS = 0x00000080
REALTIME_PRIORITY_CLASS = 0x00000100

PROCESS_MODE_BACKGROUND_BEGIN = 0x00100000
PROCESS_MODE_BACKGROUND_END = 0x00200000

# dwCreationFlag values

DEBUG_PROCESS = 0x00000001
DEBUG_ONLY_THIS_PROCESS = 0x00000002
CREATE_SUSPENDED = 0x00000004  # Threads and processes
DETACHED_PROCESS = 0x00000008
CREATE_NEW_CONSOLE = 0x00000010
NORMAL_PRIORITY_CLASS = 0x00000020
IDLE_PRIORITY_CLASS = 0x00000040
HIGH_PRIORITY_CLASS = 0x00000080
REALTIME_PRIORITY_CLASS = 0x00000100
CREATE_NEW_PROCESS_GROUP = 0x00000200
CREATE_UNICODE_ENVIRONMENT = 0x00000400
CREATE_SEPARATE_WOW_VDM = 0x00000800
CREATE_SHARED_WOW_VDM = 0x00001000
CREATE_FORCEDOS = 0x00002000
BELOW_NORMAL_PRIORITY_CLASS = 0x00004000
ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000
INHERIT_PARENT_AFFINITY = 0x00010000
STACK_SIZE_PARAM_IS_A_RESERVATION = 0x00010000  # Threads only
INHERIT_CALLER_PRIORITY = 0x00020000  # Deprecated
CREATE_PROTECTED_PROCESS = 0x00040000
EXTENDED_STARTUPINFO_PRESENT = 0x00080000
PROCESS_MODE_BACKGROUND_BEGIN = 0x00100000
PROCESS_MODE_BACKGROUND_END = 0x00200000
CREATE_BREAKAWAY_FROM_JOB = 0x01000000
CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000
CREATE_DEFAULT_ERROR_MODE = 0x04000000
CREATE_NO_WINDOW = 0x08000000
PROFILE_USER = 0x10000000
PROFILE_KERNEL = 0x20000000
PROFILE_SERVER = 0x40000000
CREATE_IGNORE_SYSTEM_DEFAULT = 0x80000000

# Thread priority values

THREAD_BASE_PRIORITY_LOWRT = 15  # value that gets a thread to LowRealtime-1
THREAD_BASE_PRIORITY_MAX = 2  # maximum thread base priority boost
THREAD_BASE_PRIORITY_MIN = -2  # minimum thread base priority boost
THREAD_BASE_PRIORITY_IDLE = -15  # value that gets a thread to idle

THREAD_PRIORITY_LOWEST = THREAD_BASE_PRIORITY_MIN
THREAD_PRIORITY_BELOW_NORMAL = THREAD_PRIORITY_LOWEST + 1
THREAD_PRIORITY_NORMAL = 0
THREAD_PRIORITY_HIGHEST = THREAD_BASE_PRIORITY_MAX
THREAD_PRIORITY_ABOVE_NORMAL = THREAD_PRIORITY_HIGHEST - 1
THREAD_PRIORITY_ERROR_RETURN = long(0xFFFFFFFF)

THREAD_PRIORITY_TIME_CRITICAL = THREAD_BASE_PRIORITY_LOWRT
THREAD_PRIORITY_IDLE = THREAD_BASE_PRIORITY_IDLE

# Memory access
SECTION_QUERY = 0x0001
SECTION_MAP_WRITE = 0x0002
SECTION_MAP_READ = 0x0004
SECTION_MAP_EXECUTE = 0x0008
SECTION_EXTEND_SIZE = 0x0010
SECTION_MAP_EXECUTE_EXPLICIT = 0x0020  # not included in SECTION_ALL_ACCESS

SECTION_ALL_ACCESS = (
    STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_WRITE | SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_EXTEND_SIZE
)
PAGE_NOACCESS = 0x01
PAGE_READONLY = 0x02
PAGE_READWRITE = 0x04
PAGE_WRITECOPY = 0x08
PAGE_EXECUTE = 0x10
PAGE_EXECUTE_READ = 0x20
PAGE_EXECUTE_READWRITE = 0x40
PAGE_EXECUTE_WRITECOPY = 0x80
PAGE_GUARD = 0x100
PAGE_NOCACHE = 0x200
PAGE_WRITECOMBINE = 0x400
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
MEM_DECOMMIT = 0x4000
MEM_RELEASE = 0x8000
MEM_FREE = 0x10000
MEM_PRIVATE = 0x20000
MEM_MAPPED = 0x40000
MEM_RESET = 0x80000
MEM_TOP_DOWN = 0x100000
MEM_WRITE_WATCH = 0x200000
MEM_PHYSICAL = 0x400000
MEM_LARGE_PAGES = 0x20000000
MEM_4MB_PAGES = 0x80000000
SEC_FILE = 0x800000
SEC_IMAGE = 0x1000000
SEC_RESERVE = 0x4000000
SEC_COMMIT = 0x8000000
SEC_NOCACHE = 0x10000000
SEC_LARGE_PAGES = 0x80000000
MEM_IMAGE = SEC_IMAGE
WRITE_WATCH_FLAG_RESET = 0x01
FILE_MAP_ALL_ACCESS = 0xF001F

SECTION_QUERY = 0x0001
SECTION_MAP_WRITE = 0x0002
SECTION_MAP_READ = 0x0004
SECTION_MAP_EXECUTE = 0x0008
SECTION_EXTEND_SIZE = 0x0010
SECTION_MAP_EXECUTE_EXPLICIT = 0x0020  # not included in SECTION_ALL_ACCESS

SECTION_ALL_ACCESS = (
    STANDARD_RIGHTS_REQUIRED | SECTION_QUERY | SECTION_MAP_WRITE | SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_EXTEND_SIZE
)

FILE_MAP_COPY = SECTION_QUERY
FILE_MAP_WRITE = SECTION_MAP_WRITE
FILE_MAP_READ = SECTION_MAP_READ
FILE_MAP_ALL_ACCESS = SECTION_ALL_ACCESS
FILE_MAP_EXECUTE = SECTION_MAP_EXECUTE_EXPLICIT  # not included in FILE_MAP_ALL_ACCESS

GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
GENERIC_EXECUTE = 0x20000000
GENERIC_ALL = 0x10000000

FILE_SHARE_READ = 0x00000001
FILE_SHARE_WRITE = 0x00000002
FILE_SHARE_DELETE = 0x00000004

CREATE_NEW = 1
CREATE_ALWAYS = 2
OPEN_EXISTING = 3
OPEN_ALWAYS = 4
TRUNCATE_EXISTING = 5

FILE_ATTRIBUTE_READONLY = 0x00000001
FILE_ATTRIBUTE_NORMAL = 0x00000080
FILE_ATTRIBUTE_TEMPORARY = 0x00000100

FILE_FLAG_WRITE_THROUGH = 0x80000000
FILE_FLAG_NO_BUFFERING = 0x20000000
FILE_FLAG_RANDOM_ACCESS = 0x10000000
FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000
FILE_FLAG_DELETE_ON_CLOSE = 0x04000000
FILE_FLAG_OVERLAPPED = 0x40000000

FILE_ATTRIBUTE_READONLY = 0x00000001
FILE_ATTRIBUTE_HIDDEN = 0x00000002
FILE_ATTRIBUTE_SYSTEM = 0x00000004
FILE_ATTRIBUTE_DIRECTORY = 0x00000010
FILE_ATTRIBUTE_ARCHIVE = 0x00000020
FILE_ATTRIBUTE_DEVICE = 0x00000040
FILE_ATTRIBUTE_NORMAL = 0x00000080
FILE_ATTRIBUTE_TEMPORARY = 0x00000100

# Debug events
EXCEPTION_DEBUG_EVENT = 1
CREATE_THREAD_DEBUG_EVENT = 2
CREATE_PROCESS_DEBUG_EVENT = 3
EXIT_THREAD_DEBUG_EVENT = 4
EXIT_PROCESS_DEBUG_EVENT = 5
LOAD_DLL_DEBUG_EVENT = 6
UNLOAD_DLL_DEBUG_EVENT = 7
OUTPUT_DEBUG_STRING_EVENT = 8
RIP_EVENT = 9

# Debug status codes (ContinueDebugEvent)
DBG_EXCEPTION_HANDLED = long(0x00010001)
DBG_CONTINUE = long(0x00010002)
DBG_REPLY_LATER = long(0x40010001)
DBG_UNABLE_TO_PROVIDE_HANDLE = long(0x40010002)
DBG_TERMINATE_THREAD = long(0x40010003)
DBG_TERMINATE_PROCESS = long(0x40010004)
DBG_CONTROL_C = long(0x40010005)
DBG_PRINTEXCEPTION_C = long(0x40010006)
DBG_RIPEXCEPTION = long(0x40010007)
DBG_CONTROL_BREAK = long(0x40010008)
DBG_COMMAND_EXCEPTION = long(0x40010009)
DBG_EXCEPTION_NOT_HANDLED = long(0x80010001)
DBG_NO_STATE_CHANGE = long(0xC0010001)
DBG_APP_NOT_IDLE = long(0xC0010002)

# Status codes
STATUS_WAIT_0 = long(0x00000000)
STATUS_ABANDONED_WAIT_0 = long(0x00000080)
STATUS_USER_APC = long(0x000000C0)
STATUS_TIMEOUT = long(0x00000102)
STATUS_PENDING = long(0x00000103)
STATUS_SEGMENT_NOTIFICATION = long(0x40000005)
STATUS_GUARD_PAGE_VIOLATION = long(0x80000001)
STATUS_DATATYPE_MISALIGNMENT = long(0x80000002)
STATUS_BREAKPOINT = long(0x80000003)
STATUS_SINGLE_STEP = long(0x80000004)
STATUS_INVALID_INFO_CLASS = long(0xC0000003)
STATUS_ACCESS_VIOLATION = long(0xC0000005)
STATUS_IN_PAGE_ERROR = long(0xC0000006)
STATUS_INVALID_HANDLE = long(0xC0000008)
STATUS_NO_MEMORY = long(0xC0000017)
STATUS_ILLEGAL_INSTRUCTION = long(0xC000001D)
STATUS_NONCONTINUABLE_EXCEPTION = long(0xC0000025)
STATUS_INVALID_DISPOSITION = long(0xC0000026)
STATUS_ARRAY_BOUNDS_EXCEEDED = long(0xC000008C)
STATUS_FLOAT_DENORMAL_OPERAND = long(0xC000008D)
STATUS_FLOAT_DIVIDE_BY_ZERO = long(0xC000008E)
STATUS_FLOAT_INEXACT_RESULT = long(0xC000008F)
STATUS_FLOAT_INVALID_OPERATION = long(0xC0000090)
STATUS_FLOAT_OVERFLOW = long(0xC0000091)
STATUS_FLOAT_STACK_CHECK = long(0xC0000092)
STATUS_FLOAT_UNDERFLOW = long(0xC0000093)
STATUS_INTEGER_DIVIDE_BY_ZERO = long(0xC0000094)
STATUS_INTEGER_OVERFLOW = long(0xC0000095)
STATUS_PRIVILEGED_INSTRUCTION = long(0xC0000096)
STATUS_STACK_OVERFLOW = long(0xC00000FD)
STATUS_CONTROL_C_EXIT = long(0xC000013A)
STATUS_FLOAT_MULTIPLE_FAULTS = long(0xC00002B4)
STATUS_FLOAT_MULTIPLE_TRAPS = long(0xC00002B5)
STATUS_REG_NAT_CONSUMPTION = long(0xC00002C9)
STATUS_SXS_EARLY_DEACTIVATION = long(0xC015000F)
STATUS_SXS_INVALID_DEACTIVATION = long(0xC0150010)

STATUS_STACK_BUFFER_OVERRUN = long(0xC0000409)
STATUS_WX86_BREAKPOINT = long(0x4000001F)
STATUS_HEAP_CORRUPTION = long(0xC0000374)

STATUS_POSSIBLE_DEADLOCK = long(0xC0000194)

STATUS_UNWIND_CONSOLIDATE = long(0x80000029)

# Exception codes

EXCEPTION_ACCESS_VIOLATION = STATUS_ACCESS_VIOLATION
EXCEPTION_ARRAY_BOUNDS_EXCEEDED = STATUS_ARRAY_BOUNDS_EXCEEDED
EXCEPTION_BREAKPOINT = STATUS_BREAKPOINT
EXCEPTION_DATATYPE_MISALIGNMENT = STATUS_DATATYPE_MISALIGNMENT
EXCEPTION_FLT_DENORMAL_OPERAND = STATUS_FLOAT_DENORMAL_OPERAND
EXCEPTION_FLT_DIVIDE_BY_ZERO = STATUS_FLOAT_DIVIDE_BY_ZERO
EXCEPTION_FLT_INEXACT_RESULT = STATUS_FLOAT_INEXACT_RESULT
EXCEPTION_FLT_INVALID_OPERATION = STATUS_FLOAT_INVALID_OPERATION
EXCEPTION_FLT_OVERFLOW = STATUS_FLOAT_OVERFLOW
EXCEPTION_FLT_STACK_CHECK = STATUS_FLOAT_STACK_CHECK
EXCEPTION_FLT_UNDERFLOW = STATUS_FLOAT_UNDERFLOW
EXCEPTION_ILLEGAL_INSTRUCTION = STATUS_ILLEGAL_INSTRUCTION
EXCEPTION_IN_PAGE_ERROR = STATUS_IN_PAGE_ERROR
EXCEPTION_INT_DIVIDE_BY_ZERO = STATUS_INTEGER_DIVIDE_BY_ZERO
EXCEPTION_INT_OVERFLOW = STATUS_INTEGER_OVERFLOW
EXCEPTION_INVALID_DISPOSITION = STATUS_INVALID_DISPOSITION
EXCEPTION_NONCONTINUABLE_EXCEPTION = STATUS_NONCONTINUABLE_EXCEPTION
EXCEPTION_PRIV_INSTRUCTION = STATUS_PRIVILEGED_INSTRUCTION
EXCEPTION_SINGLE_STEP = STATUS_SINGLE_STEP
EXCEPTION_STACK_OVERFLOW = STATUS_STACK_OVERFLOW

EXCEPTION_GUARD_PAGE = STATUS_GUARD_PAGE_VIOLATION
EXCEPTION_INVALID_HANDLE = STATUS_INVALID_HANDLE
EXCEPTION_POSSIBLE_DEADLOCK = STATUS_POSSIBLE_DEADLOCK
EXCEPTION_WX86_BREAKPOINT = STATUS_WX86_BREAKPOINT

CONTROL_C_EXIT = STATUS_CONTROL_C_EXIT

DBG_CONTROL_C = long(0x40010005)
MS_VC_EXCEPTION = long(0x406D1388)

# Access violation types
ACCESS_VIOLATION_TYPE_READ = EXCEPTION_READ_FAULT
ACCESS_VIOLATION_TYPE_WRITE = EXCEPTION_WRITE_FAULT
ACCESS_VIOLATION_TYPE_DEP = EXCEPTION_EXECUTE_FAULT

# RIP event types
SLE_ERROR = 1
SLE_MINORERROR = 2
SLE_WARNING = 3

# DuplicateHandle constants
DUPLICATE_CLOSE_SOURCE = 0x00000001
DUPLICATE_SAME_ACCESS = 0x00000002

# GetFinalPathNameByHandle constants
FILE_NAME_NORMALIZED = 0x0
FILE_NAME_OPENED = 0x8
VOLUME_NAME_DOS = 0x0
VOLUME_NAME_GUID = 0x1
VOLUME_NAME_NONE = 0x4
VOLUME_NAME_NT = 0x2

# GetProductInfo constants
PRODUCT_BUSINESS = 0x00000006
PRODUCT_BUSINESS_N = 0x00000010
PRODUCT_CLUSTER_SERVER = 0x00000012
PRODUCT_DATACENTER_SERVER = 0x00000008
PRODUCT_DATACENTER_SERVER_CORE = 0x0000000C
PRODUCT_DATACENTER_SERVER_CORE_V = 0x00000027
PRODUCT_DATACENTER_SERVER_V = 0x00000025
PRODUCT_ENTERPRISE = 0x00000004
PRODUCT_ENTERPRISE_E = 0x00000046
PRODUCT_ENTERPRISE_N = 0x0000001B
PRODUCT_ENTERPRISE_SERVER = 0x0000000A
PRODUCT_ENTERPRISE_SERVER_CORE = 0x0000000E
PRODUCT_ENTERPRISE_SERVER_CORE_V = 0x00000029
PRODUCT_ENTERPRISE_SERVER_IA64 = 0x0000000F
PRODUCT_ENTERPRISE_SERVER_V = 0x00000026
PRODUCT_HOME_BASIC = 0x00000002
PRODUCT_HOME_BASIC_E = 0x00000043
PRODUCT_HOME_BASIC_N = 0x00000005
PRODUCT_HOME_PREMIUM = 0x00000003
PRODUCT_HOME_PREMIUM_E = 0x00000044
PRODUCT_HOME_PREMIUM_N = 0x0000001A
PRODUCT_HYPERV = 0x0000002A
PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT = 0x0000001E
PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING = 0x00000020
PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY = 0x0000001F
PRODUCT_PROFESSIONAL = 0x00000030
PRODUCT_PROFESSIONAL_E = 0x00000045
PRODUCT_PROFESSIONAL_N = 0x00000031
PRODUCT_SERVER_FOR_SMALLBUSINESS = 0x00000018
PRODUCT_SERVER_FOR_SMALLBUSINESS_V = 0x00000023
PRODUCT_SERVER_FOUNDATION = 0x00000021
PRODUCT_SMALLBUSINESS_SERVER = 0x00000009
PRODUCT_STANDARD_SERVER = 0x00000007
PRODUCT_STANDARD_SERVER_CORE = 0x0000000D
PRODUCT_STANDARD_SERVER_CORE_V = 0x00000028
PRODUCT_STANDARD_SERVER_V = 0x00000024
PRODUCT_STARTER = 0x0000000B
PRODUCT_STARTER_E = 0x00000042
PRODUCT_STARTER_N = 0x0000002F
PRODUCT_STORAGE_ENTERPRISE_SERVER = 0x00000017
PRODUCT_STORAGE_EXPRESS_SERVER = 0x00000014
PRODUCT_STORAGE_STANDARD_SERVER = 0x00000015
PRODUCT_STORAGE_WORKGROUP_SERVER = 0x00000016
PRODUCT_UNDEFINED = 0x00000000
PRODUCT_UNLICENSED = 0xABCDABCD
PRODUCT_ULTIMATE = 0x00000001
PRODUCT_ULTIMATE_E = 0x00000047
PRODUCT_ULTIMATE_N = 0x0000001C
PRODUCT_WEB_SERVER = 0x00000011
PRODUCT_WEB_SERVER_CORE = 0x0000001D

# DEP policy flags
PROCESS_DEP_ENABLE = 1
PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION = 2

# Error modes
SEM_FAILCRITICALERRORS = 0x001
SEM_NOGPFAULTERRORBOX = 0x002
SEM_NOALIGNMENTFAULTEXCEPT = 0x004
SEM_NOOPENFILEERRORBOX = 0x800

# GetHandleInformation / SetHandleInformation
HANDLE_FLAG_INHERIT = 0x00000001
HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002

# --- Handle wrappers ----------------------------------------------------------


class Handle(object):
    """
    Encapsulates Win32 handles to avoid leaking them.

    @type inherit: bool
    @ivar inherit: C{True} if the handle is to be inherited by child processes,
        C{False} otherwise.

    @type protectFromClose: bool
    @ivar protectFromClose: Set to C{True} to prevent the handle from being
        closed. Must be set to C{False} before you're done using the handle,
        or it will be left open until the debugger exits. Use with care!

    @see:
        L{ProcessHandle}, L{ThreadHandle}, L{FileHandle}, L{SnapshotHandle}
    """

    # XXX DEBUG
    # When this private flag is True each Handle will print a message to
    # standard output when it's created and destroyed. This is useful for
    # detecting handle leaks within WinAppDbg itself.
    __bLeakDetection = False

    def __init__(self, aHandle=None, bOwnership=True):
        """
        @type  aHandle: int
        @param aHandle: Win32 handle value.

        @type  bOwnership: bool
        @param bOwnership:
           C{True} if we own the handle and we need to close it.
           C{False} if someone else will be calling L{CloseHandle}.
        """
        super(Handle, self).__init__()
        self._value = self._normalize(aHandle)
        self.bOwnership = bOwnership
        if Handle.__bLeakDetection:  # XXX DEBUG
            print("INIT HANDLE (%r) %r" % (self.value, self))

    @property
    def value(self):
        return self._value

    def __del__(self):
        """
        Closes the Win32 handle when the Python object is destroyed.
        """
        try:
            if Handle.__bLeakDetection:  # XXX DEBUG
                print("DEL HANDLE %r" % self)
            self.close()
        except Exception:
            pass

    def __enter__(self):
        """
        Compatibility with the "C{with}" Python statement.
        """
        if Handle.__bLeakDetection:  # XXX DEBUG
            print("ENTER HANDLE %r" % self)
        return self

    def __exit__(self, type, value, traceback):
        """
        Compatibility with the "C{with}" Python statement.
        """
        if Handle.__bLeakDetection:  # XXX DEBUG
            print("EXIT HANDLE %r" % self)
        try:
            self.close()
        except Exception:
            pass

    def __copy__(self):
        """
        Duplicates the Win32 handle when copying the Python object.

        @rtype:  L{Handle}
        @return: A new handle to the same Win32 object.
        """
        return self.dup()

    def __deepcopy__(self):
        """
        Duplicates the Win32 handle when copying the Python object.

        @rtype:  L{Handle}
        @return: A new handle to the same win32 object.
        """
        return self.dup()

    @property
    def _as_parameter_(self):
        """
        Compatibility with ctypes.
        Allows passing transparently a Handle object to an API call.
        """
        return HANDLE(self.value)

    @staticmethod
    def from_param(value):
        """
        Compatibility with ctypes.
        Allows passing transparently a Handle object to an API call.

        @type  value: int
        @param value: Numeric handle value.
        """
        return HANDLE(value)

    def close(self):
        """
        Closes the Win32 handle.
        """
        if self.bOwnership and self.value not in (None, INVALID_HANDLE_VALUE):
            if Handle.__bLeakDetection:  # XXX DEBUG
                print("CLOSE HANDLE (%d) %r" % (self.value, self))
            try:
                self._close()
            finally:
                self._value = None

    def _close(self):
        """
        Low-level close method.
        This is a private method, do not call it.
        """
        CloseHandle(self.value)

    def dup(self):
        """
        @rtype:  L{Handle}
        @return: A new handle to the same Win32 object.
        """
        if self.value is None:
            raise ValueError("Closed handles can't be duplicated!")
        new_handle = DuplicateHandle(self.value)
        if Handle.__bLeakDetection:  # XXX DEBUG
            print("DUP HANDLE (%d -> %d) %r %r" % (self.value, new_handle.value, self, new_handle))
        return new_handle

    @staticmethod
    def _normalize(value):
        """
        Normalize handle values.
        """
        if hasattr(value, "value"):
            value = value.value
        if value is not None:
            value = long(value)
        return value

    def wait(self, dwMilliseconds=None):
        """
        Wait for the Win32 object to be signaled.

        @type  dwMilliseconds: int
        @param dwMilliseconds: (Optional) Timeout value in milliseconds.
            Use C{INFINITE} or C{None} for no timeout.
        """
        if self.value is None:
            raise ValueError("Handle is already closed!")
        if dwMilliseconds is None:
            dwMilliseconds = INFINITE
        r = WaitForSingleObject(self.value, dwMilliseconds)
        if r != WAIT_OBJECT_0:
            raise ctypes.WinError(r)

    def __repr__(self):
        return "<%s: %s>" % (self.__class__.__name__, self.value)

    def __get_inherit(self):
        if self.value is None:
            raise ValueError("Handle is already closed!")
        return bool(GetHandleInformation(self.value) & HANDLE_FLAG_INHERIT)

    def __set_inherit(self, value):
        if self.value is None:
            raise ValueError("Handle is already closed!")
        flag = (0, HANDLE_FLAG_INHERIT)[bool(value)]
        SetHandleInformation(self.value, flag, flag)

    inherit = property(__get_inherit, __set_inherit)

    def __get_protectFromClose(self):
        if self.value is None:
            raise ValueError("Handle is already closed!")
        return bool(GetHandleInformation(self.value) & HANDLE_FLAG_PROTECT_FROM_CLOSE)

    def __set_protectFromClose(self, value):
        if self.value is None:
            raise ValueError("Handle is already closed!")
        flag = (0, HANDLE_FLAG_PROTECT_FROM_CLOSE)[bool(value)]
        SetHandleInformation(self.value, flag, flag)

    protectFromClose = property(__get_protectFromClose, __set_protectFromClose)


class UserModeHandle(Handle):
    """
    Base class for non-kernel handles. Generally this means they are closed
    by special Win32 API functions instead of CloseHandle() and some standard
    operations (synchronizing, duplicating, inheritance) are not supported.

    @type _TYPE: C type
    @cvar _TYPE: C type to translate this handle to.
        Subclasses should override this.
        Defaults to L{HANDLE}.
    """

    # Subclasses should override this.
    _TYPE = HANDLE

    # This method must be implemented by subclasses.
    def _close(self):
        raise NotImplementedError()

    # Translation to C type.
    @property
    def _as_parameter_(self):
        return self._TYPE(self.value)

    # Translation to C type.
    @staticmethod
    def from_param(value):
        return self._TYPE(self.value)

    # Operation not supported.
    @property
    def inherit(self):
        return False

    # Operation not supported.
    @property
    def protectFromClose(self):
        return False

    # Operation not supported.
    def dup(self):
        raise NotImplementedError()

    # Operation not supported.
    def wait(self, dwMilliseconds=None):
        raise NotImplementedError()


class ProcessHandle(Handle):
    """
    Win32 process handle.

    @type dwAccess: int
    @ivar dwAccess: Current access flags to this handle.
            This is the same value passed to L{OpenProcess}.
            Can only be C{None} if C{aHandle} is also C{None}.
            Defaults to L{PROCESS_ALL_ACCESS}.

    @see: L{Handle}
    """

    def __init__(self, aHandle=None, bOwnership=True, dwAccess=PROCESS_ALL_ACCESS):
        """
        @type  aHandle: int
        @param aHandle: Win32 handle value.

        @type  bOwnership: bool
        @param bOwnership:
           C{True} if we own the handle and we need to close it.
           C{False} if someone else will be calling L{CloseHandle}.

        @type  dwAccess: int
        @param dwAccess: Current access flags to this handle.
            This is the same value passed to L{OpenProcess}.
            Can only be C{None} if C{aHandle} is also C{None}.
            Defaults to L{PROCESS_ALL_ACCESS}.
        """
        super(ProcessHandle, self).__init__(aHandle, bOwnership)
        self.dwAccess = dwAccess
        if aHandle is not None and dwAccess is None:
            msg = "Missing access flags for process handle: %x" % aHandle
            raise TypeError(msg)

    def get_pid(self):
        """
        @rtype:  int
        @return: Process global ID.
        """
        return GetProcessId(self.value)


class ThreadHandle(Handle):
    """
    Win32 thread handle.

    @type dwAccess: int
    @ivar dwAccess: Current access flags to this handle.
            This is the same value passed to L{OpenThread}.
            Can only be C{None} if C{aHandle} is also C{None}.
            Defaults to L{THREAD_ALL_ACCESS}.

    @see: L{Handle}
    """

    def __init__(self, aHandle=None, bOwnership=True, dwAccess=THREAD_ALL_ACCESS):
        """
        @type  aHandle: int
        @param aHandle: Win32 handle value.

        @type  bOwnership: bool
        @param bOwnership:
           C{True} if we own the handle and we need to close it.
           C{False} if someone else will be calling L{CloseHandle}.

        @type  dwAccess: int
        @param dwAccess: Current access flags to this handle.
            This is the same value passed to L{OpenThread}.
            Can only be C{None} if C{aHandle} is also C{None}.
            Defaults to L{THREAD_ALL_ACCESS}.
        """
        super(ThreadHandle, self).__init__(aHandle, bOwnership)
        self.dwAccess = dwAccess
        if aHandle is not None and dwAccess is None:
            msg = "Missing access flags for thread handle: %x" % aHandle
            raise TypeError(msg)

    def get_tid(self):
        """
        @rtype:  int
        @return: Thread global ID.
        """
        return GetThreadId(self.value)


class FileHandle(Handle):
    """
    Win32 file handle.

    @see: L{Handle}
    """

    def get_filename(self):
        """
        @rtype:  None or str
        @return: Name of the open file, or C{None} if unavailable.
        """
        #
        # XXX BUG
        #
        # This code truncates the first two bytes of the path.
        # It seems to be the expected behavior of NtQueryInformationFile.
        #
        # My guess is it only returns the NT pathname, without the device name.
        # It's like dropping the drive letter in a Win32 pathname.
        #
        # Note that using the "official" GetFileInformationByHandleEx
        # API introduced in Vista doesn't change the results!
        #
        dwBufferSize = 0x1004
        lpFileInformation = ctypes.create_string_buffer(dwBufferSize)
        try:
            GetFileInformationByHandleEx(self.value, FILE_INFO_BY_HANDLE_CLASS.FileNameInfo, lpFileInformation, dwBufferSize)
        except AttributeError:
            from winappdbg.win32.ntdll import NtQueryInformationFile, FileNameInformation, FILE_NAME_INFORMATION

            NtQueryInformationFile(self.value, FileNameInformation, lpFileInformation, dwBufferSize)
        FileName = compat.unicode(lpFileInformation.raw[sizeof(DWORD) :], "U16")
        FileName = ctypes.create_unicode_buffer(FileName).value
        if not FileName:
            FileName = None
        elif FileName[1:2] != ":":
            # When the drive letter is missing, we'll assume SYSTEMROOT.
            # Not a good solution but it could be worse.
            import os

            FileName = os.environ["SYSTEMROOT"][:2] + FileName
        return FileName


class FileMappingHandle(Handle):
    """
    File mapping handle.

    @see: L{Handle}
    """

    pass


# XXX maybe add functions related to the toolhelp snapshots here?
class SnapshotHandle(Handle):
    """
    Toolhelp32 snapshot handle.

    @see: L{Handle}
    """

    pass


# --- Structure wrappers -------------------------------------------------------


class ProcessInformation(object):
    """
    Process information object returned by L{CreateProcess}.
    """

    def __init__(self, pi):
        self.hProcess = ProcessHandle(pi.hProcess)
        self.hThread = ThreadHandle(pi.hThread)
        self.dwProcessId = pi.dwProcessId
        self.dwThreadId = pi.dwThreadId


# Don't psyco-optimize this class because it needs to be serialized.
class MemoryBasicInformation(object):
    """
    Memory information object returned by L{VirtualQueryEx}.
    """

    READABLE = PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY

    WRITEABLE = PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY | PAGE_READWRITE | PAGE_WRITECOPY

    COPY_ON_WRITE = PAGE_EXECUTE_WRITECOPY | PAGE_WRITECOPY

    EXECUTABLE = PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY

    EXECUTABLE_AND_WRITEABLE = PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY

    def __init__(self, mbi=None):
        """
        @type  mbi: L{MEMORY_BASIC_INFORMATION} or L{MemoryBasicInformation}
        @param mbi: Either a L{MEMORY_BASIC_INFORMATION} structure or another
            L{MemoryBasicInformation} instance.
        """
        if mbi is None:
            self.BaseAddress = None
            self.AllocationBase = None
            self.AllocationProtect = None
            self.RegionSize = None
            self.State = None
            self.Protect = None
            self.Type = None
        else:
            self.BaseAddress = mbi.BaseAddress
            self.AllocationBase = mbi.AllocationBase
            self.AllocationProtect = mbi.AllocationProtect
            self.RegionSize = mbi.RegionSize
            self.State = mbi.State
            self.Protect = mbi.Protect
            self.Type = mbi.Type

            # Only used when copying MemoryBasicInformation objects, instead of
            # instancing them from a MEMORY_BASIC_INFORMATION structure.
            if hasattr(mbi, "content"):
                self.content = mbi.content
            if hasattr(mbi, "filename"):
                self.content = mbi.filename

    def __contains__(self, address):
        """
        Test if the given memory address falls within this memory region.

        @type  address: int
        @param address: Memory address to test.

        @rtype:  bool
        @return: C{True} if the given memory address falls within this memory
            region, C{False} otherwise.
        """
        return self.BaseAddress <= address < (self.BaseAddress + self.RegionSize)

    def is_free(self):
        """
        @rtype:  bool
        @return: C{True} if the memory in this region is free.
        """
        return self.State == MEM_FREE

    def is_reserved(self):
        """
        @rtype:  bool
        @return: C{True} if the memory in this region is reserved.
        """
        return self.State == MEM_RESERVE

    def is_commited(self):
        """
        @rtype:  bool
        @return: C{True} if the memory in this region is commited.
        """
        return self.State == MEM_COMMIT

    def is_image(self):
        """
        @rtype:  bool
        @return: C{True} if the memory in this region belongs to an executable
            image.
        """
        return self.Type == MEM_IMAGE

    def is_mapped(self):
        """
        @rtype:  bool
        @return: C{True} if the memory in this region belongs to a mapped file.
        """
        return self.Type == MEM_MAPPED

    def is_private(self):
        """
        @rtype:  bool
        @return: C{True} if the memory in this region is private.
        """
        return self.Type == MEM_PRIVATE

    def is_guard(self):
        """
        @rtype:  bool
        @return: C{True} if all pages in this region are guard pages.
        """
        return self.is_commited() and bool(self.Protect & PAGE_GUARD)

    def has_content(self):
        """
        @rtype:  bool
        @return: C{True} if the memory in this region has any data in it.
        """
        return self.is_commited() and not bool(self.Protect & (PAGE_GUARD | PAGE_NOACCESS))

    def is_readable(self):
        """
        @rtype:  bool
        @return: C{True} if all pages in this region are readable.
        """
        return self.has_content() and bool(self.Protect & self.READABLE)

    def is_writeable(self):
        """
        @rtype:  bool
        @return: C{True} if all pages in this region are writeable.
        """
        return self.has_content() and bool(self.Protect & self.WRITEABLE)

    def is_copy_on_write(self):
        """
        @rtype:  bool
        @return: C{True} if all pages in this region are marked as
            copy-on-write. This means the pages are writeable, but changes
            are not propagated to disk.
        @note:
            Tipically data sections in executable images are marked like this.
        """
        return self.has_content() and bool(self.Protect & self.COPY_ON_WRITE)

    def is_executable(self):
        """
        @rtype:  bool
        @return: C{True} if all pages in this region are executable.
        @note: Executable pages are always readable.
        """
        return self.has_content() and bool(self.Protect & self.EXECUTABLE)

    def is_executable_and_writeable(self):
        """
        @rtype:  bool
        @return: C{True} if all pages in this region are executable and
            writeable.
        @note: The presence of such pages make memory corruption
            vulnerabilities much easier to exploit.
        """
        return self.has_content() and bool(self.Protect & self.EXECUTABLE_AND_WRITEABLE)


class ProcThreadAttributeList(object):
    """
    Extended process and thread attribute support.

    To be used with L{STARTUPINFOEX}.
    Only available for Windows Vista and above.

    @type AttributeList: list of tuple( int, ctypes-compatible object )
    @ivar AttributeList: List of (Attribute, Value) pairs.

    @type AttributeListBuffer: L{LPPROC_THREAD_ATTRIBUTE_LIST}
    @ivar AttributeListBuffer: Memory buffer used to store the attribute list.
        L{InitializeProcThreadAttributeList},
        L{UpdateProcThreadAttribute},
        L{DeleteProcThreadAttributeList} and
        L{STARTUPINFOEX}.
    """

    def __init__(self, AttributeList):
        """
        @type  AttributeList: list of tuple( int, ctypes-compatible object )
        @param AttributeList: List of (Attribute, Value) pairs.
        """
        self.AttributeList = AttributeList
        self.AttributeListBuffer = InitializeProcThreadAttributeList(len(AttributeList))
        try:
            for Attribute, Value in AttributeList:
                UpdateProcThreadAttribute(self.AttributeListBuffer, Attribute, Value)
        except:
            ProcThreadAttributeList.__del__(self)
            raise

    def __del__(self):
        try:
            DeleteProcThreadAttributeList(self.AttributeListBuffer)
            del self.AttributeListBuffer
        except Exception:
            pass

    def __copy__(self):
        return self.__deepcopy__()

    def __deepcopy__(self):
        return self.__class__(self.AttributeList)

    @property
    def value(self):
        return ctypes.cast(ctypes.pointer(self.AttributeListBuffer), LPVOID)

    @property
    def _as_parameter_(self):
        return self.value

    # XXX TODO
    @staticmethod
    def from_param(value):
        raise NotImplementedError()


# --- OVERLAPPED structure -----------------------------------------------------


# typedef struct _OVERLAPPED {
#   ULONG_PTR Internal;
#   ULONG_PTR InternalHigh;
#   union {
#     struct {
#       DWORD Offset;
#       DWORD OffsetHigh;
#     } ;
#     PVOID Pointer;
#   } ;
#   HANDLE    hEvent;
# }OVERLAPPED, *LPOVERLAPPED;
class _OVERLAPPED_STRUCT(Structure):
    _fields_ = [
        ("Offset", DWORD),
        ("OffsetHigh", DWORD),
    ]


class _OVERLAPPED_UNION(Union):
    _fields_ = [
        ("s", _OVERLAPPED_STRUCT),
        ("Pointer", PVOID),
    ]


class OVERLAPPED(Structure):
    _fields_ = [
        ("Internal", ULONG_PTR),
        ("InternalHigh", ULONG_PTR),
        ("u", _OVERLAPPED_UNION),
        ("hEvent", HANDLE),
    ]


LPOVERLAPPED = POINTER(OVERLAPPED)

# --- SECURITY_ATTRIBUTES structure --------------------------------------------


# typedef struct _SECURITY_ATTRIBUTES {
#     DWORD nLength;
#     LPVOID lpSecurityDescriptor;
#     BOOL bInheritHandle;
# } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
class SECURITY_ATTRIBUTES(Structure):
    _fields_ = [
        ("nLength", DWORD),
        ("lpSecurityDescriptor", LPVOID),
        ("bInheritHandle", BOOL),
    ]


LPSECURITY_ATTRIBUTES = POINTER(SECURITY_ATTRIBUTES)

# --- Extended process and thread attribute support ---------------------------

PPROC_THREAD_ATTRIBUTE_LIST = LPVOID
LPPROC_THREAD_ATTRIBUTE_LIST = PPROC_THREAD_ATTRIBUTE_LIST

PROC_THREAD_ATTRIBUTE_NUMBER = 0x0000FFFF
PROC_THREAD_ATTRIBUTE_THREAD = 0x00010000  # Attribute may be used with thread creation
PROC_THREAD_ATTRIBUTE_INPUT = 0x00020000  # Attribute is input only
PROC_THREAD_ATTRIBUTE_ADDITIVE = 0x00040000  # Attribute may be "accumulated," e.g. bitmasks, counters, etc.

# PROC_THREAD_ATTRIBUTE_NUM
ProcThreadAttributeParentProcess = 0
ProcThreadAttributeExtendedFlags = 1
ProcThreadAttributeHandleList = 2
ProcThreadAttributeGroupAffinity = 3
ProcThreadAttributePreferredNode = 4
ProcThreadAttributeIdealProcessor = 5
ProcThreadAttributeUmsThread = 6
ProcThreadAttributeMitigationPolicy = 7
ProcThreadAttributeMax = 8

PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = ProcThreadAttributeParentProcess | PROC_THREAD_ATTRIBUTE_INPUT
PROC_THREAD_ATTRIBUTE_EXTENDED_FLAGS = ProcThreadAttributeExtendedFlags | PROC_THREAD_ATTRIBUTE_INPUT | PROC_THREAD_ATTRIBUTE_ADDITIVE
PROC_THREAD_ATTRIBUTE_HANDLE_LIST = ProcThreadAttributeHandleList | PROC_THREAD_ATTRIBUTE_INPUT
PROC_THREAD_ATTRIBUTE_GROUP_AFFINITY = ProcThreadAttributeGroupAffinity | PROC_THREAD_ATTRIBUTE_THREAD | PROC_THREAD_ATTRIBUTE_INPUT
PROC_THREAD_ATTRIBUTE_PREFERRED_NODE = ProcThreadAttributePreferredNode | PROC_THREAD_ATTRIBUTE_INPUT
PROC_THREAD_ATTRIBUTE_IDEAL_PROCESSOR = ProcThreadAttributeIdealProcessor | PROC_THREAD_ATTRIBUTE_THREAD | PROC_THREAD_ATTRIBUTE_INPUT
PROC_THREAD_ATTRIBUTE_UMS_THREAD = ProcThreadAttributeUmsThread | PROC_THREAD_ATTRIBUTE_THREAD | PROC_THREAD_ATTRIBUTE_INPUT
PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY = ProcThreadAttributeMitigationPolicy | PROC_THREAD_ATTRIBUTE_INPUT

PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE = 0x01
PROCESS_CREATION_MITIGATION_POLICY_DEP_ATL_THUNK_ENABLE = 0x02
PROCESS_CREATION_MITIGATION_POLICY_SEHOP_ENABLE = 0x04

# --- VS_FIXEDFILEINFO structure -----------------------------------------------


# struct VS_FIXEDFILEINFO {
#   DWORD dwSignature;
#   DWORD dwStrucVersion;
#   DWORD dwFileVersionMS;
#   DWORD dwFileVersionLS;
#   DWORD dwProductVersionMS;
#   DWORD dwProductVersionLS;
#   DWORD dwFileFlagsMask;
#   DWORD dwFileFlags;
#   DWORD dwFileOS;
#   DWORD dwFileType;
#   DWORD dwFileSubtype;
#   DWORD dwFileDateMS;
#   DWORD dwFileDateLS;
# };
class VS_FIXEDFILEINFO(Structure):
    _fields_ = [
        ("dwSignature", DWORD),  # 0xFEEF04BD
        ("dwStrucVersion", DWORD),
        ("dwFileVersionMS", DWORD),
        ("dwFileVersionLS", DWORD),
        ("dwProductVersionMS", DWORD),
        ("dwProductVersionLS", DWORD),
        ("dwFileFlagsMask", DWORD),
        ("dwFileFlags", DWORD),
        ("dwFileOS", DWORD),
        ("dwFileType", DWORD),
        ("dwFileSubtype", DWORD),
        ("dwFileDateMS", DWORD),
        ("dwFileDateLS", DWORD),
    ]


# --- THREADNAME_INFO structure ------------------------------------------------


# typedef struct tagTHREADNAME_INFO
# {
#    DWORD dwType; // Must be 0x1000.
#    LPCSTR szName; // Pointer to name (in user addr space).
#    DWORD dwThreadID; // Thread ID (-1=caller thread).
#    DWORD dwFlags; // Reserved for future use, must be zero.
# } THREADNAME_INFO;
class THREADNAME_INFO(Structure):
    _fields_ = [
        ("dwType", DWORD),  # 0x1000
        ("szName", LPVOID),  # remote pointer
        ("dwThreadID", DWORD),  # -1 usually
        ("dwFlags", DWORD),  # 0
    ]


# --- MEMORY_BASIC_INFORMATION structure ---------------------------------------


# typedef struct _MEMORY_BASIC_INFORMATION32 {
#     DWORD BaseAddress;
#     DWORD AllocationBase;
#     DWORD AllocationProtect;
#     DWORD RegionSize;
#     DWORD State;
#     DWORD Protect;
#     DWORD Type;
# } MEMORY_BASIC_INFORMATION32, *PMEMORY_BASIC_INFORMATION32;
class MEMORY_BASIC_INFORMATION32(Structure):
    _fields_ = [
        ("BaseAddress", DWORD),  # remote pointer
        ("AllocationBase", DWORD),  # remote pointer
        ("AllocationProtect", DWORD),
        ("RegionSize", DWORD),
        ("State", DWORD),
        ("Protect", DWORD),
        ("Type", DWORD),
    ]


# typedef struct DECLSPEC_ALIGN(16) _MEMORY_BASIC_INFORMATION64 {
#     ULONGLONG BaseAddress;
#     ULONGLONG AllocationBase;
#     DWORD     AllocationProtect;
#     DWORD     __alignment1;
#     ULONGLONG RegionSize;
#     DWORD     State;
#     DWORD     Protect;
#     DWORD     Type;
#     DWORD     __alignment2;
# } MEMORY_BASIC_INFORMATION64, *PMEMORY_BASIC_INFORMATION64;
class MEMORY_BASIC_INFORMATION64(Structure):
    _fields_ = [
        ("BaseAddress", ULONGLONG),  # remote pointer
        ("AllocationBase", ULONGLONG),  # remote pointer
        ("AllocationProtect", DWORD),
        ("__alignment1", DWORD),
        ("RegionSize", ULONGLONG),
        ("State", DWORD),
        ("Protect", DWORD),
        ("Type", DWORD),
        ("__alignment2", DWORD),
    ]


# typedef struct _MEMORY_BASIC_INFORMATION {
#     PVOID BaseAddress;
#     PVOID AllocationBase;
#     DWORD AllocationProtect;
#     SIZE_T RegionSize;
#     DWORD State;
#     DWORD Protect;
#     DWORD Type;
# } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
class MEMORY_BASIC_INFORMATION(Structure):
    _fields_ = [
        ("BaseAddress", SIZE_T),  # remote pointer
        ("AllocationBase", SIZE_T),  # remote pointer
        ("AllocationProtect", DWORD),
        ("RegionSize", SIZE_T),
        ("State", DWORD),
        ("Protect", DWORD),
        ("Type", DWORD),
    ]


PMEMORY_BASIC_INFORMATION = POINTER(MEMORY_BASIC_INFORMATION)

# --- BY_HANDLE_FILE_INFORMATION structure -------------------------------------


# typedef struct _FILETIME {
#    DWORD dwLowDateTime;
#    DWORD dwHighDateTime;
# } FILETIME, *PFILETIME;
class FILETIME(Structure):
    _fields_ = [
        ("dwLowDateTime", DWORD),
        ("dwHighDateTime", DWORD),
    ]


LPFILETIME = POINTER(FILETIME)


# typedef struct _SYSTEMTIME {
#   WORD wYear;
#   WORD wMonth;
#   WORD wDayOfWeek;
#   WORD wDay;
#   WORD wHour;
#   WORD wMinute;
#   WORD wSecond;
#   WORD wMilliseconds;
# }SYSTEMTIME, *PSYSTEMTIME;
class SYSTEMTIME(Structure):
    _fields_ = [
        ("wYear", WORD),
        ("wMonth", WORD),
        ("wDayOfWeek", WORD),
        ("wDay", WORD),
        ("wHour", WORD),
        ("wMinute", WORD),
        ("wSecond", WORD),
        ("wMilliseconds", WORD),
    ]


LPSYSTEMTIME = POINTER(SYSTEMTIME)


# typedef struct _BY_HANDLE_FILE_INFORMATION {
#   DWORD dwFileAttributes;
#   FILETIME ftCreationTime;
#   FILETIME ftLastAccessTime;
#   FILETIME ftLastWriteTime;
#   DWORD dwVolumeSerialNumber;
#   DWORD nFileSizeHigh;
#   DWORD nFileSizeLow;
#   DWORD nNumberOfLinks;
#   DWORD nFileIndexHigh;
#   DWORD nFileIndexLow;
# } BY_HANDLE_FILE_INFORMATION, *PBY_HANDLE_FILE_INFORMATION;
class BY_HANDLE_FILE_INFORMATION(Structure):
    _fields_ = [
        ("dwFileAttributes", DWORD),
        ("ftCreationTime", FILETIME),
        ("ftLastAccessTime", FILETIME),
        ("ftLastWriteTime", FILETIME),
        ("dwVolumeSerialNumber", DWORD),
        ("nFileSizeHigh", DWORD),
        ("nFileSizeLow", DWORD),
        ("nNumberOfLinks", DWORD),
        ("nFileIndexHigh", DWORD),
        ("nFileIndexLow", DWORD),
    ]


LPBY_HANDLE_FILE_INFORMATION = POINTER(BY_HANDLE_FILE_INFORMATION)


# typedef enum _FILE_INFO_BY_HANDLE_CLASS {
#   FileBasicInfo = 0,
#   FileStandardInfo = 1,
#   FileNameInfo = 2,
#   FileRenameInfo = 3,
#   FileDispositionInfo = 4,
#   FileAllocationInfo = 5,
#   FileEndOfFileInfo = 6,
#   FileStreamInfo = 7,
#   FileCompressionInfo = 8,
#   FileAttributeTagInfo = 9,
#   FileIdBothDirectoryInfo = 10,
#   FileIdBothDirectoryRestartInfo = 11,
#   FileIoPriorityHintInfo = 12,
#   MaximumFileInfoByHandlesClass = 13
# } FILE_INFO_BY_HANDLE_CLASS, *PFILE_INFO_BY_HANDLE_CLASS;
class FILE_INFO_BY_HANDLE_CLASS(object):
    FileBasicInfo = 0
    FileStandardInfo = 1
    FileNameInfo = 2
    FileRenameInfo = 3
    FileDispositionInfo = 4
    FileAllocationInfo = 5
    FileEndOfFileInfo = 6
    FileStreamInfo = 7
    FileCompressionInfo = 8
    FileAttributeTagInfo = 9
    FileIdBothDirectoryInfo = 10
    FileIdBothDirectoryRestartInfo = 11
    FileIoPriorityHintInfo = 12
    MaximumFileInfoByHandlesClass = 13


# typedef struct _FILE_NAME_INFO {
#   DWORD  FileNameLength;
#   WCHAR FileName[1];
# } FILE_NAME_INFO, *PFILE_NAME_INFO;
##class FILE_NAME_INFO(Structure):
##    _fields_ = [
##        ('FileNameLength',  DWORD),
##        ('FileName',        WCHAR * 1),
##    ]

# TO DO: add more structures used by GetFileInformationByHandleEx()

# --- PROCESS_INFORMATION structure --------------------------------------------


# typedef struct _PROCESS_INFORMATION {
#     HANDLE hProcess;
#     HANDLE hThread;
#     DWORD dwProcessId;
#     DWORD dwThreadId;
# } PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
class PROCESS_INFORMATION(Structure):
    _fields_ = [
        ("hProcess", HANDLE),
        ("hThread", HANDLE),
        ("dwProcessId", DWORD),
        ("dwThreadId", DWORD),
    ]


LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)

# --- STARTUPINFO and STARTUPINFOEX structures ---------------------------------


# typedef struct _STARTUPINFO {
#   DWORD  cb;
#   LPTSTR lpReserved;
#   LPTSTR lpDesktop;
#   LPTSTR lpTitle;
#   DWORD  dwX;
#   DWORD  dwY;
#   DWORD  dwXSize;
#   DWORD  dwYSize;
#   DWORD  dwXCountChars;
#   DWORD  dwYCountChars;
#   DWORD  dwFillAttribute;
#   DWORD  dwFlags;
#   WORD   wShowWindow;
#   WORD   cbReserved2;
#   LPBYTE lpReserved2;
#   HANDLE hStdInput;
#   HANDLE hStdOutput;
#   HANDLE hStdError;
# }STARTUPINFO, *LPSTARTUPINFO;
class STARTUPINFO(Structure):
    _fields_ = [
        ("cb", DWORD),
        ("lpReserved", LPSTR),
        ("lpDesktop", LPSTR),
        ("lpTitle", LPSTR),
        ("dwX", DWORD),
        ("dwY", DWORD),
        ("dwXSize", DWORD),
        ("dwYSize", DWORD),
        ("dwXCountChars", DWORD),
        ("dwYCountChars", DWORD),
        ("dwFillAttribute", DWORD),
        ("dwFlags", DWORD),
        ("wShowWindow", WORD),
        ("cbReserved2", WORD),
        ("lpReserved2", LPVOID),  # LPBYTE
        ("hStdInput", HANDLE),
        ("hStdOutput", HANDLE),
        ("hStdError", HANDLE),
    ]


LPSTARTUPINFO = POINTER(STARTUPINFO)


# typedef struct _STARTUPINFOEX {
#   STARTUPINFO StartupInfo;
#   PPROC_THREAD_ATTRIBUTE_LIST lpAttributeList;
# } STARTUPINFOEX,  *LPSTARTUPINFOEX;
class STARTUPINFOEX(Structure):
    _fields_ = [
        ("StartupInfo", STARTUPINFO),
        ("lpAttributeList", PPROC_THREAD_ATTRIBUTE_LIST),
    ]


LPSTARTUPINFOEX = POINTER(STARTUPINFOEX)


class STARTUPINFOW(Structure):
    _fields_ = [
        ("cb", DWORD),
        ("lpReserved", LPWSTR),
        ("lpDesktop", LPWSTR),
        ("lpTitle", LPWSTR),
        ("dwX", DWORD),
        ("dwY", DWORD),
        ("dwXSize", DWORD),
        ("dwYSize", DWORD),
        ("dwXCountChars", DWORD),
        ("dwYCountChars", DWORD),
        ("dwFillAttribute", DWORD),
        ("dwFlags", DWORD),
        ("wShowWindow", WORD),
        ("cbReserved2", WORD),
        ("lpReserved2", LPVOID),  # LPBYTE
        ("hStdInput", HANDLE),
        ("hStdOutput", HANDLE),
        ("hStdError", HANDLE),
    ]


LPSTARTUPINFOW = POINTER(STARTUPINFOW)


class STARTUPINFOEXW(Structure):
    _fields_ = [
        ("StartupInfo", STARTUPINFOW),
        ("lpAttributeList", PPROC_THREAD_ATTRIBUTE_LIST),
    ]


LPSTARTUPINFOEXW = POINTER(STARTUPINFOEXW)

# --- JIT_DEBUG_INFO structure -------------------------------------------------


# typedef struct _JIT_DEBUG_INFO {
#     DWORD dwSize;
#     DWORD dwProcessorArchitecture;
#     DWORD dwThreadID;
#     DWORD dwReserved0;
#     ULONG64 lpExceptionAddress;
#     ULONG64 lpExceptionRecord;
#     ULONG64 lpContextRecord;
# } JIT_DEBUG_INFO, *LPJIT_DEBUG_INFO;
class JIT_DEBUG_INFO(Structure):
    _fields_ = [
        ("dwSize", DWORD),
        ("dwProcessorArchitecture", DWORD),
        ("dwThreadID", DWORD),
        ("dwReserved0", DWORD),
        ("lpExceptionAddress", ULONG64),
        ("lpExceptionRecord", ULONG64),
        ("lpContextRecord", ULONG64),
    ]


JIT_DEBUG_INFO32 = JIT_DEBUG_INFO
JIT_DEBUG_INFO64 = JIT_DEBUG_INFO

LPJIT_DEBUG_INFO = POINTER(JIT_DEBUG_INFO)
LPJIT_DEBUG_INFO32 = POINTER(JIT_DEBUG_INFO32)
LPJIT_DEBUG_INFO64 = POINTER(JIT_DEBUG_INFO64)

# --- DEBUG_EVENT structure ----------------------------------------------------


# typedef struct _EXCEPTION_RECORD32 {
#     DWORD ExceptionCode;
#     DWORD ExceptionFlags;
#     DWORD ExceptionRecord;
#     DWORD ExceptionAddress;
#     DWORD NumberParameters;
#     DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
# } EXCEPTION_RECORD32, *PEXCEPTION_RECORD32;
class EXCEPTION_RECORD32(Structure):
    _fields_ = [
        ("ExceptionCode", DWORD),
        ("ExceptionFlags", DWORD),
        ("ExceptionRecord", DWORD),
        ("ExceptionAddress", DWORD),
        ("NumberParameters", DWORD),
        ("ExceptionInformation", DWORD * EXCEPTION_MAXIMUM_PARAMETERS),
    ]


PEXCEPTION_RECORD32 = POINTER(EXCEPTION_RECORD32)


# typedef struct _EXCEPTION_RECORD64 {
#     DWORD    ExceptionCode;
#     DWORD ExceptionFlags;
#     DWORD64 ExceptionRecord;
#     DWORD64 ExceptionAddress;
#     DWORD NumberParameters;
#     DWORD __unusedAlignment;
#     DWORD64 ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
# } EXCEPTION_RECORD64, *PEXCEPTION_RECORD64;
class EXCEPTION_RECORD64(Structure):
    _fields_ = [
        ("ExceptionCode", DWORD),
        ("ExceptionFlags", DWORD),
        ("ExceptionRecord", DWORD64),
        ("ExceptionAddress", DWORD64),
        ("NumberParameters", DWORD),
        ("__unusedAlignment", DWORD),
        ("ExceptionInformation", DWORD64 * EXCEPTION_MAXIMUM_PARAMETERS),
    ]


PEXCEPTION_RECORD64 = POINTER(EXCEPTION_RECORD64)


# typedef struct _EXCEPTION_RECORD {
#     DWORD ExceptionCode;
#     DWORD ExceptionFlags;
#     LPVOID ExceptionRecord;
#     LPVOID ExceptionAddress;
#     DWORD NumberParameters;
#     LPVOID ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
# } EXCEPTION_RECORD, *PEXCEPTION_RECORD;
class EXCEPTION_RECORD(Structure):
    pass


PEXCEPTION_RECORD = POINTER(EXCEPTION_RECORD)
EXCEPTION_RECORD._fields_ = [
    ("ExceptionCode", DWORD),
    ("ExceptionFlags", DWORD),
    ("ExceptionRecord", PEXCEPTION_RECORD),
    ("ExceptionAddress", LPVOID),
    ("NumberParameters", DWORD),
    ("ExceptionInformation", LPVOID * EXCEPTION_MAXIMUM_PARAMETERS),
]


# typedef struct _EXCEPTION_DEBUG_INFO {
#   EXCEPTION_RECORD ExceptionRecord;
#   DWORD dwFirstChance;
# } EXCEPTION_DEBUG_INFO;
class EXCEPTION_DEBUG_INFO(Structure):
    _fields_ = [
        ("ExceptionRecord", EXCEPTION_RECORD),
        ("dwFirstChance", DWORD),
    ]


# typedef struct _CREATE_THREAD_DEBUG_INFO {
#   HANDLE hThread;
#   LPVOID lpThreadLocalBase;
#   LPTHREAD_START_ROUTINE lpStartAddress;
# } CREATE_THREAD_DEBUG_INFO;
class CREATE_THREAD_DEBUG_INFO(Structure):
    _fields_ = [
        ("hThread", HANDLE),
        ("lpThreadLocalBase", LPVOID),
        ("lpStartAddress", LPVOID),
    ]


# typedef struct _CREATE_PROCESS_DEBUG_INFO {
#   HANDLE hFile;
#   HANDLE hProcess;
#   HANDLE hThread;
#   LPVOID lpBaseOfImage;
#   DWORD dwDebugInfoFileOffset;
#   DWORD nDebugInfoSize;
#   LPVOID lpThreadLocalBase;
#   LPTHREAD_START_ROUTINE lpStartAddress;
#   LPVOID lpImageName;
#   WORD fUnicode;
# } CREATE_PROCESS_DEBUG_INFO;
class CREATE_PROCESS_DEBUG_INFO(Structure):
    _fields_ = [
        ("hFile", HANDLE),
        ("hProcess", HANDLE),
        ("hThread", HANDLE),
        ("lpBaseOfImage", LPVOID),
        ("dwDebugInfoFileOffset", DWORD),
        ("nDebugInfoSize", DWORD),
        ("lpThreadLocalBase", LPVOID),
        ("lpStartAddress", LPVOID),
        ("lpImageName", LPVOID),
        ("fUnicode", WORD),
    ]


# typedef struct _EXIT_THREAD_DEBUG_INFO {
#   DWORD dwExitCode;
# } EXIT_THREAD_DEBUG_INFO;
class EXIT_THREAD_DEBUG_INFO(Structure):
    _fields_ = [
        ("dwExitCode", DWORD),
    ]


# typedef struct _EXIT_PROCESS_DEBUG_INFO {
#   DWORD dwExitCode;
# } EXIT_PROCESS_DEBUG_INFO;
class EXIT_PROCESS_DEBUG_INFO(Structure):
    _fields_ = [
        ("dwExitCode", DWORD),
    ]


# typedef struct _LOAD_DLL_DEBUG_INFO {
#   HANDLE hFile;
#   LPVOID lpBaseOfDll;
#   DWORD dwDebugInfoFileOffset;
#   DWORD nDebugInfoSize;
#   LPVOID lpImageName;
#   WORD fUnicode;
# } LOAD_DLL_DEBUG_INFO;
class LOAD_DLL_DEBUG_INFO(Structure):
    _fields_ = [
        ("hFile", HANDLE),
        ("lpBaseOfDll", LPVOID),
        ("dwDebugInfoFileOffset", DWORD),
        ("nDebugInfoSize", DWORD),
        ("lpImageName", LPVOID),
        ("fUnicode", WORD),
    ]


# typedef struct _UNLOAD_DLL_DEBUG_INFO {
#   LPVOID lpBaseOfDll;
# } UNLOAD_DLL_DEBUG_INFO;
class UNLOAD_DLL_DEBUG_INFO(Structure):
    _fields_ = [
        ("lpBaseOfDll", LPVOID),
    ]


# typedef struct _OUTPUT_DEBUG_STRING_INFO {
#   LPSTR lpDebugStringData;
#   WORD fUnicode;
#   WORD nDebugStringLength;
# } OUTPUT_DEBUG_STRING_INFO;
class OUTPUT_DEBUG_STRING_INFO(Structure):
    _fields_ = [
        ("lpDebugStringData", LPVOID),  # don't use LPSTR
        ("fUnicode", WORD),
        ("nDebugStringLength", WORD),
    ]


# typedef struct _RIP_INFO {
#     DWORD dwError;
#     DWORD dwType;
# } RIP_INFO, *LPRIP_INFO;
class RIP_INFO(Structure):
    _fields_ = [
        ("dwError", DWORD),
        ("dwType", DWORD),
    ]


# typedef struct _DEBUG_EVENT {
#   DWORD dwDebugEventCode;
#   DWORD dwProcessId;
#   DWORD dwThreadId;
#   union {
#     EXCEPTION_DEBUG_INFO Exception;
#     CREATE_THREAD_DEBUG_INFO CreateThread;
#     CREATE_PROCESS_DEBUG_INFO CreateProcessInfo;
#     EXIT_THREAD_DEBUG_INFO ExitThread;
#     EXIT_PROCESS_DEBUG_INFO ExitProcess;
#     LOAD_DLL_DEBUG_INFO LoadDll;
#     UNLOAD_DLL_DEBUG_INFO UnloadDll;
#     OUTPUT_DEBUG_STRING_INFO DebugString;
#     RIP_INFO RipInfo;
#   } u;
# } DEBUG_EVENT;.
class _DEBUG_EVENT_UNION_(Union):
    _fields_ = [
        ("Exception", EXCEPTION_DEBUG_INFO),
        ("CreateThread", CREATE_THREAD_DEBUG_INFO),
        ("CreateProcessInfo", CREATE_PROCESS_DEBUG_INFO),
        ("ExitThread", EXIT_THREAD_DEBUG_INFO),
        ("ExitProcess", EXIT_PROCESS_DEBUG_INFO),
        ("LoadDll", LOAD_DLL_DEBUG_INFO),
        ("UnloadDll", UNLOAD_DLL_DEBUG_INFO),
        ("DebugString", OUTPUT_DEBUG_STRING_INFO),
        ("RipInfo", RIP_INFO),
    ]


class DEBUG_EVENT(Structure):
    _fields_ = [
        ("dwDebugEventCode", DWORD),
        ("dwProcessId", DWORD),
        ("dwThreadId", DWORD),
        ("u", _DEBUG_EVENT_UNION_),
    ]


LPDEBUG_EVENT = POINTER(DEBUG_EVENT)

# --- Console API defines and structures ---------------------------------------

FOREGROUND_MASK = 0x000F
BACKGROUND_MASK = 0x00F0
COMMON_LVB_MASK = 0xFF00

FOREGROUND_BLACK = 0x0000
FOREGROUND_BLUE = 0x0001
FOREGROUND_GREEN = 0x0002
FOREGROUND_CYAN = 0x0003
FOREGROUND_RED = 0x0004
FOREGROUND_MAGENTA = 0x0005
FOREGROUND_YELLOW = 0x0006
FOREGROUND_GREY = 0x0007
FOREGROUND_INTENSITY = 0x0008

BACKGROUND_BLACK = 0x0000
BACKGROUND_BLUE = 0x0010
BACKGROUND_GREEN = 0x0020
BACKGROUND_CYAN = 0x0030
BACKGROUND_RED = 0x0040
BACKGROUND_MAGENTA = 0x0050
BACKGROUND_YELLOW = 0x0060
BACKGROUND_GREY = 0x0070
BACKGROUND_INTENSITY = 0x0080

COMMON_LVB_LEADING_BYTE = 0x0100
COMMON_LVB_TRAILING_BYTE = 0x0200
COMMON_LVB_GRID_HORIZONTAL = 0x0400
COMMON_LVB_GRID_LVERTICAL = 0x0800
COMMON_LVB_GRID_RVERTICAL = 0x1000
COMMON_LVB_REVERSE_VIDEO = 0x4000
COMMON_LVB_UNDERSCORE = 0x8000


# typedef struct _CHAR_INFO {
#   union {
#     WCHAR UnicodeChar;
#     CHAR  AsciiChar;
#   } Char;
#   WORD  Attributes;
# } CHAR_INFO, *PCHAR_INFO;
class _CHAR_INFO_CHAR(Union):
    _fields_ = [
        ("UnicodeChar", WCHAR),
        ("AsciiChar", CHAR),
    ]


class CHAR_INFO(Structure):
    _fields_ = [
        ("Char", _CHAR_INFO_CHAR),
        ("Attributes", WORD),
    ]


PCHAR_INFO = POINTER(CHAR_INFO)


# typedef struct _COORD {
#   SHORT X;
#   SHORT Y;
# } COORD, *PCOORD;
class COORD(Structure):
    _fields_ = [
        ("X", SHORT),
        ("Y", SHORT),
    ]


PCOORD = POINTER(COORD)


# typedef struct _SMALL_RECT {
#   SHORT Left;
#   SHORT Top;
#   SHORT Right;
#   SHORT Bottom;
# } SMALL_RECT;
class SMALL_RECT(Structure):
    _fields_ = [
        ("Left", SHORT),
        ("Top", SHORT),
        ("Right", SHORT),
        ("Bottom", SHORT),
    ]


PSMALL_RECT = POINTER(SMALL_RECT)


# typedef struct _CONSOLE_SCREEN_BUFFER_INFO {
#   COORD      dwSize;
#   COORD      dwCursorPosition;
#   WORD       wAttributes;
#   SMALL_RECT srWindow;
#   COORD      dwMaximumWindowSize;
# } CONSOLE_SCREEN_BUFFER_INFO;
class CONSOLE_SCREEN_BUFFER_INFO(Structure):
    _fields_ = [
        ("dwSize", COORD),
        ("dwCursorPosition", COORD),
        ("wAttributes", WORD),
        ("srWindow", SMALL_RECT),
        ("dwMaximumWindowSize", COORD),
    ]


PCONSOLE_SCREEN_BUFFER_INFO = POINTER(CONSOLE_SCREEN_BUFFER_INFO)

# --- Toolhelp library defines and structures ----------------------------------

TH32CS_SNAPHEAPLIST = 0x00000001
TH32CS_SNAPPROCESS = 0x00000002
TH32CS_SNAPTHREAD = 0x00000004
TH32CS_SNAPMODULE = 0x00000008
TH32CS_INHERIT = 0x80000000
TH32CS_SNAPALL = TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE


# typedef struct tagTHREADENTRY32 {
#   DWORD dwSize;
#   DWORD cntUsage;
#   DWORD th32ThreadID;
#   DWORD th32OwnerProcessID;
#   LONG tpBasePri;
#   LONG tpDeltaPri;
#   DWORD dwFlags;
# } THREADENTRY32,  *PTHREADENTRY32;
class THREADENTRY32(Structure):
    _fields_ = [
        ("dwSize", DWORD),
        ("cntUsage", DWORD),
        ("th32ThreadID", DWORD),
        ("th32OwnerProcessID", DWORD),
        ("tpBasePri", LONG),
        ("tpDeltaPri", LONG),
        ("dwFlags", DWORD),
    ]


LPTHREADENTRY32 = POINTER(THREADENTRY32)


# typedef struct tagPROCESSENTRY32 {
#    DWORD dwSize;
#    DWORD cntUsage;
#    DWORD th32ProcessID;
#    ULONG_PTR th32DefaultHeapID;
#    DWORD th32ModuleID;
#    DWORD cntThreads;
#    DWORD th32ParentProcessID;
#    LONG pcPriClassBase;
#    DWORD dwFlags;
#    TCHAR szExeFile[MAX_PATH];
# } PROCESSENTRY32,  *PPROCESSENTRY32;
class PROCESSENTRY32(Structure):
    _fields_ = [
        ("dwSize", DWORD),
        ("cntUsage", DWORD),
        ("th32ProcessID", DWORD),
        ("th32DefaultHeapID", ULONG_PTR),
        ("th32ModuleID", DWORD),
        ("cntThreads", DWORD),
        ("th32ParentProcessID", DWORD),
        ("pcPriClassBase", LONG),
        ("dwFlags", DWORD),
        ("szExeFile", TCHAR * 260),
    ]


LPPROCESSENTRY32 = POINTER(PROCESSENTRY32)


# typedef struct tagMODULEENTRY32 {
#   DWORD dwSize;
#   DWORD th32ModuleID;
#   DWORD th32ProcessID;
#   DWORD GlblcntUsage;
#   DWORD ProccntUsage;
#   BYTE* modBaseAddr;
#   DWORD modBaseSize;
#   HMODULE hModule;
#   TCHAR szModule[MAX_MODULE_NAME32 + 1];
#   TCHAR szExePath[MAX_PATH];
# } MODULEENTRY32,  *PMODULEENTRY32;
class MODULEENTRY32(Structure):
    _fields_ = [
        ("dwSize", DWORD),
        ("th32ModuleID", DWORD),
        ("th32ProcessID", DWORD),
        ("GlblcntUsage", DWORD),
        ("ProccntUsage", DWORD),
        ("modBaseAddr", LPVOID),  # BYTE*
        ("modBaseSize", DWORD),
        ("hModule", HMODULE),
        ("szModule", TCHAR * (MAX_MODULE_NAME32 + 1)),
        ("szExePath", TCHAR * MAX_PATH),
    ]


LPMODULEENTRY32 = POINTER(MODULEENTRY32)


# typedef struct tagHEAPENTRY32 {
#   SIZE_T    dwSize;
#   HANDLE    hHandle;
#   ULONG_PTR dwAddress;
#   SIZE_T    dwBlockSize;
#   DWORD     dwFlags;
#   DWORD     dwLockCount;
#   DWORD     dwResvd;
#   DWORD     th32ProcessID;
#   ULONG_PTR th32HeapID;
# } HEAPENTRY32,
# *PHEAPENTRY32;
class HEAPENTRY32(Structure):
    _fields_ = [
        ("dwSize", SIZE_T),
        ("hHandle", HANDLE),
        ("dwAddress", ULONG_PTR),
        ("dwBlockSize", SIZE_T),
        ("dwFlags", DWORD),
        ("dwLockCount", DWORD),
        ("dwResvd", DWORD),
        ("th32ProcessID", DWORD),
        ("th32HeapID", ULONG_PTR),
    ]


LPHEAPENTRY32 = POINTER(HEAPENTRY32)


# typedef struct tagHEAPLIST32 {
#   SIZE_T    dwSize;
#   DWORD     th32ProcessID;
#   ULONG_PTR th32HeapID;
#   DWORD     dwFlags;
# } HEAPLIST32,
#  *PHEAPLIST32;
class HEAPLIST32(Structure):
    _fields_ = [
        ("dwSize", SIZE_T),
        ("th32ProcessID", DWORD),
        ("th32HeapID", ULONG_PTR),
        ("dwFlags", DWORD),
    ]


LPHEAPLIST32 = POINTER(HEAPLIST32)

# --- kernel32.dll -------------------------------------------------------------


# DWORD WINAPI GetLastError(void);
def GetLastError():
    _GetLastError = windll.kernel32.GetLastError
    _GetLastError.argtypes = []
    _GetLastError.restype = DWORD
    return _GetLastError()


# void WINAPI SetLastError(
#   __in  DWORD dwErrCode
# );
def SetLastError(dwErrCode):
    _SetLastError = windll.kernel32.SetLastError
    _SetLastError.argtypes = [DWORD]
    _SetLastError.restype = None
    _SetLastError(dwErrCode)


# UINT WINAPI GetErrorMode(void);
def GetErrorMode():
    _GetErrorMode = windll.kernel32.GetErrorMode
    _GetErrorMode.argtypes = []
    _GetErrorMode.restype = UINT
    return _GetErrorMode()


# UINT WINAPI SetErrorMode(
#   __in  UINT uMode
# );
def SetErrorMode(uMode):
    _SetErrorMode = windll.kernel32.SetErrorMode
    _SetErrorMode.argtypes = [UINT]
    _SetErrorMode.restype = UINT
    return _SetErrorMode(dwErrCode)


# DWORD GetThreadErrorMode(void);
def GetThreadErrorMode():
    _GetThreadErrorMode = windll.kernel32.GetThreadErrorMode
    _GetThreadErrorMode.argtypes = []
    _GetThreadErrorMode.restype = DWORD
    return _GetThreadErrorMode()


# BOOL SetThreadErrorMode(
#   __in   DWORD dwNewMode,
#   __out  LPDWORD lpOldMode
# );
def SetThreadErrorMode(dwNewMode):
    _SetThreadErrorMode = windll.kernel32.SetThreadErrorMode
    _SetThreadErrorMode.argtypes = [DWORD, LPDWORD]
    _SetThreadErrorMode.restype = BOOL
    _SetThreadErrorMode.errcheck = RaiseIfZero

    old = DWORD(0)
    _SetThreadErrorMode(dwErrCode, byref(old))
    return old.value


# BOOL WINAPI CloseHandle(
#   __in  HANDLE hObject
# );
def CloseHandle(hHandle):
    if isinstance(hHandle, Handle):
        # Prevents the handle from being closed without notifying the Handle object.
        hHandle.close()
    else:
        _CloseHandle = windll.kernel32.CloseHandle
        _CloseHandle.argtypes = [HANDLE]
        _CloseHandle.restype = bool
        _CloseHandle.errcheck = RaiseIfZero
        _CloseHandle(hHandle)


# BOOL WINAPI DuplicateHandle(
#   __in   HANDLE hSourceProcessHandle,
#   __in   HANDLE hSourceHandle,
#   __in   HANDLE hTargetProcessHandle,
#   __out  LPHANDLE lpTargetHandle,
#   __in   DWORD dwDesiredAccess,
#   __in   BOOL bInheritHandle,
#   __in   DWORD dwOptions
# );
def DuplicateHandle(
    hSourceHandle,
    hSourceProcessHandle=None,
    hTargetProcessHandle=None,
    dwDesiredAccess=STANDARD_RIGHTS_ALL,
    bInheritHandle=False,
    dwOptions=DUPLICATE_SAME_ACCESS,
):
    _DuplicateHandle = windll.kernel32.DuplicateHandle
    _DuplicateHandle.argtypes = [HANDLE, HANDLE, HANDLE, LPHANDLE, DWORD, BOOL, DWORD]
    _DuplicateHandle.restype = bool
    _DuplicateHandle.errcheck = RaiseIfZero

    # NOTE: the arguments to this function are in a different order,
    # so we can set default values for all of them but one (hSourceHandle).

    if hSourceProcessHandle is None:
        hSourceProcessHandle = GetCurrentProcess()
    if hTargetProcessHandle is None:
        hTargetProcessHandle = hSourceProcessHandle
    lpTargetHandle = HANDLE(INVALID_HANDLE_VALUE)
    _DuplicateHandle(
        hSourceProcessHandle, hSourceHandle, hTargetProcessHandle, byref(lpTargetHandle), dwDesiredAccess, bool(bInheritHandle), dwOptions
    )
    if isinstance(hSourceHandle, Handle):
        HandleClass = hSourceHandle.__class__
    else:
        HandleClass = Handle
    if hasattr(hSourceHandle, "dwAccess"):
        return HandleClass(lpTargetHandle.value, dwAccess=hSourceHandle.dwAccess)
    else:
        return HandleClass(lpTargetHandle.value)


# HLOCAL WINAPI LocalFree(
#   __in  HLOCAL hMem
# );
def LocalFree(hMem):
    _LocalFree = windll.kernel32.LocalFree
    _LocalFree.argtypes = [HLOCAL]
    _LocalFree.restype = HLOCAL

    result = _LocalFree(hMem)
    if result != NULL:
        ctypes.WinError()


# ------------------------------------------------------------------------------
# Console API


# HANDLE WINAPI GetStdHandle(
#   _In_  DWORD nStdHandle
# );
def GetStdHandle(nStdHandle):
    _GetStdHandle = windll.kernel32.GetStdHandle
    _GetStdHandle.argytpes = [DWORD]
    _GetStdHandle.restype = HANDLE
    _GetStdHandle.errcheck = RaiseIfZero
    return Handle(_GetStdHandle(nStdHandle), bOwnership=False)


# BOOL WINAPI SetStdHandle(
#   _In_  DWORD nStdHandle,
#   _In_  HANDLE hHandle
# );

# TODO


# UINT WINAPI GetConsoleCP(void);
def GetConsoleCP():
    _GetConsoleCP = windll.kernel32.GetConsoleCP
    _GetConsoleCP.argytpes = []
    _GetConsoleCP.restype = UINT
    return _GetConsoleCP()


# UINT WINAPI GetConsoleOutputCP(void);
def GetConsoleOutputCP():
    _GetConsoleOutputCP = windll.kernel32.GetConsoleOutputCP
    _GetConsoleOutputCP.argytpes = []
    _GetConsoleOutputCP.restype = UINT
    return _GetConsoleOutputCP()


# BOOL WINAPI SetConsoleCP(
#  _In_  UINT wCodePageID
# );
def SetConsoleCP(wCodePageID):
    _SetConsoleCP = windll.kernel32.SetConsoleCP
    _SetConsoleCP.argytpes = [UINT]
    _SetConsoleCP.restype = bool
    _SetConsoleCP.errcheck = RaiseIfZero
    _SetConsoleCP(wCodePageID)


# BOOL WINAPI SetConsoleOutputCP(
#  _In_  UINT wCodePageID
# );
def SetConsoleOutputCP(wCodePageID):
    _SetConsoleOutputCP = windll.kernel32.SetConsoleOutputCP
    _SetConsoleOutputCP.argytpes = [UINT]
    _SetConsoleOutputCP.restype = bool
    _SetConsoleOutputCP.errcheck = RaiseIfZero
    _SetConsoleOutputCP(wCodePageID)


# HANDLE WINAPI CreateConsoleScreenBuffer(
#   _In_        DWORD dwDesiredAccess,
#   _In_        DWORD dwShareMode,
#   _In_opt_    const SECURITY_ATTRIBUTES *lpSecurityAttributes,
#   _In_        DWORD dwFlags,
#   _Reserved_  LPVOID lpScreenBufferData
# );

# TODO


# BOOL WINAPI SetConsoleActiveScreenBuffer(
#   _In_  HANDLE hConsoleOutput
# );
def SetConsoleActiveScreenBuffer(hConsoleOutput=None):
    _SetConsoleActiveScreenBuffer = windll.kernel32.SetConsoleActiveScreenBuffer
    _SetConsoleActiveScreenBuffer.argytpes = [HANDLE]
    _SetConsoleActiveScreenBuffer.restype = bool
    _SetConsoleActiveScreenBuffer.errcheck = RaiseIfZero

    if hConsoleOutput is None:
        hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE)
    _SetConsoleActiveScreenBuffer(hConsoleOutput)


# BOOL WINAPI GetConsoleScreenBufferInfo(
#   _In_   HANDLE hConsoleOutput,
#   _Out_  PCONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo
# );
def GetConsoleScreenBufferInfo(hConsoleOutput=None):
    _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo
    _GetConsoleScreenBufferInfo.argytpes = [HANDLE, PCONSOLE_SCREEN_BUFFER_INFO]
    _GetConsoleScreenBufferInfo.restype = bool
    _GetConsoleScreenBufferInfo.errcheck = RaiseIfZero

    if hConsoleOutput is None:
        hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE)
    ConsoleScreenBufferInfo = CONSOLE_SCREEN_BUFFER_INFO()
    _GetConsoleScreenBufferInfo(hConsoleOutput, byref(ConsoleScreenBufferInfo))
    return ConsoleScreenBufferInfo


# BOOL WINAPI GetConsoleScreenBufferInfoEx(
#   _In_   HANDLE hConsoleOutput,
#   _Out_  PCONSOLE_SCREEN_BUFFER_INFOEX lpConsoleScreenBufferInfoEx
# );

# TODO


# BOOL WINAPI SetConsoleWindowInfo(
#   _In_  HANDLE hConsoleOutput,
#   _In_  BOOL bAbsolute,
#   _In_  const SMALL_RECT *lpConsoleWindow
# );
def SetConsoleWindowInfo(hConsoleOutput, bAbsolute, lpConsoleWindow):
    _SetConsoleWindowInfo = windll.kernel32.SetConsoleWindowInfo
    _SetConsoleWindowInfo.argytpes = [HANDLE, BOOL, PSMALL_RECT]
    _SetConsoleWindowInfo.restype = bool
    _SetConsoleWindowInfo.errcheck = RaiseIfZero

    if hConsoleOutput is None:
        hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE)
    if isinstance(lpConsoleWindow, SMALL_RECT):
        ConsoleWindow = lpConsoleWindow
    else:
        ConsoleWindow = SMALL_RECT(*lpConsoleWindow)
    _SetConsoleWindowInfo(hConsoleOutput, bAbsolute, byref(ConsoleWindow))


# BOOL WINAPI SetConsoleTextAttribute(
#   _In_  HANDLE hConsoleOutput,
#   _In_  WORD wAttributes
# );
def SetConsoleTextAttribute(hConsoleOutput=None, wAttributes=0):
    _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute
    _SetConsoleTextAttribute.argytpes = [HANDLE, WORD]
    _SetConsoleTextAttribute.restype = bool
    _SetConsoleTextAttribute.errcheck = RaiseIfZero

    if hConsoleOutput is None:
        hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE)
    _SetConsoleTextAttribute(hConsoleOutput, wAttributes)


# HANDLE WINAPI CreateConsoleScreenBuffer(
#   _In_        DWORD dwDesiredAccess,
#   _In_        DWORD dwShareMode,
#   _In_opt_    const SECURITY_ATTRIBUTES *lpSecurityAttributes,
#   _In_        DWORD dwFlags,
#   _Reserved_  LPVOID lpScreenBufferData
# );

# TODO


# BOOL WINAPI AllocConsole(void);
def AllocConsole():
    _AllocConsole = windll.kernel32.AllocConsole
    _AllocConsole.argytpes = []
    _AllocConsole.restype = bool
    _AllocConsole.errcheck = RaiseIfZero
    _AllocConsole()


# BOOL WINAPI AttachConsole(
#   _In_  DWORD dwProcessId
# );
def AttachConsole(dwProcessId=ATTACH_PARENT_PROCESS):
    _AttachConsole = windll.kernel32.AttachConsole
    _AttachConsole.argytpes = [DWORD]
    _AttachConsole.restype = bool
    _AttachConsole.errcheck = RaiseIfZero
    _AttachConsole(dwProcessId)


# BOOL WINAPI FreeConsole(void);
def FreeConsole():
    _FreeConsole = windll.kernel32.FreeConsole
    _FreeConsole.argytpes = []
    _FreeConsole.restype = bool
    _FreeConsole.errcheck = RaiseIfZero
    _FreeConsole()


# DWORD WINAPI GetConsoleProcessList(
#   _Out_  LPDWORD lpdwProcessList,
#   _In_   DWORD dwProcessCount
# );

# TODO

# DWORD WINAPI GetConsoleTitle(
#   _Out_  LPTSTR lpConsoleTitle,
#   _In_   DWORD nSize
# );

# TODO

# BOOL WINAPI SetConsoleTitle(
#  _In_  LPCTSTR lpConsoleTitle
# );

# TODO

# COORD WINAPI GetLargestConsoleWindowSize(
#   _In_  HANDLE hConsoleOutput
# );

# TODO

# BOOL WINAPI GetConsoleHistoryInfo(
#   _Out_  PCONSOLE_HISTORY_INFO lpConsoleHistoryInfo
# );

# TODO

# ------------------------------------------------------------------------------
# DLL API


# DWORD WINAPI GetDllDirectory(
#   __in   DWORD nBufferLength,
#   __out  LPTSTR lpBuffer
# );
def GetDllDirectoryA():
    _GetDllDirectoryA = windll.kernel32.GetDllDirectoryA
    _GetDllDirectoryA.argytpes = [DWORD, LPSTR]
    _GetDllDirectoryA.restype = DWORD

    nBufferLength = _GetDllDirectoryA(0, None)
    if nBufferLength == 0:
        return None
    lpBuffer = ctypes.create_string_buffer("", nBufferLength)
    _GetDllDirectoryA(nBufferLength, byref(lpBuffer))
    return lpBuffer.value


def GetDllDirectoryW():
    _GetDllDirectoryW = windll.kernel32.GetDllDirectoryW
    _GetDllDirectoryW.argytpes = [DWORD, LPWSTR]
    _GetDllDirectoryW.restype = DWORD

    nBufferLength = _GetDllDirectoryW(0, None)
    if nBufferLength == 0:
        return None
    lpBuffer = ctypes.create_unicode_buffer("", nBufferLength)
    _GetDllDirectoryW(nBufferLength, byref(lpBuffer))
    return lpBuffer.value


GetDllDirectory = GuessStringType(GetDllDirectoryA, GetDllDirectoryW)


# BOOL WINAPI SetDllDirectory(
#   __in_opt  LPCTSTR lpPathName
# );
def SetDllDirectoryA(lpPathName=None):
    _SetDllDirectoryA = windll.kernel32.SetDllDirectoryA
    _SetDllDirectoryA.argytpes = [LPSTR]
    _SetDllDirectoryA.restype = bool
    _SetDllDirectoryA.errcheck = RaiseIfZero
    _SetDllDirectoryA(lpPathName)


def SetDllDirectoryW(lpPathName):
    _SetDllDirectoryW = windll.kernel32.SetDllDirectoryW
    _SetDllDirectoryW.argytpes = [LPWSTR]
    _SetDllDirectoryW.restype = bool
    _SetDllDirectoryW.errcheck = RaiseIfZero
    _SetDllDirectoryW(lpPathName)


SetDllDirectory = GuessStringType(SetDllDirectoryA, SetDllDirectoryW)


# HMODULE WINAPI LoadLibrary(
#   __in  LPCTSTR lpFileName
# );
def LoadLibraryA(pszLibrary):
    _LoadLibraryA = windll.kernel32.LoadLibraryA
    _LoadLibraryA.argtypes = [LPSTR]
    _LoadLibraryA.restype = HMODULE
    hModule = _LoadLibraryA(pszLibrary)
    if hModule == NULL:
        raise ctypes.WinError()
    return hModule


def LoadLibraryW(pszLibrary):
    _LoadLibraryW = windll.kernel32.LoadLibraryW
    _LoadLibraryW.argtypes = [LPWSTR]
    _LoadLibraryW.restype = HMODULE
    hModule = _LoadLibraryW(pszLibrary)
    if hModule == NULL:
        raise ctypes.WinError()
    return hModule


LoadLibrary = GuessStringType(LoadLibraryA, LoadLibraryW)


# HMODULE WINAPI LoadLibraryEx(
#   __in        LPCTSTR lpFileName,
#   __reserved  HANDLE hFile,
#   __in        DWORD dwFlags
# );
def LoadLibraryExA(pszLibrary, dwFlags=0):
    _LoadLibraryExA = windll.kernel32.LoadLibraryExA
    _LoadLibraryExA.argtypes = [LPSTR, HANDLE, DWORD]
    _LoadLibraryExA.restype = HMODULE
    hModule = _LoadLibraryExA(pszLibrary, NULL, dwFlags)
    if hModule == NULL:
        raise ctypes.WinError()
    return hModule


def LoadLibraryExW(pszLibrary, dwFlags=0):
    _LoadLibraryExW = windll.kernel32.LoadLibraryExW
    _LoadLibraryExW.argtypes = [LPWSTR, HANDLE, DWORD]
    _LoadLibraryExW.restype = HMODULE
    hModule = _LoadLibraryExW(pszLibrary, NULL, dwFlags)
    if hModule == NULL:
        raise ctypes.WinError()
    return hModule


LoadLibraryEx = GuessStringType(LoadLibraryExA, LoadLibraryExW)


# HMODULE WINAPI GetModuleHandle(
#   __in_opt  LPCTSTR lpModuleName
# );
def GetModuleHandleA(lpModuleName):
    _GetModuleHandleA = windll.kernel32.GetModuleHandleA
    _GetModuleHandleA.argtypes = [LPSTR]
    _GetModuleHandleA.restype = HMODULE
    hModule = _GetModuleHandleA(lpModuleName)
    if hModule == NULL:
        raise ctypes.WinError()
    return hModule


def GetModuleHandleW(lpModuleName):
    _GetModuleHandleW = windll.kernel32.GetModuleHandleW
    _GetModuleHandleW.argtypes = [LPWSTR]
    _GetModuleHandleW.restype = HMODULE
    hModule = _GetModuleHandleW(lpModuleName)
    if hModule == NULL:
        raise ctypes.WinError()
    return hModule


GetModuleHandle = GuessStringType(GetModuleHandleA, GetModuleHandleW)


# FARPROC WINAPI GetProcAddress(
#   __in  HMODULE hModule,
#   __in  LPCSTR lpProcName
# );
def GetProcAddressA(hModule, lpProcName):
    _GetProcAddress = windll.kernel32.GetProcAddress
    _GetProcAddress.argtypes = [HMODULE, LPVOID]
    _GetProcAddress.restype = LPVOID

    if type(lpProcName) in (type(0), type(long(0))):
        lpProcName = LPVOID(lpProcName)
        if lpProcName.value & (~0xFFFF):
            raise ValueError("Ordinal number too large: %d" % lpProcName.value)
    elif type(lpProcName) == type(compat.b("")):
        lpProcName = ctypes.c_char_p(lpProcName)
    else:
        raise TypeError(str(type(lpProcName)))
    return _GetProcAddress(hModule, lpProcName)


GetProcAddressW = MakeWideVersion(GetProcAddressA)
GetProcAddress = GuessStringType(GetProcAddressA, GetProcAddressW)


# BOOL WINAPI FreeLibrary(
#   __in  HMODULE hModule
# );
def FreeLibrary(hModule):
    _FreeLibrary = windll.kernel32.FreeLibrary
    _FreeLibrary.argtypes = [HMODULE]
    _FreeLibrary.restype = bool
    _FreeLibrary.errcheck = RaiseIfZero
    _FreeLibrary(hModule)


# PVOID WINAPI RtlPcToFileHeader(
#   __in   PVOID PcValue,
#   __out  PVOID *BaseOfImage
# );
def RtlPcToFileHeader(PcValue):
    _RtlPcToFileHeader = windll.kernel32.RtlPcToFileHeader
    _RtlPcToFileHeader.argtypes = [PVOID, POINTER(PVOID)]
    _RtlPcToFileHeader.restype = PRUNTIME_FUNCTION

    BaseOfImage = PVOID(0)
    _RtlPcToFileHeader(PcValue, byref(BaseOfImage))
    return BaseOfImage.value


# ------------------------------------------------------------------------------
# File API and related


# BOOL WINAPI GetHandleInformation(
#   __in   HANDLE hObject,
#   __out  LPDWORD lpdwFlags
# );
def GetHandleInformation(hObject):
    _GetHandleInformation = windll.kernel32.GetHandleInformation
    _GetHandleInformation.argtypes = [HANDLE, PDWORD]
    _GetHandleInformation.restype = bool
    _GetHandleInformation.errcheck = RaiseIfZero

    dwFlags = DWORD(0)
    _GetHandleInformation(hObject, byref(dwFlags))
    return dwFlags.value


# BOOL WINAPI SetHandleInformation(
#   __in  HANDLE hObject,
#   __in  DWORD dwMask,
#   __in  DWORD dwFlags
# );
def SetHandleInformation(hObject, dwMask, dwFlags):
    _SetHandleInformation = windll.kernel32.SetHandleInformation
    _SetHandleInformation.argtypes = [HANDLE, DWORD, DWORD]
    _SetHandleInformation.restype = bool
    _SetHandleInformation.errcheck = RaiseIfZero
    _SetHandleInformation(hObject, dwMask, dwFlags)


# UINT WINAPI GetWindowModuleFileName(
#   __in   HWND hwnd,
#   __out  LPTSTR lpszFileName,
#   __in   UINT cchFileNameMax
# );
# Not included because it doesn't work in other processes.
# See: http://support.microsoft.com/?id=228469


# BOOL WINAPI QueryFullProcessImageName(
#   __in     HANDLE hProcess,
#   __in     DWORD dwFlags,
#   __out    LPTSTR lpExeName,
#   __inout  PDWORD lpdwSize
# );
def QueryFullProcessImageNameA(hProcess, dwFlags=0):
    _QueryFullProcessImageNameA = windll.kernel32.QueryFullProcessImageNameA
    _QueryFullProcessImageNameA.argtypes = [HANDLE, DWORD, LPSTR, PDWORD]
    _QueryFullProcessImageNameA.restype = bool

    dwSize = MAX_PATH
    while 1:
        lpdwSize = DWORD(dwSize)
        lpExeName = ctypes.create_string_buffer("", lpdwSize.value + 1)
        success = _QueryFullProcessImageNameA(hProcess, dwFlags, lpExeName, byref(lpdwSize))
        if success and 0 < lpdwSize.value < dwSize:
            break
        error = GetLastError()
        if error != ERROR_INSUFFICIENT_BUFFER:
            raise ctypes.WinError(error)
        dwSize = dwSize + 256
        if dwSize > 0x1000:
            # this prevents an infinite loop in Windows 2008 when the path has spaces,
            # see http://msdn.microsoft.com/en-us/library/ms684919(VS.85).aspx#4
            raise ctypes.WinError(error)
    return lpExeName.value


def QueryFullProcessImageNameW(hProcess, dwFlags=0):
    _QueryFullProcessImageNameW = windll.kernel32.QueryFullProcessImageNameW
    _QueryFullProcessImageNameW.argtypes = [HANDLE, DWORD, LPWSTR, PDWORD]
    _QueryFullProcessImageNameW.restype = bool

    dwSize = MAX_PATH
    while 1:
        lpdwSize = DWORD(dwSize)
        lpExeName = ctypes.create_unicode_buffer("", lpdwSize.value + 1)
        success = _QueryFullProcessImageNameW(hProcess, dwFlags, lpExeName, byref(lpdwSize))
        if success and 0 < lpdwSize.value < dwSize:
            break
        error = GetLastError()
        if error != ERROR_INSUFFICIENT_BUFFER:
            raise ctypes.WinError(error)
        dwSize = dwSize + 256
        if dwSize > 0x1000:
            # this prevents an infinite loop in Windows 2008 when the path has spaces,
            # see http://msdn.microsoft.com/en-us/library/ms684919(VS.85).aspx#4
            raise ctypes.WinError(error)
    return lpExeName.value


QueryFullProcessImageName = GuessStringType(QueryFullProcessImageNameA, QueryFullProcessImageNameW)


# DWORD WINAPI GetLogicalDriveStrings(
#   __in   DWORD nBufferLength,
#   __out  LPTSTR lpBuffer
# );
def GetLogicalDriveStringsA():
    _GetLogicalDriveStringsA = ctypes.windll.kernel32.GetLogicalDriveStringsA
    _GetLogicalDriveStringsA.argtypes = [DWORD, LPSTR]
    _GetLogicalDriveStringsA.restype = DWORD
    _GetLogicalDriveStringsA.errcheck = RaiseIfZero

    nBufferLength = (4 * 26) + 1  # "X:\\\0" from A to Z plus empty string
    lpBuffer = ctypes.create_string_buffer("", nBufferLength)
    _GetLogicalDriveStringsA(nBufferLength, lpBuffer)
    drive_strings = list()
    string_p = addressof(lpBuffer)
    sizeof_char = sizeof(ctypes.c_char)
    while True:
        string_v = ctypes.string_at(string_p)
        if string_v == "":
            break
        drive_strings.append(string_v)
        string_p += len(string_v) + sizeof_char
    return drive_strings


def GetLogicalDriveStringsW():
    _GetLogicalDriveStringsW = ctypes.windll.kernel32.GetLogicalDriveStringsW
    _GetLogicalDriveStringsW.argtypes = [DWORD, LPWSTR]
    _GetLogicalDriveStringsW.restype = DWORD
    _GetLogicalDriveStringsW.errcheck = RaiseIfZero

    nBufferLength = (4 * 26) + 1  # "X:\\\0" from A to Z plus empty string
    lpBuffer = ctypes.create_unicode_buffer("", nBufferLength)
    _GetLogicalDriveStringsW(nBufferLength, lpBuffer)
    drive_strings = list()
    string_p = addressof(lpBuffer)
    sizeof_wchar = sizeof(ctypes.c_wchar)
    while True:
        string_v = ctypes.wstring_at(string_p)
        if string_v == "":
            break
        drive_strings.append(string_v)
        string_p += (len(string_v) * sizeof_wchar) + sizeof_wchar
    return drive_strings


##def GetLogicalDriveStringsA():
##    _GetLogicalDriveStringsA = windll.kernel32.GetLogicalDriveStringsA
##    _GetLogicalDriveStringsA.argtypes = [DWORD, LPSTR]
##    _GetLogicalDriveStringsA.restype  = DWORD
##    _GetLogicalDriveStringsA.errcheck = RaiseIfZero
##
##    nBufferLength = (4 * 26) + 1    # "X:\\\0" from A to Z plus empty string
##    lpBuffer = ctypes.create_string_buffer('', nBufferLength)
##    _GetLogicalDriveStringsA(nBufferLength, lpBuffer)
##    result = list()
##    index = 0
##    while 1:
##        string = list()
##        while 1:
##            character = lpBuffer[index]
##            index = index + 1
##            if character == '\0':
##                break
##            string.append(character)
##        if not string:
##            break
##        result.append(''.join(string))
##    return result
##
##def GetLogicalDriveStringsW():
##    _GetLogicalDriveStringsW = windll.kernel32.GetLogicalDriveStringsW
##    _GetLogicalDriveStringsW.argtypes = [DWORD, LPWSTR]
##    _GetLogicalDriveStringsW.restype  = DWORD
##    _GetLogicalDriveStringsW.errcheck = RaiseIfZero
##
##    nBufferLength = (4 * 26) + 1    # "X:\\\0" from A to Z plus empty string
##    lpBuffer = ctypes.create_unicode_buffer(u'', nBufferLength)
##    _GetLogicalDriveStringsW(nBufferLength, lpBuffer)
##    result = list()
##    index = 0
##    while 1:
##        string = list()
##        while 1:
##            character = lpBuffer[index]
##            index = index + 1
##            if character == u'\0':
##                break
##            string.append(character)
##        if not string:
##            break
##        result.append(u''.join(string))
##    return result

GetLogicalDriveStrings = GuessStringType(GetLogicalDriveStringsA, GetLogicalDriveStringsW)


# DWORD WINAPI QueryDosDevice(
#   __in_opt  LPCTSTR lpDeviceName,
#   __out     LPTSTR lpTargetPath,
#   __in      DWORD ucchMax
# );
def QueryDosDeviceA(lpDeviceName=None):
    _QueryDosDeviceA = windll.kernel32.QueryDosDeviceA
    _QueryDosDeviceA.argtypes = [LPSTR, LPSTR, DWORD]
    _QueryDosDeviceA.restype = DWORD
    _QueryDosDeviceA.errcheck = RaiseIfZero

    if not lpDeviceName:
        lpDeviceName = None
    ucchMax = 0x1000
    lpTargetPath = ctypes.create_string_buffer("", ucchMax)
    _QueryDosDeviceA(lpDeviceName, lpTargetPath, ucchMax)
    return lpTargetPath.value


def QueryDosDeviceW(lpDeviceName):
    _QueryDosDeviceW = windll.kernel32.QueryDosDeviceW
    _QueryDosDeviceW.argtypes = [LPWSTR, LPWSTR, DWORD]
    _QueryDosDeviceW.restype = DWORD
    _QueryDosDeviceW.errcheck = RaiseIfZero

    if not lpDeviceName:
        lpDeviceName = None
    ucchMax = 0x1000
    lpTargetPath = ctypes.create_unicode_buffer("", ucchMax)
    _QueryDosDeviceW(lpDeviceName, lpTargetPath, ucchMax)
    return lpTargetPath.value


QueryDosDevice = GuessStringType(QueryDosDeviceA, QueryDosDeviceW)


# LPVOID WINAPI MapViewOfFile(
#   __in  HANDLE hFileMappingObject,
#   __in  DWORD dwDesiredAccess,
#   __in  DWORD dwFileOffsetHigh,
#   __in  DWORD dwFileOffsetLow,
#   __in  SIZE_T dwNumberOfBytesToMap
# );
def MapViewOfFile(
    hFileMappingObject,
    dwDesiredAccess=FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE,
    dwFileOffsetHigh=0,
    dwFileOffsetLow=0,
    dwNumberOfBytesToMap=0,
):
    _MapViewOfFile = windll.kernel32.MapViewOfFile
    _MapViewOfFile.argtypes = [HANDLE, DWORD, DWORD, DWORD, SIZE_T]
    _MapViewOfFile.restype = LPVOID
    lpBaseAddress = _MapViewOfFile(hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap)
    if lpBaseAddress == NULL:
        raise ctypes.WinError()
    return lpBaseAddress


# BOOL WINAPI UnmapViewOfFile(
#   __in  LPCVOID lpBaseAddress
# );
def UnmapViewOfFile(lpBaseAddress):
    _UnmapViewOfFile = windll.kernel32.UnmapViewOfFile
    _UnmapViewOfFile.argtypes = [LPVOID]
    _UnmapViewOfFile.restype = bool
    _UnmapViewOfFile.errcheck = RaiseIfZero
    _UnmapViewOfFile(lpBaseAddress)


# HANDLE WINAPI OpenFileMapping(
#   __in  DWORD dwDesiredAccess,
#   __in  BOOL bInheritHandle,
#   __in  LPCTSTR lpName
# );
def OpenFileMappingA(dwDesiredAccess, bInheritHandle, lpName):
    _OpenFileMappingA = windll.kernel32.OpenFileMappingA
    _OpenFileMappingA.argtypes = [DWORD, BOOL, LPSTR]
    _OpenFileMappingA.restype = HANDLE
    _OpenFileMappingA.errcheck = RaiseIfZero
    hFileMappingObject = _OpenFileMappingA(dwDesiredAccess, bool(bInheritHandle), lpName)
    return FileMappingHandle(hFileMappingObject)


def OpenFileMappingW(dwDesiredAccess, bInheritHandle, lpName):
    _OpenFileMappingW = windll.kernel32.OpenFileMappingW
    _OpenFileMappingW.argtypes = [DWORD, BOOL, LPWSTR]
    _OpenFileMappingW.restype = HANDLE
    _OpenFileMappingW.errcheck = RaiseIfZero
    hFileMappingObject = _OpenFileMappingW(dwDesiredAccess, bool(bInheritHandle), lpName)
    return FileMappingHandle(hFileMappingObject)


OpenFileMapping = GuessStringType(OpenFileMappingA, OpenFileMappingW)


# HANDLE WINAPI CreateFileMapping(
#   __in      HANDLE hFile,
#   __in_opt  LPSECURITY_ATTRIBUTES lpAttributes,
#   __in      DWORD flProtect,
#   __in      DWORD dwMaximumSizeHigh,
#   __in      DWORD dwMaximumSizeLow,
#   __in_opt  LPCTSTR lpName
# );
def CreateFileMappingA(hFile, lpAttributes=None, flProtect=PAGE_EXECUTE_READWRITE, dwMaximumSizeHigh=0, dwMaximumSizeLow=0, lpName=None):
    _CreateFileMappingA = windll.kernel32.CreateFileMappingA
    _CreateFileMappingA.argtypes = [HANDLE, LPVOID, DWORD, DWORD, DWORD, LPSTR]
    _CreateFileMappingA.restype = HANDLE
    _CreateFileMappingA.errcheck = RaiseIfZero

    if lpAttributes:
        lpAttributes = ctypes.pointer(lpAttributes)
    if not lpName:
        lpName = None
    hFileMappingObject = _CreateFileMappingA(hFile, lpAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, lpName)
    return FileMappingHandle(hFileMappingObject)


def CreateFileMappingW(hFile, lpAttributes=None, flProtect=PAGE_EXECUTE_READWRITE, dwMaximumSizeHigh=0, dwMaximumSizeLow=0, lpName=None):
    _CreateFileMappingW = windll.kernel32.CreateFileMappingW
    _CreateFileMappingW.argtypes = [HANDLE, LPVOID, DWORD, DWORD, DWORD, LPWSTR]
    _CreateFileMappingW.restype = HANDLE
    _CreateFileMappingW.errcheck = RaiseIfZero

    if lpAttributes:
        lpAttributes = ctypes.pointer(lpAttributes)
    if not lpName:
        lpName = None
    hFileMappingObject = _CreateFileMappingW(hFile, lpAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, lpName)
    return FileMappingHandle(hFileMappingObject)


CreateFileMapping = GuessStringType(CreateFileMappingA, CreateFileMappingW)


# HANDLE WINAPI CreateFile(
#   __in      LPCTSTR lpFileName,
#   __in      DWORD dwDesiredAccess,
#   __in      DWORD dwShareMode,
#   __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
#   __in      DWORD dwCreationDisposition,
#   __in      DWORD dwFlagsAndAttributes,
#   __in_opt  HANDLE hTemplateFile
# );
def CreateFileA(
    lpFileName,
    dwDesiredAccess=GENERIC_ALL,
    dwShareMode=0,
    lpSecurityAttributes=None,
    dwCreationDisposition=OPEN_ALWAYS,
    dwFlagsAndAttributes=FILE_ATTRIBUTE_NORMAL,
    hTemplateFile=None,
):
    _CreateFileA = windll.kernel32.CreateFileA
    _CreateFileA.argtypes = [LPSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, HANDLE]
    _CreateFileA.restype = HANDLE

    if not lpFileName:
        lpFileName = None
    if lpSecurityAttributes:
        lpSecurityAttributes = ctypes.pointer(lpSecurityAttributes)
    hFile = _CreateFileA(
        lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile
    )
    if hFile == INVALID_HANDLE_VALUE:
        raise ctypes.WinError()
    return FileHandle(hFile)


def CreateFileW(
    lpFileName,
    dwDesiredAccess=GENERIC_ALL,
    dwShareMode=0,
    lpSecurityAttributes=None,
    dwCreationDisposition=OPEN_ALWAYS,
    dwFlagsAndAttributes=FILE_ATTRIBUTE_NORMAL,
    hTemplateFile=None,
):
    _CreateFileW = windll.kernel32.CreateFileW
    _CreateFileW.argtypes = [LPWSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, HANDLE]
    _CreateFileW.restype = HANDLE

    if not lpFileName:
        lpFileName = None
    if lpSecurityAttributes:
        lpSecurityAttributes = ctypes.pointer(lpSecurityAttributes)
    hFile = _CreateFileW(
        lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile
    )
    if hFile == INVALID_HANDLE_VALUE:
        raise ctypes.WinError()
    return FileHandle(hFile)


CreateFile = GuessStringType(CreateFileA, CreateFileW)


# BOOL WINAPI FlushFileBuffers(
#   __in  HANDLE hFile
# );
def FlushFileBuffers(hFile):
    _FlushFileBuffers = windll.kernel32.FlushFileBuffers
    _FlushFileBuffers.argtypes = [HANDLE]
    _FlushFileBuffers.restype = bool
    _FlushFileBuffers.errcheck = RaiseIfZero
    _FlushFileBuffers(hFile)


# BOOL WINAPI FlushViewOfFile(
#   __in  LPCVOID lpBaseAddress,
#   __in  SIZE_T dwNumberOfBytesToFlush
# );
def FlushViewOfFile(lpBaseAddress, dwNumberOfBytesToFlush=0):
    _FlushViewOfFile = windll.kernel32.FlushViewOfFile
    _FlushViewOfFile.argtypes = [LPVOID, SIZE_T]
    _FlushViewOfFile.restype = bool
    _FlushViewOfFile.errcheck = RaiseIfZero
    _FlushViewOfFile(lpBaseAddress, dwNumberOfBytesToFlush)


# DWORD WINAPI SearchPath(
#   __in_opt   LPCTSTR lpPath,
#   __in       LPCTSTR lpFileName,
#   __in_opt   LPCTSTR lpExtension,
#   __in       DWORD nBufferLength,
#   __out      LPTSTR lpBuffer,
#   __out_opt  LPTSTR *lpFilePart
# );
def SearchPathA(lpPath, lpFileName, lpExtension):
    _SearchPathA = windll.kernel32.SearchPathA
    _SearchPathA.argtypes = [LPSTR, LPSTR, LPSTR, DWORD, LPSTR, POINTER(LPSTR)]
    _SearchPathA.restype = DWORD
    _SearchPathA.errcheck = RaiseIfZero

    if not lpPath:
        lpPath = None
    if not lpExtension:
        lpExtension = None
    nBufferLength = _SearchPathA(lpPath, lpFileName, lpExtension, 0, None, None)
    lpBuffer = ctypes.create_string_buffer("", nBufferLength + 1)
    lpFilePart = LPSTR()
    _SearchPathA(lpPath, lpFileName, lpExtension, nBufferLength, lpBuffer, byref(lpFilePart))
    lpFilePart = lpFilePart.value
    lpBuffer = lpBuffer.value
    if lpBuffer == "":
        if GetLastError() == ERROR_SUCCESS:
            raise ctypes.WinError(ERROR_FILE_NOT_FOUND)
        raise ctypes.WinError()
    return (lpBuffer, lpFilePart)


def SearchPathW(lpPath, lpFileName, lpExtension):
    _SearchPathW = windll.kernel32.SearchPathW
    _SearchPathW.argtypes = [LPWSTR, LPWSTR, LPWSTR, DWORD, LPWSTR, POINTER(LPWSTR)]
    _SearchPathW.restype = DWORD
    _SearchPathW.errcheck = RaiseIfZero

    if not lpPath:
        lpPath = None
    if not lpExtension:
        lpExtension = None
    nBufferLength = _SearchPathW(lpPath, lpFileName, lpExtension, 0, None, None)
    lpBuffer = ctypes.create_unicode_buffer("", nBufferLength + 1)
    lpFilePart = LPWSTR()
    _SearchPathW(lpPath, lpFileName, lpExtension, nBufferLength, lpBuffer, byref(lpFilePart))
    lpFilePart = lpFilePart.value
    lpBuffer = lpBuffer.value
    if lpBuffer == "":
        if GetLastError() == ERROR_SUCCESS:
            raise ctypes.WinError(ERROR_FILE_NOT_FOUND)
        raise ctypes.WinError()
    return (lpBuffer, lpFilePart)


SearchPath = GuessStringType(SearchPathA, SearchPathW)


# BOOL SetSearchPathMode(
#   __in  DWORD Flags
# );
def SetSearchPathMode(Flags):
    _SetSearchPathMode = windll.kernel32.SetSearchPathMode
    _SetSearchPathMode.argtypes = [DWORD]
    _SetSearchPathMode.restype = bool
    _SetSearchPathMode.errcheck = RaiseIfZero
    _SetSearchPathMode(Flags)


# BOOL WINAPI DeviceIoControl(
#   __in         HANDLE hDevice,
#   __in         DWORD dwIoControlCode,
#   __in_opt     LPVOID lpInBuffer,
#   __in         DWORD nInBufferSize,
#   __out_opt    LPVOID lpOutBuffer,
#   __in         DWORD nOutBufferSize,
#   __out_opt    LPDWORD lpBytesReturned,
#   __inout_opt  LPOVERLAPPED lpOverlapped
# );
def DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpOverlapped):
    _DeviceIoControl = windll.kernel32.DeviceIoControl
    _DeviceIoControl.argtypes = [HANDLE, DWORD, LPVOID, DWORD, LPVOID, DWORD, LPDWORD, LPOVERLAPPED]
    _DeviceIoControl.restype = bool
    _DeviceIoControl.errcheck = RaiseIfZero

    if not lpInBuffer:
        lpInBuffer = None
    if not lpOutBuffer:
        lpOutBuffer = None
    if lpOverlapped:
        lpOverlapped = ctypes.pointer(lpOverlapped)
    lpBytesReturned = DWORD(0)
    _DeviceIoControl(hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, byref(lpBytesReturned), lpOverlapped)
    return lpBytesReturned.value


# BOOL GetFileInformationByHandle(
#   HANDLE hFile,
#   LPBY_HANDLE_FILE_INFORMATION lpFileInformation
# );
def GetFileInformationByHandle(hFile):
    _GetFileInformationByHandle = windll.kernel32.GetFileInformationByHandle
    _GetFileInformationByHandle.argtypes = [HANDLE, LPBY_HANDLE_FILE_INFORMATION]
    _GetFileInformationByHandle.restype = bool
    _GetFileInformationByHandle.errcheck = RaiseIfZero

    lpFileInformation = BY_HANDLE_FILE_INFORMATION()
    _GetFileInformationByHandle(hFile, byref(lpFileInformation))
    return lpFileInformation


# BOOL WINAPI GetFileInformationByHandleEx(
#   __in   HANDLE hFile,
#   __in   FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
#   __out  LPVOID lpFileInformation,
#   __in   DWORD dwBufferSize
# );
def GetFileInformationByHandleEx(hFile, FileInformationClass, lpFileInformation, dwBufferSize):
    _GetFileInformationByHandleEx = windll.kernel32.GetFileInformationByHandleEx
    _GetFileInformationByHandleEx.argtypes = [HANDLE, DWORD, LPVOID, DWORD]
    _GetFileInformationByHandleEx.restype = bool
    _GetFileInformationByHandleEx.errcheck = RaiseIfZero
    # XXX TODO
    # support each FileInformationClass so the function can allocate the
    # corresponding structure for the lpFileInformation parameter
    _GetFileInformationByHandleEx(hFile, FileInformationClass, byref(lpFileInformation), dwBufferSize)


# DWORD WINAPI GetFinalPathNameByHandle(
#   __in   HANDLE hFile,
#   __out  LPTSTR lpszFilePath,
#   __in   DWORD cchFilePath,
#   __in   DWORD dwFlags
# );
def GetFinalPathNameByHandleA(hFile, dwFlags=FILE_NAME_NORMALIZED | VOLUME_NAME_DOS):
    _GetFinalPathNameByHandleA = windll.kernel32.GetFinalPathNameByHandleA
    _GetFinalPathNameByHandleA.argtypes = [HANDLE, LPSTR, DWORD, DWORD]
    _GetFinalPathNameByHandleA.restype = DWORD

    cchFilePath = _GetFinalPathNameByHandleA(hFile, None, 0, dwFlags)
    if cchFilePath == 0:
        raise ctypes.WinError()
    lpszFilePath = ctypes.create_string_buffer("", cchFilePath + 1)
    nCopied = _GetFinalPathNameByHandleA(hFile, lpszFilePath, cchFilePath, dwFlags)
    if nCopied <= 0 or nCopied > cchFilePath:
        raise ctypes.WinError()
    return lpszFilePath.value


def GetFinalPathNameByHandleW(hFile, dwFlags=FILE_NAME_NORMALIZED | VOLUME_NAME_DOS):
    _GetFinalPathNameByHandleW = windll.kernel32.GetFinalPathNameByHandleW
    _GetFinalPathNameByHandleW.argtypes = [HANDLE, LPWSTR, DWORD, DWORD]
    _GetFinalPathNameByHandleW.restype = DWORD

    cchFilePath = _GetFinalPathNameByHandleW(hFile, None, 0, dwFlags)
    if cchFilePath == 0:
        raise ctypes.WinError()
    lpszFilePath = ctypes.create_unicode_buffer("", cchFilePath + 1)
    nCopied = _GetFinalPathNameByHandleW(hFile, lpszFilePath, cchFilePath, dwFlags)
    if nCopied <= 0 or nCopied > cchFilePath:
        raise ctypes.WinError()
    return lpszFilePath.value


GetFinalPathNameByHandle = GuessStringType(GetFinalPathNameByHandleA, GetFinalPathNameByHandleW)


# DWORD GetFullPathName(
#   LPCTSTR lpFileName,
#   DWORD nBufferLength,
#   LPTSTR lpBuffer,
#   LPTSTR* lpFilePart
# );
def GetFullPathNameA(lpFileName):
    _GetFullPathNameA = windll.kernel32.GetFullPathNameA
    _GetFullPathNameA.argtypes = [LPSTR, DWORD, LPSTR, POINTER(LPSTR)]
    _GetFullPathNameA.restype = DWORD

    nBufferLength = _GetFullPathNameA(lpFileName, 0, None, None)
    if nBufferLength <= 0:
        raise ctypes.WinError()
    lpBuffer = ctypes.create_string_buffer("", nBufferLength + 1)
    lpFilePart = LPSTR()
    nCopied = _GetFullPathNameA(lpFileName, nBufferLength, lpBuffer, byref(lpFilePart))
    if nCopied > nBufferLength or nCopied == 0:
        raise ctypes.WinError()
    return lpBuffer.value, lpFilePart.value


def GetFullPathNameW(lpFileName):
    _GetFullPathNameW = windll.kernel32.GetFullPathNameW
    _GetFullPathNameW.argtypes = [LPWSTR, DWORD, LPWSTR, POINTER(LPWSTR)]
    _GetFullPathNameW.restype = DWORD

    nBufferLength = _GetFullPathNameW(lpFileName, 0, None, None)
    if nBufferLength <= 0:
        raise ctypes.WinError()
    lpBuffer = ctypes.create_unicode_buffer("", nBufferLength + 1)
    lpFilePart = LPWSTR()
    nCopied = _GetFullPathNameW(lpFileName, nBufferLength, lpBuffer, byref(lpFilePart))
    if nCopied > nBufferLength or nCopied == 0:
        raise ctypes.WinError()
    return lpBuffer.value, lpFilePart.value


GetFullPathName = GuessStringType(GetFullPathNameA, GetFullPathNameW)


# DWORD WINAPI GetTempPath(
#   __in   DWORD nBufferLength,
#   __out  LPTSTR lpBuffer
# );
def GetTempPathA():
    _GetTempPathA = windll.kernel32.GetTempPathA
    _GetTempPathA.argtypes = [DWORD, LPSTR]
    _GetTempPathA.restype = DWORD

    nBufferLength = _GetTempPathA(0, None)
    if nBufferLength <= 0:
        raise ctypes.WinError()
    lpBuffer = ctypes.create_string_buffer("", nBufferLength)
    nCopied = _GetTempPathA(nBufferLength, lpBuffer)
    if nCopied > nBufferLength or nCopied == 0:
        raise ctypes.WinError()
    return lpBuffer.value


def GetTempPathW():
    _GetTempPathW = windll.kernel32.GetTempPathW
    _GetTempPathW.argtypes = [DWORD, LPWSTR]
    _GetTempPathW.restype = DWORD

    nBufferLength = _GetTempPathW(0, None)
    if nBufferLength <= 0:
        raise ctypes.WinError()
    lpBuffer = ctypes.create_unicode_buffer("", nBufferLength)
    nCopied = _GetTempPathW(nBufferLength, lpBuffer)
    if nCopied > nBufferLength or nCopied == 0:
        raise ctypes.WinError()
    return lpBuffer.value


GetTempPath = GuessStringType(GetTempPathA, GetTempPathW)


# UINT WINAPI GetTempFileName(
#   __in   LPCTSTR lpPathName,
#   __in   LPCTSTR lpPrefixString,
#   __in   UINT uUnique,
#   __out  LPTSTR lpTempFileName
# );
def GetTempFileNameA(lpPathName=None, lpPrefixString="TMP", uUnique=0):
    _GetTempFileNameA = windll.kernel32.GetTempFileNameA
    _GetTempFileNameA.argtypes = [LPSTR, LPSTR, UINT, LPSTR]
    _GetTempFileNameA.restype = UINT

    if lpPathName is None:
        lpPathName = GetTempPathA()
    lpTempFileName = ctypes.create_string_buffer("", MAX_PATH)
    uUnique = _GetTempFileNameA(lpPathName, lpPrefixString, uUnique, lpTempFileName)
    if uUnique == 0:
        raise ctypes.WinError()
    return lpTempFileName.value, uUnique


def GetTempFileNameW(lpPathName=None, lpPrefixString="TMP", uUnique=0):
    _GetTempFileNameW = windll.kernel32.GetTempFileNameW
    _GetTempFileNameW.argtypes = [LPWSTR, LPWSTR, UINT, LPWSTR]
    _GetTempFileNameW.restype = UINT

    if lpPathName is None:
        lpPathName = GetTempPathW()
    lpTempFileName = ctypes.create_unicode_buffer("", MAX_PATH)
    uUnique = _GetTempFileNameW(lpPathName, lpPrefixString, uUnique, lpTempFileName)
    if uUnique == 0:
        raise ctypes.WinError()
    return lpTempFileName.value, uUnique


GetTempFileName = GuessStringType(GetTempFileNameA, GetTempFileNameW)


# DWORD WINAPI GetCurrentDirectory(
#   __in   DWORD nBufferLength,
#   __out  LPTSTR lpBuffer
# );
def GetCurrentDirectoryA():
    _GetCurrentDirectoryA = windll.kernel32.GetCurrentDirectoryA
    _GetCurrentDirectoryA.argtypes = [DWORD, LPSTR]
    _GetCurrentDirectoryA.restype = DWORD

    nBufferLength = _GetCurrentDirectoryA(0, None)
    if nBufferLength <= 0:
        raise ctypes.WinError()
    lpBuffer = ctypes.create_string_buffer("", nBufferLength)
    nCopied = _GetCurrentDirectoryA(nBufferLength, lpBuffer)
    if nCopied > nBufferLength or nCopied == 0:
        raise ctypes.WinError()
    return lpBuffer.value


def GetCurrentDirectoryW():
    _GetCurrentDirectoryW = windll.kernel32.GetCurrentDirectoryW
    _GetCurrentDirectoryW.argtypes = [DWORD, LPWSTR]
    _GetCurrentDirectoryW.restype = DWORD

    nBufferLength = _GetCurrentDirectoryW(0, None)
    if nBufferLength <= 0:
        raise ctypes.WinError()
    lpBuffer = ctypes.create_unicode_buffer("", nBufferLength)
    nCopied = _GetCurrentDirectoryW(nBufferLength, lpBuffer)
    if nCopied > nBufferLength or nCopied == 0:
        raise ctypes.WinError()
    return lpBuffer.value


GetCurrentDirectory = GuessStringType(GetCurrentDirectoryA, GetCurrentDirectoryW)

# ------------------------------------------------------------------------------
# Contrl-C handler

# BOOL WINAPI HandlerRoutine(
#   __in  DWORD dwCtrlType
# );
PHANDLER_ROUTINE = ctypes.WINFUNCTYPE(BOOL, DWORD)


# BOOL WINAPI SetConsoleCtrlHandler(
#   __in_opt  PHANDLER_ROUTINE HandlerRoutine,
#   __in      BOOL Add
# );
def SetConsoleCtrlHandler(HandlerRoutine=None, Add=True):
    _SetConsoleCtrlHandler = windll.kernel32.SetConsoleCtrlHandler
    _SetConsoleCtrlHandler.argtypes = [PHANDLER_ROUTINE, BOOL]
    _SetConsoleCtrlHandler.restype = bool
    _SetConsoleCtrlHandler.errcheck = RaiseIfZero
    _SetConsoleCtrlHandler(HandlerRoutine, bool(Add))
    # we can't automagically transform Python functions to PHANDLER_ROUTINE
    # because a) the actual pointer value is meaningful to the API
    # and b) if it gets garbage collected bad things would happen


# BOOL WINAPI GenerateConsoleCtrlEvent(
#   __in  DWORD dwCtrlEvent,
#   __in  DWORD dwProcessGroupId
# );
def GenerateConsoleCtrlEvent(dwCtrlEvent, dwProcessGroupId):
    _GenerateConsoleCtrlEvent = windll.kernel32.GenerateConsoleCtrlEvent
    _GenerateConsoleCtrlEvent.argtypes = [DWORD, DWORD]
    _GenerateConsoleCtrlEvent.restype = bool
    _GenerateConsoleCtrlEvent.errcheck = RaiseIfZero
    _GenerateConsoleCtrlEvent(dwCtrlEvent, dwProcessGroupId)


# ------------------------------------------------------------------------------
# Synchronization API

# XXX NOTE
#
# Instead of waiting forever, we wait for a small period of time and loop.
# This is a workaround for an unwanted behavior of psyco-accelerated code:
# you can't interrupt a blocking call using Ctrl+C, because signal processing
# is only done between C calls.
#
# Also see: bug #2793618 in Psyco project
# http://sourceforge.net/tracker/?func=detail&aid=2793618&group_id=41036&atid=429622


# DWORD WINAPI WaitForSingleObject(
#   HANDLE hHandle,
#   DWORD dwMilliseconds
# );
def WaitForSingleObject(hHandle, dwMilliseconds=INFINITE):
    _WaitForSingleObject = windll.kernel32.WaitForSingleObject
    _WaitForSingleObject.argtypes = [HANDLE, DWORD]
    _WaitForSingleObject.restype = DWORD

    if not dwMilliseconds and dwMilliseconds != 0:
        dwMilliseconds = INFINITE
    if dwMilliseconds != INFINITE:
        r = _WaitForSingleObject(hHandle, dwMilliseconds)
        if r == WAIT_FAILED:
            raise ctypes.WinError()
    else:
        while 1:
            r = _WaitForSingleObject(hHandle, 100)
            if r == WAIT_FAILED:
                raise ctypes.WinError()
            if r != WAIT_TIMEOUT:
                break
    return r


# DWORD WINAPI WaitForSingleObjectEx(
#   HANDLE hHandle,
#   DWORD dwMilliseconds,
#   BOOL bAlertable
# );
def WaitForSingleObjectEx(hHandle, dwMilliseconds=INFINITE, bAlertable=True):
    _WaitForSingleObjectEx = windll.kernel32.WaitForSingleObjectEx
    _WaitForSingleObjectEx.argtypes = [HANDLE, DWORD, BOOL]
    _WaitForSingleObjectEx.restype = DWORD

    if not dwMilliseconds and dwMilliseconds != 0:
        dwMilliseconds = INFINITE
    if dwMilliseconds != INFINITE:
        r = _WaitForSingleObjectEx(hHandle, dwMilliseconds, bool(bAlertable))
        if r == WAIT_FAILED:
            raise ctypes.WinError()
    else:
        while 1:
            r = _WaitForSingleObjectEx(hHandle, 100, bool(bAlertable))
            if r == WAIT_FAILED:
                raise ctypes.WinError()
            if r != WAIT_TIMEOUT:
                break
    return r


# DWORD WINAPI WaitForMultipleObjects(
#   DWORD nCount,
#   const HANDLE *lpHandles,
#   BOOL bWaitAll,
#   DWORD dwMilliseconds
# );
def WaitForMultipleObjects(handles, bWaitAll=False, dwMilliseconds=INFINITE):
    _WaitForMultipleObjects = windll.kernel32.WaitForMultipleObjects
    _WaitForMultipleObjects.argtypes = [DWORD, POINTER(HANDLE), BOOL, DWORD]
    _WaitForMultipleObjects.restype = DWORD

    if not dwMilliseconds and dwMilliseconds != 0:
        dwMilliseconds = INFINITE
    nCount = len(handles)
    lpHandlesType = HANDLE * nCount
    lpHandles = lpHandlesType(*handles)
    if dwMilliseconds != INFINITE:
        r = _WaitForMultipleObjects(byref(lpHandles), bool(bWaitAll), dwMilliseconds)
        if r == WAIT_FAILED:
            raise ctypes.WinError()
    else:
        while 1:
            r = _WaitForMultipleObjects(byref(lpHandles), bool(bWaitAll), 100)
            if r == WAIT_FAILED:
                raise ctypes.WinError()
            if r != WAIT_TIMEOUT:
                break
    return r


# DWORD WINAPI WaitForMultipleObjectsEx(
#   DWORD nCount,
#   const HANDLE *lpHandles,
#   BOOL bWaitAll,
#   DWORD dwMilliseconds,
#   BOOL bAlertable
# );
def WaitForMultipleObjectsEx(handles, bWaitAll=False, dwMilliseconds=INFINITE, bAlertable=True):
    _WaitForMultipleObjectsEx = windll.kernel32.WaitForMultipleObjectsEx
    _WaitForMultipleObjectsEx.argtypes = [DWORD, POINTER(HANDLE), BOOL, DWORD]
    _WaitForMultipleObjectsEx.restype = DWORD

    if not dwMilliseconds and dwMilliseconds != 0:
        dwMilliseconds = INFINITE
    nCount = len(handles)
    lpHandlesType = HANDLE * nCount
    lpHandles = lpHandlesType(*handles)
    if dwMilliseconds != INFINITE:
        r = _WaitForMultipleObjectsEx(byref(lpHandles), bool(bWaitAll), dwMilliseconds, bool(bAlertable))
        if r == WAIT_FAILED:
            raise ctypes.WinError()
    else:
        while 1:
            r = _WaitForMultipleObjectsEx(byref(lpHandles), bool(bWaitAll), 100, bool(bAlertable))
            if r == WAIT_FAILED:
                raise ctypes.WinError()
            if r != WAIT_TIMEOUT:
                break
    return r


# HANDLE WINAPI CreateMutex(
#   _In_opt_  LPSECURITY_ATTRIBUTES lpMutexAttributes,
#   _In_      BOOL bInitialOwner,
#   _In_opt_  LPCTSTR lpName
# );
def CreateMutexA(lpMutexAttributes=None, bInitialOwner=True, lpName=None):
    _CreateMutexA = windll.kernel32.CreateMutexA
    _CreateMutexA.argtypes = [LPVOID, BOOL, LPSTR]
    _CreateMutexA.restype = HANDLE
    _CreateMutexA.errcheck = RaiseIfZero
    return Handle(_CreateMutexA(lpMutexAttributes, bInitialOwner, lpName))


def CreateMutexW(lpMutexAttributes=None, bInitialOwner=True, lpName=None):
    _CreateMutexW = windll.kernel32.CreateMutexW
    _CreateMutexW.argtypes = [LPVOID, BOOL, LPWSTR]
    _CreateMutexW.restype = HANDLE
    _CreateMutexW.errcheck = RaiseIfZero
    return Handle(_CreateMutexW(lpMutexAttributes, bInitialOwner, lpName))


CreateMutex = GuessStringType(CreateMutexA, CreateMutexW)


# HANDLE WINAPI OpenMutex(
#   _In_  DWORD dwDesiredAccess,
#   _In_  BOOL bInheritHandle,
#   _In_  LPCTSTR lpName
# );
def OpenMutexA(dwDesiredAccess=MUTEX_ALL_ACCESS, bInitialOwner=True, lpName=None):
    _OpenMutexA = windll.kernel32.OpenMutexA
    _OpenMutexA.argtypes = [DWORD, BOOL, LPSTR]
    _OpenMutexA.restype = HANDLE
    _OpenMutexA.errcheck = RaiseIfZero
    return Handle(_OpenMutexA(lpMutexAttributes, bInitialOwner, lpName))


def OpenMutexW(dwDesiredAccess=MUTEX_ALL_ACCESS, bInitialOwner=True, lpName=None):
    _OpenMutexW = windll.kernel32.OpenMutexW
    _OpenMutexW.argtypes = [DWORD, BOOL, LPWSTR]
    _OpenMutexW.restype = HANDLE
    _OpenMutexW.errcheck = RaiseIfZero
    return Handle(_OpenMutexW(lpMutexAttributes, bInitialOwner, lpName))


OpenMutex = GuessStringType(OpenMutexA, OpenMutexW)


# HANDLE WINAPI CreateEvent(
#   _In_opt_  LPSECURITY_ATTRIBUTES lpEventAttributes,
#   _In_      BOOL bManualReset,
#   _In_      BOOL bInitialState,
#   _In_opt_  LPCTSTR lpName
# );
def CreateEventA(lpMutexAttributes=None, bManualReset=False, bInitialState=False, lpName=None):
    _CreateEventA = windll.kernel32.CreateEventA
    _CreateEventA.argtypes = [LPVOID, BOOL, BOOL, LPSTR]
    _CreateEventA.restype = HANDLE
    _CreateEventA.errcheck = RaiseIfZero
    return Handle(_CreateEventA(lpMutexAttributes, bManualReset, bInitialState, lpName))


def CreateEventW(lpMutexAttributes=None, bManualReset=False, bInitialState=False, lpName=None):
    _CreateEventW = windll.kernel32.CreateEventW
    _CreateEventW.argtypes = [LPVOID, BOOL, BOOL, LPWSTR]
    _CreateEventW.restype = HANDLE
    _CreateEventW.errcheck = RaiseIfZero
    return Handle(_CreateEventW(lpMutexAttributes, bManualReset, bInitialState, lpName))


CreateEvent = GuessStringType(CreateEventA, CreateEventW)


# HANDLE WINAPI OpenEvent(
#   _In_  DWORD dwDesiredAccess,
#   _In_  BOOL bInheritHandle,
#   _In_  LPCTSTR lpName
# );
def OpenEventA(dwDesiredAccess=EVENT_ALL_ACCESS, bInheritHandle=False, lpName=None):
    _OpenEventA = windll.kernel32.OpenEventA
    _OpenEventA.argtypes = [DWORD, BOOL, LPSTR]
    _OpenEventA.restype = HANDLE
    _OpenEventA.errcheck = RaiseIfZero
    return Handle(_OpenEventA(dwDesiredAccess, bInheritHandle, lpName))


def OpenEventW(dwDesiredAccess=EVENT_ALL_ACCESS, bInheritHandle=False, lpName=None):
    _OpenEventW = windll.kernel32.OpenEventW
    _OpenEventW.argtypes = [DWORD, BOOL, LPWSTR]
    _OpenEventW.restype = HANDLE
    _OpenEventW.errcheck = RaiseIfZero
    return Handle(_OpenEventW(dwDesiredAccess, bInheritHandle, lpName))


OpenEvent = GuessStringType(OpenEventA, OpenEventW)

# HANDLE WINAPI CreateSemaphore(
#   _In_opt_  LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
#   _In_      LONG lInitialCount,
#   _In_      LONG lMaximumCount,
#   _In_opt_  LPCTSTR lpName
# );

# TODO

# HANDLE WINAPI OpenSemaphore(
#   _In_  DWORD dwDesiredAccess,
#   _In_  BOOL bInheritHandle,
#   _In_  LPCTSTR lpName
# );

# TODO


# BOOL WINAPI ReleaseMutex(
#   _In_  HANDLE hMutex
# );
def ReleaseMutex(hMutex):
    _ReleaseMutex = windll.kernel32.ReleaseMutex
    _ReleaseMutex.argtypes = [HANDLE]
    _ReleaseMutex.restype = bool
    _ReleaseMutex.errcheck = RaiseIfZero
    _ReleaseMutex(hMutex)


# BOOL WINAPI SetEvent(
#   _In_  HANDLE hEvent
# );
def SetEvent(hEvent):
    _SetEvent = windll.kernel32.SetEvent
    _SetEvent.argtypes = [HANDLE]
    _SetEvent.restype = bool
    _SetEvent.errcheck = RaiseIfZero
    _SetEvent(hEvent)


# BOOL WINAPI ResetEvent(
#   _In_  HANDLE hEvent
# );
def ResetEvent(hEvent):
    _ResetEvent = windll.kernel32.ResetEvent
    _ResetEvent.argtypes = [HANDLE]
    _ResetEvent.restype = bool
    _ResetEvent.errcheck = RaiseIfZero
    _ResetEvent(hEvent)


# BOOL WINAPI PulseEvent(
#   _In_  HANDLE hEvent
# );
def PulseEvent(hEvent):
    _PulseEvent = windll.kernel32.PulseEvent
    _PulseEvent.argtypes = [HANDLE]
    _PulseEvent.restype = bool
    _PulseEvent.errcheck = RaiseIfZero
    _PulseEvent(hEvent)


# BOOL WINAPI ReleaseSemaphore(
#   _In_       HANDLE hSemaphore,
#   _In_       LONG lReleaseCount,
#   _Out_opt_  LPLONG lpPreviousCount
# );

# TODO

# ------------------------------------------------------------------------------
# Debug API


# BOOL WaitForDebugEvent(
#   LPDEBUG_EVENT lpDebugEvent,
#   DWORD dwMilliseconds
# );
def WaitForDebugEvent(dwMilliseconds=INFINITE):
    _WaitForDebugEvent = windll.kernel32.WaitForDebugEvent
    _WaitForDebugEvent.argtypes = [LPDEBUG_EVENT, DWORD]
    _WaitForDebugEvent.restype = DWORD

    if not dwMilliseconds and dwMilliseconds != 0:
        dwMilliseconds = INFINITE
    lpDebugEvent = DEBUG_EVENT()
    lpDebugEvent.dwDebugEventCode = 0
    lpDebugEvent.dwProcessId = 0
    lpDebugEvent.dwThreadId = 0
    if dwMilliseconds != INFINITE:
        success = _WaitForDebugEvent(byref(lpDebugEvent), dwMilliseconds)
        if success == 0:
            raise ctypes.WinError()
    else:
        # this avoids locking the Python GIL for too long
        while 1:
            success = _WaitForDebugEvent(byref(lpDebugEvent), 100)
            if success != 0:
                break
            code = GetLastError()
            if code not in (ERROR_SEM_TIMEOUT, WAIT_TIMEOUT):
                raise ctypes.WinError(code)
    return lpDebugEvent


# BOOL ContinueDebugEvent(
#   DWORD dwProcessId,
#   DWORD dwThreadId,
#   DWORD dwContinueStatus
# );
def ContinueDebugEvent(dwProcessId, dwThreadId, dwContinueStatus=DBG_EXCEPTION_NOT_HANDLED):
    _ContinueDebugEvent = windll.kernel32.ContinueDebugEvent
    _ContinueDebugEvent.argtypes = [DWORD, DWORD, DWORD]
    _ContinueDebugEvent.restype = bool
    _ContinueDebugEvent.errcheck = RaiseIfZero
    _ContinueDebugEvent(dwProcessId, dwThreadId, dwContinueStatus)


# BOOL WINAPI FlushInstructionCache(
#   __in  HANDLE hProcess,
#   __in  LPCVOID lpBaseAddress,
#   __in  SIZE_T dwSize
# );
def FlushInstructionCache(hProcess, lpBaseAddress=None, dwSize=0):
    # http://blogs.msdn.com/oldnewthing/archive/2003/12/08/55954.aspx#55958
    _FlushInstructionCache = windll.kernel32.FlushInstructionCache
    _FlushInstructionCache.argtypes = [HANDLE, LPVOID, SIZE_T]
    _FlushInstructionCache.restype = bool
    _FlushInstructionCache.errcheck = RaiseIfZero
    _FlushInstructionCache(hProcess, lpBaseAddress, dwSize)


# BOOL DebugActiveProcess(
#   DWORD dwProcessId
# );
def DebugActiveProcess(dwProcessId):
    _DebugActiveProcess = windll.kernel32.DebugActiveProcess
    _DebugActiveProcess.argtypes = [DWORD]
    _DebugActiveProcess.restype = bool
    _DebugActiveProcess.errcheck = RaiseIfZero
    _DebugActiveProcess(dwProcessId)


# BOOL DebugActiveProcessStop(
#   DWORD dwProcessId
# );
def DebugActiveProcessStop(dwProcessId):
    _DebugActiveProcessStop = windll.kernel32.DebugActiveProcessStop
    _DebugActiveProcessStop.argtypes = [DWORD]
    _DebugActiveProcessStop.restype = bool
    _DebugActiveProcessStop.errcheck = RaiseIfZero
    _DebugActiveProcessStop(dwProcessId)


# BOOL CheckRemoteDebuggerPresent(
#   HANDLE hProcess,
#   PBOOL pbDebuggerPresent
# );
def CheckRemoteDebuggerPresent(hProcess):
    _CheckRemoteDebuggerPresent = windll.kernel32.CheckRemoteDebuggerPresent
    _CheckRemoteDebuggerPresent.argtypes = [HANDLE, PBOOL]
    _CheckRemoteDebuggerPresent.restype = bool
    _CheckRemoteDebuggerPresent.errcheck = RaiseIfZero

    pbDebuggerPresent = BOOL(0)
    _CheckRemoteDebuggerPresent(hProcess, byref(pbDebuggerPresent))
    return bool(pbDebuggerPresent.value)


# BOOL DebugSetProcessKillOnExit(
#   BOOL KillOnExit
# );
def DebugSetProcessKillOnExit(KillOnExit):
    _DebugSetProcessKillOnExit = windll.kernel32.DebugSetProcessKillOnExit
    _DebugSetProcessKillOnExit.argtypes = [BOOL]
    _DebugSetProcessKillOnExit.restype = bool
    _DebugSetProcessKillOnExit.errcheck = RaiseIfZero
    _DebugSetProcessKillOnExit(bool(KillOnExit))


# BOOL DebugBreakProcess(
#   HANDLE Process
# );
def DebugBreakProcess(hProcess):
    _DebugBreakProcess = windll.kernel32.DebugBreakProcess
    _DebugBreakProcess.argtypes = [HANDLE]
    _DebugBreakProcess.restype = bool
    _DebugBreakProcess.errcheck = RaiseIfZero
    _DebugBreakProcess(hProcess)


# void WINAPI OutputDebugString(
#   __in_opt  LPCTSTR lpOutputString
# );
def OutputDebugStringA(lpOutputString):
    _OutputDebugStringA = windll.kernel32.OutputDebugStringA
    _OutputDebugStringA.argtypes = [LPSTR]
    _OutputDebugStringA.restype = None
    _OutputDebugStringA(lpOutputString)


def OutputDebugStringW(lpOutputString):
    _OutputDebugStringW = windll.kernel32.OutputDebugStringW
    _OutputDebugStringW.argtypes = [LPWSTR]
    _OutputDebugStringW.restype = None
    _OutputDebugStringW(lpOutputString)


OutputDebugString = GuessStringType(OutputDebugStringA, OutputDebugStringW)


# BOOL WINAPI ReadProcessMemory(
#   __in   HANDLE hProcess,
#   __in   LPCVOID lpBaseAddress,
#   __out  LPVOID lpBuffer,
#   __in   SIZE_T nSize,
#   __out  SIZE_T* lpNumberOfBytesRead
# );
def ReadProcessMemory(hProcess, lpBaseAddress, nSize):
    _ReadProcessMemory = windll.kernel32.ReadProcessMemory
    _ReadProcessMemory.argtypes = [HANDLE, LPVOID, LPVOID, SIZE_T, POINTER(SIZE_T)]
    _ReadProcessMemory.restype = bool

    lpBuffer = ctypes.create_string_buffer(compat.b(""), nSize)
    lpNumberOfBytesRead = SIZE_T(0)
    success = _ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, byref(lpNumberOfBytesRead))
    if not success and GetLastError() != ERROR_PARTIAL_COPY:
        raise ctypes.WinError()
    return compat.b(lpBuffer.raw)[: lpNumberOfBytesRead.value]


# BOOL WINAPI WriteProcessMemory(
#   __in   HANDLE hProcess,
#   __in   LPCVOID lpBaseAddress,
#   __in   LPVOID lpBuffer,
#   __in   SIZE_T nSize,
#   __out  SIZE_T* lpNumberOfBytesWritten
# );
def WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer):
    _WriteProcessMemory = windll.kernel32.WriteProcessMemory
    _WriteProcessMemory.argtypes = [HANDLE, LPVOID, LPVOID, SIZE_T, POINTER(SIZE_T)]
    _WriteProcessMemory.restype = bool

    nSize = len(lpBuffer)
    lpBuffer = ctypes.create_string_buffer(lpBuffer)
    lpNumberOfBytesWritten = SIZE_T(0)
    success = _WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, byref(lpNumberOfBytesWritten))
    if not success and GetLastError() != ERROR_PARTIAL_COPY:
        raise ctypes.WinError()
    return lpNumberOfBytesWritten.value


# LPVOID WINAPI VirtualAllocEx(
#   __in      HANDLE hProcess,
#   __in_opt  LPVOID lpAddress,
#   __in      SIZE_T dwSize,
#   __in      DWORD flAllocationType,
#   __in      DWORD flProtect
# );
def VirtualAllocEx(hProcess, lpAddress=0, dwSize=0x1000, flAllocationType=MEM_COMMIT | MEM_RESERVE, flProtect=PAGE_EXECUTE_READWRITE):
    _VirtualAllocEx = windll.kernel32.VirtualAllocEx
    _VirtualAllocEx.argtypes = [HANDLE, LPVOID, SIZE_T, DWORD, DWORD]
    _VirtualAllocEx.restype = LPVOID

    lpAddress = _VirtualAllocEx(hProcess, lpAddress, dwSize, flAllocationType, flProtect)
    if lpAddress == NULL:
        raise ctypes.WinError()
    return lpAddress


# SIZE_T WINAPI VirtualQueryEx(
#   __in      HANDLE hProcess,
#   __in_opt  LPCVOID lpAddress,
#   __out     PMEMORY_BASIC_INFORMATION lpBuffer,
#   __in      SIZE_T dwLength
# );
def VirtualQueryEx(hProcess, lpAddress):
    _VirtualQueryEx = windll.kernel32.VirtualQueryEx
    _VirtualQueryEx.argtypes = [HANDLE, LPVOID, PMEMORY_BASIC_INFORMATION, SIZE_T]
    _VirtualQueryEx.restype = SIZE_T

    lpBuffer = MEMORY_BASIC_INFORMATION()
    dwLength = sizeof(MEMORY_BASIC_INFORMATION)
    success = _VirtualQueryEx(hProcess, lpAddress, byref(lpBuffer), dwLength)
    if success == 0:
        raise ctypes.WinError()
    return MemoryBasicInformation(lpBuffer)


# BOOL WINAPI VirtualProtectEx(
#   __in   HANDLE hProcess,
#   __in   LPVOID lpAddress,
#   __in   SIZE_T dwSize,
#   __in   DWORD flNewProtect,
#   __out  PDWORD lpflOldProtect
# );
def VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect=PAGE_EXECUTE_READWRITE):
    _VirtualProtectEx = windll.kernel32.VirtualProtectEx
    _VirtualProtectEx.argtypes = [HANDLE, LPVOID, SIZE_T, DWORD, PDWORD]
    _VirtualProtectEx.restype = bool
    _VirtualProtectEx.errcheck = RaiseIfZero

    flOldProtect = DWORD(0)
    _VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, byref(flOldProtect))
    return flOldProtect.value


# BOOL WINAPI VirtualFreeEx(
#   __in  HANDLE hProcess,
#   __in  LPVOID lpAddress,
#   __in  SIZE_T dwSize,
#   __in  DWORD dwFreeType
# );
def VirtualFreeEx(hProcess, lpAddress, dwSize=0, dwFreeType=MEM_RELEASE):
    _VirtualFreeEx = windll.kernel32.VirtualFreeEx
    _VirtualFreeEx.argtypes = [HANDLE, LPVOID, SIZE_T, DWORD]
    _VirtualFreeEx.restype = bool
    _VirtualFreeEx.errcheck = RaiseIfZero
    _VirtualFreeEx(hProcess, lpAddress, dwSize, dwFreeType)


# HANDLE WINAPI CreateRemoteThread(
#   __in   HANDLE hProcess,
#   __in   LPSECURITY_ATTRIBUTES lpThreadAttributes,
#   __in   SIZE_T dwStackSize,
#   __in   LPTHREAD_START_ROUTINE lpStartAddress,
#   __in   LPVOID lpParameter,
#   __in   DWORD dwCreationFlags,
#   __out  LPDWORD lpThreadId
# );
def CreateRemoteThread(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags):
    _CreateRemoteThread = windll.kernel32.CreateRemoteThread
    _CreateRemoteThread.argtypes = [HANDLE, LPSECURITY_ATTRIBUTES, SIZE_T, LPVOID, LPVOID, DWORD, LPDWORD]
    _CreateRemoteThread.restype = HANDLE

    if not lpThreadAttributes:
        lpThreadAttributes = None
    else:
        lpThreadAttributes = byref(lpThreadAttributes)
    dwThreadId = DWORD(0)
    hThread = _CreateRemoteThread(
        hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, byref(dwThreadId)
    )
    if not hThread:
        raise ctypes.WinError()
    return ThreadHandle(hThread), dwThreadId.value


# ------------------------------------------------------------------------------
# Process API


# BOOL WINAPI CreateProcess(
#   __in_opt     LPCTSTR lpApplicationName,
#   __inout_opt  LPTSTR lpCommandLine,
#   __in_opt     LPSECURITY_ATTRIBUTES lpProcessAttributes,
#   __in_opt     LPSECURITY_ATTRIBUTES lpThreadAttributes,
#   __in         BOOL bInheritHandles,
#   __in         DWORD dwCreationFlags,
#   __in_opt     LPVOID lpEnvironment,
#   __in_opt     LPCTSTR lpCurrentDirectory,
#   __in         LPSTARTUPINFO lpStartupInfo,
#   __out        LPPROCESS_INFORMATION lpProcessInformation
# );
def CreateProcessA(
    lpApplicationName,
    lpCommandLine=None,
    lpProcessAttributes=None,
    lpThreadAttributes=None,
    bInheritHandles=False,
    dwCreationFlags=0,
    lpEnvironment=None,
    lpCurrentDirectory=None,
    lpStartupInfo=None,
):
    _CreateProcessA = windll.kernel32.CreateProcessA
    _CreateProcessA.argtypes = [
        LPSTR,
        LPSTR,
        LPSECURITY_ATTRIBUTES,
        LPSECURITY_ATTRIBUTES,
        BOOL,
        DWORD,
        LPVOID,
        LPSTR,
        LPVOID,
        LPPROCESS_INFORMATION,
    ]
    _CreateProcessA.restype = bool
    _CreateProcessA.errcheck = RaiseIfZero

    if not lpApplicationName:
        lpApplicationName = None
    if not lpCommandLine:
        lpCommandLine = None
    else:
        lpCommandLine = ctypes.create_string_buffer(lpCommandLine, max(MAX_PATH, len(lpCommandLine)))
    if not lpEnvironment:
        lpEnvironment = None
    else:
        lpEnvironment = ctypes.create_string_buffer(lpEnvironment)
    if not lpCurrentDirectory:
        lpCurrentDirectory = None
    if not lpProcessAttributes:
        lpProcessAttributes = None
    else:
        lpProcessAttributes = byref(lpProcessAttributes)
    if not lpThreadAttributes:
        lpThreadAttributes = None
    else:
        lpThreadAttributes = byref(lpThreadAttributes)
    if not lpStartupInfo:
        lpStartupInfo = STARTUPINFO()
        lpStartupInfo.cb = sizeof(STARTUPINFO)
        lpStartupInfo.lpReserved = 0
        lpStartupInfo.lpDesktop = 0
        lpStartupInfo.lpTitle = 0
        lpStartupInfo.dwFlags = 0
        lpStartupInfo.cbReserved2 = 0
        lpStartupInfo.lpReserved2 = 0
    lpProcessInformation = PROCESS_INFORMATION()
    lpProcessInformation.hProcess = INVALID_HANDLE_VALUE
    lpProcessInformation.hThread = INVALID_HANDLE_VALUE
    lpProcessInformation.dwProcessId = 0
    lpProcessInformation.dwThreadId = 0
    _CreateProcessA(
        lpApplicationName,
        lpCommandLine,
        lpProcessAttributes,
        lpThreadAttributes,
        bool(bInheritHandles),
        dwCreationFlags,
        lpEnvironment,
        lpCurrentDirectory,
        byref(lpStartupInfo),
        byref(lpProcessInformation),
    )
    return ProcessInformation(lpProcessInformation)


def CreateProcessW(
    lpApplicationName,
    lpCommandLine=None,
    lpProcessAttributes=None,
    lpThreadAttributes=None,
    bInheritHandles=False,
    dwCreationFlags=0,
    lpEnvironment=None,
    lpCurrentDirectory=None,
    lpStartupInfo=None,
):
    _CreateProcessW = windll.kernel32.CreateProcessW
    _CreateProcessW.argtypes = [
        LPWSTR,
        LPWSTR,
        LPSECURITY_ATTRIBUTES,
        LPSECURITY_ATTRIBUTES,
        BOOL,
        DWORD,
        LPVOID,
        LPWSTR,
        LPVOID,
        LPPROCESS_INFORMATION,
    ]
    _CreateProcessW.restype = bool
    _CreateProcessW.errcheck = RaiseIfZero

    if not lpApplicationName:
        lpApplicationName = None
    if not lpCommandLine:
        lpCommandLine = None
    else:
        lpCommandLine = ctypes.create_unicode_buffer(lpCommandLine, max(MAX_PATH, len(lpCommandLine)))
    if not lpEnvironment:
        lpEnvironment = None
    else:
        lpEnvironment = ctypes.create_unicode_buffer(lpEnvironment)
    if not lpCurrentDirectory:
        lpCurrentDirectory = None
    if not lpProcessAttributes:
        lpProcessAttributes = None
    else:
        lpProcessAttributes = byref(lpProcessAttributes)
    if not lpThreadAttributes:
        lpThreadAttributes = None
    else:
        lpThreadAttributes = byref(lpThreadAttributes)
    if not lpStartupInfo:
        lpStartupInfo = STARTUPINFO()
        lpStartupInfo.cb = sizeof(STARTUPINFO)
        lpStartupInfo.lpReserved = 0
        lpStartupInfo.lpDesktop = 0
        lpStartupInfo.lpTitle = 0
        lpStartupInfo.dwFlags = 0
        lpStartupInfo.cbReserved2 = 0
        lpStartupInfo.lpReserved2 = 0
    lpProcessInformation = PROCESS_INFORMATION()
    lpProcessInformation.hProcess = INVALID_HANDLE_VALUE
    lpProcessInformation.hThread = INVALID_HANDLE_VALUE
    lpProcessInformation.dwProcessId = 0
    lpProcessInformation.dwThreadId = 0
    _CreateProcessW(
        lpApplicationName,
        lpCommandLine,
        lpProcessAttributes,
        lpThreadAttributes,
        bool(bInheritHandles),
        dwCreationFlags,
        lpEnvironment,
        lpCurrentDirectory,
        byref(lpStartupInfo),
        byref(lpProcessInformation),
    )
    return ProcessInformation(lpProcessInformation)


CreateProcess = GuessStringType(CreateProcessA, CreateProcessW)


# BOOL WINAPI InitializeProcThreadAttributeList(
#   __out_opt   LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
#   __in        DWORD dwAttributeCount,
#   __reserved  DWORD dwFlags,
#   __inout     PSIZE_T lpSize
# );
def InitializeProcThreadAttributeList(dwAttributeCount):
    _InitializeProcThreadAttributeList = windll.kernel32.InitializeProcThreadAttributeList
    _InitializeProcThreadAttributeList.argtypes = [LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD, PSIZE_T]
    _InitializeProcThreadAttributeList.restype = bool

    Size = SIZE_T(0)
    _InitializeProcThreadAttributeList(None, dwAttributeCount, 0, byref(Size))
    RaiseIfZero(Size.value)
    AttributeList = (BYTE * Size.value)()
    success = _InitializeProcThreadAttributeList(byref(AttributeList), dwAttributeCount, 0, byref(Size))
    RaiseIfZero(success)
    return AttributeList


# BOOL WINAPI UpdateProcThreadAttribute(
#   __inout    LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList,
#   __in       DWORD dwFlags,
#   __in       DWORD_PTR Attribute,
#   __in       PVOID lpValue,
#   __in       SIZE_T cbSize,
#   __out_opt  PVOID lpPreviousValue,
#   __in_opt   PSIZE_T lpReturnSize
# );
def UpdateProcThreadAttribute(lpAttributeList, Attribute, Value, cbSize=None):
    _UpdateProcThreadAttribute = windll.kernel32.UpdateProcThreadAttribute
    _UpdateProcThreadAttribute.argtypes = [LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD_PTR, PVOID, SIZE_T, PVOID, PSIZE_T]
    _UpdateProcThreadAttribute.restype = bool
    _UpdateProcThreadAttribute.errcheck = RaiseIfZero

    if cbSize is None:
        cbSize = sizeof(Value)
    _UpdateProcThreadAttribute(byref(lpAttributeList), 0, Attribute, byref(Value), cbSize, None, None)


# VOID WINAPI DeleteProcThreadAttributeList(
#   __inout  LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList
# );
def DeleteProcThreadAttributeList(lpAttributeList):
    _DeleteProcThreadAttributeList = windll.kernel32.DeleteProcThreadAttributeList
    _DeleteProcThreadAttributeList.restype = None
    _DeleteProcThreadAttributeList(byref(lpAttributeList))


# HANDLE WINAPI OpenProcess(
#   __in  DWORD dwDesiredAccess,
#   __in  BOOL bInheritHandle,
#   __in  DWORD dwProcessId
# );
def OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId):
    _OpenProcess = windll.kernel32.OpenProcess
    _OpenProcess.argtypes = [DWORD, BOOL, DWORD]
    _OpenProcess.restype = HANDLE

    hProcess = _OpenProcess(dwDesiredAccess, bool(bInheritHandle), dwProcessId)
    if hProcess == NULL:
        raise ctypes.WinError()
    return ProcessHandle(hProcess, dwAccess=dwDesiredAccess)


# HANDLE WINAPI OpenThread(
#   __in  DWORD dwDesiredAccess,
#   __in  BOOL bInheritHandle,
#   __in  DWORD dwThreadId
# );
def OpenThread(dwDesiredAccess, bInheritHandle, dwThreadId):
    _OpenThread = windll.kernel32.OpenThread
    _OpenThread.argtypes = [DWORD, BOOL, DWORD]
    _OpenThread.restype = HANDLE

    hThread = _OpenThread(dwDesiredAccess, bool(bInheritHandle), dwThreadId)
    if hThread == NULL:
        raise ctypes.WinError()
    return ThreadHandle(hThread, dwAccess=dwDesiredAccess)


# DWORD WINAPI SuspendThread(
#   __in  HANDLE hThread
# );
def SuspendThread(hThread):
    _SuspendThread = windll.kernel32.SuspendThread
    _SuspendThread.argtypes = [HANDLE]
    _SuspendThread.restype = DWORD

    previousCount = _SuspendThread(hThread)
    if previousCount == DWORD(-1).value:
        raise ctypes.WinError()
    return previousCount


# DWORD WINAPI ResumeThread(
#   __in  HANDLE hThread
# );
def ResumeThread(hThread):
    _ResumeThread = windll.kernel32.ResumeThread
    _ResumeThread.argtypes = [HANDLE]
    _ResumeThread.restype = DWORD

    previousCount = _ResumeThread(hThread)
    if previousCount == DWORD(-1).value:
        raise ctypes.WinError()
    return previousCount


# BOOL WINAPI TerminateThread(
#   __inout  HANDLE hThread,
#   __in     DWORD dwExitCode
# );
def TerminateThread(hThread, dwExitCode=0):
    _TerminateThread = windll.kernel32.TerminateThread
    _TerminateThread.argtypes = [HANDLE, DWORD]
    _TerminateThread.restype = bool
    _TerminateThread.errcheck = RaiseIfZero
    _TerminateThread(hThread, dwExitCode)


# BOOL WINAPI TerminateProcess(
#   __inout  HANDLE hProcess,
#   __in     DWORD dwExitCode
# );
def TerminateProcess(hProcess, dwExitCode=0):
    _TerminateProcess = windll.kernel32.TerminateProcess
    _TerminateProcess.argtypes = [HANDLE, DWORD]
    _TerminateProcess.restype = bool
    _TerminateProcess.errcheck = RaiseIfZero
    _TerminateProcess(hProcess, dwExitCode)


# DWORD WINAPI GetCurrentProcessId(void);
def GetCurrentProcessId():
    _GetCurrentProcessId = windll.kernel32.GetCurrentProcessId
    _GetCurrentProcessId.argtypes = []
    _GetCurrentProcessId.restype = DWORD
    return _GetCurrentProcessId()


# DWORD WINAPI GetCurrentThreadId(void);
def GetCurrentThreadId():
    _GetCurrentThreadId = windll.kernel32.GetCurrentThreadId
    _GetCurrentThreadId.argtypes = []
    _GetCurrentThreadId.restype = DWORD
    return _GetCurrentThreadId()


# DWORD WINAPI GetProcessId(
#   __in  HANDLE hProcess
# );
def GetProcessId(hProcess):
    _GetProcessId = windll.kernel32.GetProcessId
    _GetProcessId.argtypes = [HANDLE]
    _GetProcessId.restype = DWORD
    _GetProcessId.errcheck = RaiseIfZero
    return _GetProcessId(hProcess)


# DWORD WINAPI GetThreadId(
#   __in  HANDLE hThread
# );
def GetThreadId(hThread):
    _GetThreadId = windll.kernel32._GetThreadId
    _GetThreadId.argtypes = [HANDLE]
    _GetThreadId.restype = DWORD

    dwThreadId = _GetThreadId(hThread)
    if dwThreadId == 0:
        raise ctypes.WinError()
    return dwThreadId


# DWORD WINAPI GetProcessIdOfThread(
#   __in  HANDLE hThread
# );
def GetProcessIdOfThread(hThread):
    _GetProcessIdOfThread = windll.kernel32.GetProcessIdOfThread
    _GetProcessIdOfThread.argtypes = [HANDLE]
    _GetProcessIdOfThread.restype = DWORD

    dwProcessId = _GetProcessIdOfThread(hThread)
    if dwProcessId == 0:
        raise ctypes.WinError()
    return dwProcessId


# BOOL WINAPI GetExitCodeProcess(
#   __in   HANDLE hProcess,
#   __out  LPDWORD lpExitCode
# );
def GetExitCodeProcess(hProcess):
    _GetExitCodeProcess = windll.kernel32.GetExitCodeProcess
    _GetExitCodeProcess.argtypes = [HANDLE]
    _GetExitCodeProcess.restype = bool
    _GetExitCodeProcess.errcheck = RaiseIfZero

    lpExitCode = DWORD(0)
    _GetExitCodeProcess(hProcess, byref(lpExitCode))
    return lpExitCode.value


# BOOL WINAPI GetExitCodeThread(
#   __in   HANDLE hThread,
#   __out  LPDWORD lpExitCode
# );
def GetExitCodeThread(hThread):
    _GetExitCodeThread = windll.kernel32.GetExitCodeThread
    _GetExitCodeThread.argtypes = [HANDLE]
    _GetExitCodeThread.restype = bool
    _GetExitCodeThread.errcheck = RaiseIfZero

    lpExitCode = DWORD(0)
    _GetExitCodeThread(hThread, byref(lpExitCode))
    return lpExitCode.value


# DWORD WINAPI GetProcessVersion(
#   __in  DWORD ProcessId
# );
def GetProcessVersion(ProcessId):
    _GetProcessVersion = windll.kernel32.GetProcessVersion
    _GetProcessVersion.argtypes = [DWORD]
    _GetProcessVersion.restype = DWORD

    retval = _GetProcessVersion(ProcessId)
    if retval == 0:
        raise ctypes.WinError()
    return retval


# DWORD WINAPI GetPriorityClass(
#   __in  HANDLE hProcess
# );
def GetPriorityClass(hProcess):
    _GetPriorityClass = windll.kernel32.GetPriorityClass
    _GetPriorityClass.argtypes = [HANDLE]
    _GetPriorityClass.restype = DWORD

    retval = _GetPriorityClass(hProcess)
    if retval == 0:
        raise ctypes.WinError()
    return retval


# BOOL WINAPI SetPriorityClass(
#   __in  HANDLE hProcess,
#   __in  DWORD dwPriorityClass
# );
def SetPriorityClass(hProcess, dwPriorityClass=NORMAL_PRIORITY_CLASS):
    _SetPriorityClass = windll.kernel32.SetPriorityClass
    _SetPriorityClass.argtypes = [HANDLE, DWORD]
    _SetPriorityClass.restype = bool
    _SetPriorityClass.errcheck = RaiseIfZero
    _SetPriorityClass(hProcess, dwPriorityClass)


# BOOL WINAPI GetProcessPriorityBoost(
#   __in   HANDLE hProcess,
#   __out  PBOOL pDisablePriorityBoost
# );
def GetProcessPriorityBoost(hProcess):
    _GetProcessPriorityBoost = windll.kernel32.GetProcessPriorityBoost
    _GetProcessPriorityBoost.argtypes = [HANDLE, PBOOL]
    _GetProcessPriorityBoost.restype = bool
    _GetProcessPriorityBoost.errcheck = RaiseIfZero

    pDisablePriorityBoost = BOOL(False)
    _GetProcessPriorityBoost(hProcess, byref(pDisablePriorityBoost))
    return bool(pDisablePriorityBoost.value)


# BOOL WINAPI SetProcessPriorityBoost(
#   __in  HANDLE hProcess,
#   __in  BOOL DisablePriorityBoost
# );
def SetProcessPriorityBoost(hProcess, DisablePriorityBoost):
    _SetProcessPriorityBoost = windll.kernel32.SetProcessPriorityBoost
    _SetProcessPriorityBoost.argtypes = [HANDLE, BOOL]
    _SetProcessPriorityBoost.restype = bool
    _SetProcessPriorityBoost.errcheck = RaiseIfZero
    _SetProcessPriorityBoost(hProcess, bool(DisablePriorityBoost))


# BOOL WINAPI GetProcessAffinityMask(
#   __in   HANDLE hProcess,
#   __out  PDWORD_PTR lpProcessAffinityMask,
#   __out  PDWORD_PTR lpSystemAffinityMask
# );
def GetProcessAffinityMask(hProcess):
    _GetProcessAffinityMask = windll.kernel32.GetProcessAffinityMask
    _GetProcessAffinityMask.argtypes = [HANDLE, PDWORD_PTR, PDWORD_PTR]
    _GetProcessAffinityMask.restype = bool
    _GetProcessAffinityMask.errcheck = RaiseIfZero

    lpProcessAffinityMask = DWORD_PTR(0)
    lpSystemAffinityMask = DWORD_PTR(0)
    _GetProcessAffinityMask(hProcess, byref(lpProcessAffinityMask), byref(lpSystemAffinityMask))
    return lpProcessAffinityMask.value, lpSystemAffinityMask.value


# BOOL WINAPI SetProcessAffinityMask(
#   __in  HANDLE hProcess,
#   __in  DWORD_PTR dwProcessAffinityMask
# );
def SetProcessAffinityMask(hProcess, dwProcessAffinityMask):
    _SetProcessAffinityMask = windll.kernel32.SetProcessAffinityMask
    _SetProcessAffinityMask.argtypes = [HANDLE, DWORD_PTR]
    _SetProcessAffinityMask.restype = bool
    _SetProcessAffinityMask.errcheck = RaiseIfZero
    _SetProcessAffinityMask(hProcess, dwProcessAffinityMask)


# ------------------------------------------------------------------------------
# Toolhelp32 API


# HANDLE WINAPI CreateToolhelp32Snapshot(
#   __in  DWORD dwFlags,
#   __in  DWORD th32ProcessID
# );
def CreateToolhelp32Snapshot(dwFlags=TH32CS_SNAPALL, th32ProcessID=0):
    _CreateToolhelp32Snapshot = windll.kernel32.CreateToolhelp32Snapshot
    _CreateToolhelp32Snapshot.argtypes = [DWORD, DWORD]
    _CreateToolhelp32Snapshot.restype = HANDLE

    hSnapshot = _CreateToolhelp32Snapshot(dwFlags, th32ProcessID)
    if hSnapshot == INVALID_HANDLE_VALUE:
        raise ctypes.WinError()
    return SnapshotHandle(hSnapshot)


# BOOL WINAPI Process32First(
#   __in     HANDLE hSnapshot,
#   __inout  LPPROCESSENTRY32 lppe
# );
def Process32First(hSnapshot):
    _Process32First = windll.kernel32.Process32First
    _Process32First.argtypes = [HANDLE, LPPROCESSENTRY32]
    _Process32First.restype = bool

    pe = PROCESSENTRY32()
    pe.dwSize = sizeof(PROCESSENTRY32)
    success = _Process32First(hSnapshot, byref(pe))
    if not success:
        if GetLastError() == ERROR_NO_MORE_FILES:
            return None
        raise ctypes.WinError()
    return pe


# BOOL WINAPI Process32Next(
#   __in     HANDLE hSnapshot,
#   __out  LPPROCESSENTRY32 lppe
# );
def Process32Next(hSnapshot, pe=None):
    _Process32Next = windll.kernel32.Process32Next
    _Process32Next.argtypes = [HANDLE, LPPROCESSENTRY32]
    _Process32Next.restype = bool

    if pe is None:
        pe = PROCESSENTRY32()
    pe.dwSize = sizeof(PROCESSENTRY32)
    success = _Process32Next(hSnapshot, byref(pe))
    if not success:
        if GetLastError() == ERROR_NO_MORE_FILES:
            return None
        raise ctypes.WinError()
    return pe


# BOOL WINAPI Thread32First(
#   __in     HANDLE hSnapshot,
#   __inout  LPTHREADENTRY32 lpte
# );
def Thread32First(hSnapshot):
    _Thread32First = windll.kernel32.Thread32First
    _Thread32First.argtypes = [HANDLE, LPTHREADENTRY32]
    _Thread32First.restype = bool

    te = THREADENTRY32()
    te.dwSize = sizeof(THREADENTRY32)
    success = _Thread32First(hSnapshot, byref(te))
    if not success:
        if GetLastError() == ERROR_NO_MORE_FILES:
            return None
        raise ctypes.WinError()
    return te


# BOOL WINAPI Thread32Next(
#   __in     HANDLE hSnapshot,
#   __out  LPTHREADENTRY32 lpte
# );
def Thread32Next(hSnapshot, te=None):
    _Thread32Next = windll.kernel32.Thread32Next
    _Thread32Next.argtypes = [HANDLE, LPTHREADENTRY32]
    _Thread32Next.restype = bool

    if te is None:
        te = THREADENTRY32()
    te.dwSize = sizeof(THREADENTRY32)
    success = _Thread32Next(hSnapshot, byref(te))
    if not success:
        if GetLastError() == ERROR_NO_MORE_FILES:
            return None
        raise ctypes.WinError()
    return te


# BOOL WINAPI Module32First(
#   __in     HANDLE hSnapshot,
#   __inout  LPMODULEENTRY32 lpme
# );
def Module32First(hSnapshot):
    _Module32First = windll.kernel32.Module32First
    _Module32First.argtypes = [HANDLE, LPMODULEENTRY32]
    _Module32First.restype = bool

    me = MODULEENTRY32()
    me.dwSize = sizeof(MODULEENTRY32)
    success = _Module32First(hSnapshot, byref(me))
    if not success:
        if GetLastError() == ERROR_NO_MORE_FILES:
            return None
        raise ctypes.WinError()
    return me


# BOOL WINAPI Module32Next(
#   __in     HANDLE hSnapshot,
#   __out  LPMODULEENTRY32 lpme
# );
def Module32Next(hSnapshot, me=None):
    _Module32Next = windll.kernel32.Module32Next
    _Module32Next.argtypes = [HANDLE, LPMODULEENTRY32]
    _Module32Next.restype = bool

    if me is None:
        me = MODULEENTRY32()
    me.dwSize = sizeof(MODULEENTRY32)
    success = _Module32Next(hSnapshot, byref(me))
    if not success:
        if GetLastError() == ERROR_NO_MORE_FILES:
            return None
        raise ctypes.WinError()
    return me


# BOOL WINAPI Heap32First(
#   __inout  LPHEAPENTRY32 lphe,
#   __in     DWORD th32ProcessID,
#   __in     ULONG_PTR th32HeapID
# );
def Heap32First(th32ProcessID, th32HeapID):
    _Heap32First = windll.kernel32.Heap32First
    _Heap32First.argtypes = [LPHEAPENTRY32, DWORD, ULONG_PTR]
    _Heap32First.restype = bool

    he = HEAPENTRY32()
    he.dwSize = sizeof(HEAPENTRY32)
    success = _Heap32First(byref(he), th32ProcessID, th32HeapID)
    if not success:
        if GetLastError() == ERROR_NO_MORE_FILES:
            return None
        raise ctypes.WinError()
    return he


# BOOL WINAPI Heap32Next(
#   __out  LPHEAPENTRY32 lphe
# );
def Heap32Next(he):
    _Heap32Next = windll.kernel32.Heap32Next
    _Heap32Next.argtypes = [LPHEAPENTRY32]
    _Heap32Next.restype = bool

    he.dwSize = sizeof(HEAPENTRY32)
    success = _Heap32Next(byref(he))
    if not success:
        if GetLastError() == ERROR_NO_MORE_FILES:
            return None
        raise ctypes.WinError()
    return he


# BOOL WINAPI Heap32ListFirst(
#   __in     HANDLE hSnapshot,
#   __inout  LPHEAPLIST32 lphl
# );
def Heap32ListFirst(hSnapshot):
    _Heap32ListFirst = windll.kernel32.Heap32ListFirst
    _Heap32ListFirst.argtypes = [HANDLE, LPHEAPLIST32]
    _Heap32ListFirst.restype = bool

    hl = HEAPLIST32()
    hl.dwSize = sizeof(HEAPLIST32)
    success = _Heap32ListFirst(hSnapshot, byref(hl))
    if not success:
        if GetLastError() == ERROR_NO_MORE_FILES:
            return None
        raise ctypes.WinError()
    return hl


# BOOL WINAPI Heap32ListNext(
#   __in     HANDLE hSnapshot,
#   __out  LPHEAPLIST32 lphl
# );
def Heap32ListNext(hSnapshot, hl=None):
    _Heap32ListNext = windll.kernel32.Heap32ListNext
    _Heap32ListNext.argtypes = [HANDLE, LPHEAPLIST32]
    _Heap32ListNext.restype = bool

    if hl is None:
        hl = HEAPLIST32()
    hl.dwSize = sizeof(HEAPLIST32)
    success = _Heap32ListNext(hSnapshot, byref(hl))
    if not success:
        if GetLastError() == ERROR_NO_MORE_FILES:
            return None
        raise ctypes.WinError()
    return hl


# BOOL WINAPI Toolhelp32ReadProcessMemory(
#   __in   DWORD th32ProcessID,
#   __in   LPCVOID lpBaseAddress,
#   __out  LPVOID lpBuffer,
#   __in   SIZE_T cbRead,
#   __out  SIZE_T lpNumberOfBytesRead
# );
def Toolhelp32ReadProcessMemory(th32ProcessID, lpBaseAddress, cbRead):
    _Toolhelp32ReadProcessMemory = windll.kernel32.Toolhelp32ReadProcessMemory
    _Toolhelp32ReadProcessMemory.argtypes = [DWORD, LPVOID, LPVOID, SIZE_T, POINTER(SIZE_T)]
    _Toolhelp32ReadProcessMemory.restype = bool

    lpBuffer = ctypes.create_string_buffer("", cbRead)
    lpNumberOfBytesRead = SIZE_T(0)
    success = _Toolhelp32ReadProcessMemory(th32ProcessID, lpBaseAddress, lpBuffer, cbRead, byref(lpNumberOfBytesRead))
    if not success and GetLastError() != ERROR_PARTIAL_COPY:
        raise ctypes.WinError()
    return str(lpBuffer.raw)[: lpNumberOfBytesRead.value]


# ------------------------------------------------------------------------------
# Miscellaneous system information


# BOOL WINAPI GetProcessDEPPolicy(
#  __in   HANDLE hProcess,
#  __out  LPDWORD lpFlags,
#  __out  PBOOL lpPermanent
# );
# Contribution by ivanlef0u (http://ivanlef0u.fr/)
# XP SP3 and > only
def GetProcessDEPPolicy(hProcess):
    _GetProcessDEPPolicy = windll.kernel32.GetProcessDEPPolicy
    _GetProcessDEPPolicy.argtypes = [HANDLE, LPDWORD, PBOOL]
    _GetProcessDEPPolicy.restype = bool
    _GetProcessDEPPolicy.errcheck = RaiseIfZero

    lpFlags = DWORD(0)
    lpPermanent = BOOL(0)
    _GetProcessDEPPolicy(hProcess, byref(lpFlags), byref(lpPermanent))
    return (lpFlags.value, lpPermanent.value)


# DWORD WINAPI GetCurrentProcessorNumber(void);
def GetCurrentProcessorNumber():
    _GetCurrentProcessorNumber = windll.kernel32.GetCurrentProcessorNumber
    _GetCurrentProcessorNumber.argtypes = []
    _GetCurrentProcessorNumber.restype = DWORD
    _GetCurrentProcessorNumber.errcheck = RaiseIfZero
    return _GetCurrentProcessorNumber()


# VOID WINAPI FlushProcessWriteBuffers(void);
def FlushProcessWriteBuffers():
    _FlushProcessWriteBuffers = windll.kernel32.FlushProcessWriteBuffers
    _FlushProcessWriteBuffers.argtypes = []
    _FlushProcessWriteBuffers.restype = None
    _FlushProcessWriteBuffers()


# BOOL WINAPI GetLogicalProcessorInformation(
#   __out    PSYSTEM_LOGICAL_PROCESSOR_INFORMATION Buffer,
#   __inout  PDWORD ReturnLength
# );

# TO DO http://msdn.microsoft.com/en-us/library/ms683194(VS.85).aspx

# BOOL WINAPI GetProcessIoCounters(
#   __in   HANDLE hProcess,
#   __out  PIO_COUNTERS lpIoCounters
# );

# TO DO http://msdn.microsoft.com/en-us/library/ms683218(VS.85).aspx


# DWORD WINAPI GetGuiResources(
#   __in  HANDLE hProcess,
#   __in  DWORD uiFlags
# );
def GetGuiResources(hProcess, uiFlags=GR_GDIOBJECTS):
    _GetGuiResources = windll.kernel32.GetGuiResources
    _GetGuiResources.argtypes = [HANDLE, DWORD]
    _GetGuiResources.restype = DWORD

    dwCount = _GetGuiResources(hProcess, uiFlags)
    if dwCount == 0:
        errcode = GetLastError()
        if errcode != ERROR_SUCCESS:
            raise ctypes.WinError(errcode)
    return dwCount


# BOOL WINAPI GetProcessHandleCount(
#   __in     HANDLE hProcess,
#   __inout  PDWORD pdwHandleCount
# );
def GetProcessHandleCount(hProcess):
    _GetProcessHandleCount = windll.kernel32.GetProcessHandleCount
    _GetProcessHandleCount.argtypes = [HANDLE, PDWORD]
    _GetProcessHandleCount.restype = DWORD
    _GetProcessHandleCount.errcheck = RaiseIfZero

    pdwHandleCount = DWORD(0)
    _GetProcessHandleCount(hProcess, byref(pdwHandleCount))
    return pdwHandleCount.value


# BOOL WINAPI GetProcessTimes(
#   __in   HANDLE hProcess,
#   __out  LPFILETIME lpCreationTime,
#   __out  LPFILETIME lpExitTime,
#   __out  LPFILETIME lpKernelTime,
#   __out  LPFILETIME lpUserTime
# );
def GetProcessTimes(hProcess=None):
    _GetProcessTimes = windll.kernel32.GetProcessTimes
    _GetProcessTimes.argtypes = [HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME]
    _GetProcessTimes.restype = bool
    _GetProcessTimes.errcheck = RaiseIfZero

    if hProcess is None:
        hProcess = GetCurrentProcess()

    CreationTime = FILETIME()
    ExitTime = FILETIME()
    KernelTime = FILETIME()
    UserTime = FILETIME()

    _GetProcessTimes(hProcess, byref(CreationTime), byref(ExitTime), byref(KernelTime), byref(UserTime))

    return (CreationTime, ExitTime, KernelTime, UserTime)


# BOOL WINAPI FileTimeToSystemTime(
#   __in   const FILETIME *lpFileTime,
#   __out  LPSYSTEMTIME lpSystemTime
# );
def FileTimeToSystemTime(lpFileTime):
    _FileTimeToSystemTime = windll.kernel32.FileTimeToSystemTime
    _FileTimeToSystemTime.argtypes = [LPFILETIME, LPSYSTEMTIME]
    _FileTimeToSystemTime.restype = bool
    _FileTimeToSystemTime.errcheck = RaiseIfZero

    if isinstance(lpFileTime, FILETIME):
        FileTime = lpFileTime
    else:
        FileTime = FILETIME()
        FileTime.dwLowDateTime = lpFileTime & 0xFFFFFFFF
        FileTime.dwHighDateTime = lpFileTime >> 32
    SystemTime = SYSTEMTIME()
    _FileTimeToSystemTime(byref(FileTime), byref(SystemTime))
    return SystemTime


# void WINAPI GetSystemTimeAsFileTime(
#   __out  LPFILETIME lpSystemTimeAsFileTime
# );
def GetSystemTimeAsFileTime():
    _GetSystemTimeAsFileTime = windll.kernel32.GetSystemTimeAsFileTime
    _GetSystemTimeAsFileTime.argtypes = [LPFILETIME]
    _GetSystemTimeAsFileTime.restype = None

    FileTime = FILETIME()
    _GetSystemTimeAsFileTime(byref(FileTime))
    return FileTime


# ------------------------------------------------------------------------------
# Global ATOM API


# ATOM GlobalAddAtom(
#   __in  LPCTSTR lpString
# );
def GlobalAddAtomA(lpString):
    _GlobalAddAtomA = windll.kernel32.GlobalAddAtomA
    _GlobalAddAtomA.argtypes = [LPSTR]
    _GlobalAddAtomA.restype = ATOM
    _GlobalAddAtomA.errcheck = RaiseIfZero
    return _GlobalAddAtomA(lpString)


def GlobalAddAtomW(lpString):
    _GlobalAddAtomW = windll.kernel32.GlobalAddAtomW
    _GlobalAddAtomW.argtypes = [LPWSTR]
    _GlobalAddAtomW.restype = ATOM
    _GlobalAddAtomW.errcheck = RaiseIfZero
    return _GlobalAddAtomW(lpString)


GlobalAddAtom = GuessStringType(GlobalAddAtomA, GlobalAddAtomW)


# ATOM GlobalFindAtom(
#   __in  LPCTSTR lpString
# );
def GlobalFindAtomA(lpString):
    _GlobalFindAtomA = windll.kernel32.GlobalFindAtomA
    _GlobalFindAtomA.argtypes = [LPSTR]
    _GlobalFindAtomA.restype = ATOM
    _GlobalFindAtomA.errcheck = RaiseIfZero
    return _GlobalFindAtomA(lpString)


def GlobalFindAtomW(lpString):
    _GlobalFindAtomW = windll.kernel32.GlobalFindAtomW
    _GlobalFindAtomW.argtypes = [LPWSTR]
    _GlobalFindAtomW.restype = ATOM
    _GlobalFindAtomW.errcheck = RaiseIfZero
    return _GlobalFindAtomW(lpString)


GlobalFindAtom = GuessStringType(GlobalFindAtomA, GlobalFindAtomW)


# UINT GlobalGetAtomName(
#   __in   ATOM nAtom,
#   __out  LPTSTR lpBuffer,
#   __in   int nSize
# );
def GlobalGetAtomNameA(nAtom):
    _GlobalGetAtomNameA = windll.kernel32.GlobalGetAtomNameA
    _GlobalGetAtomNameA.argtypes = [ATOM, LPSTR, ctypes.c_int]
    _GlobalGetAtomNameA.restype = UINT
    _GlobalGetAtomNameA.errcheck = RaiseIfZero

    nSize = 64
    while 1:
        lpBuffer = ctypes.create_string_buffer("", nSize)
        nCopied = _GlobalGetAtomNameA(nAtom, lpBuffer, nSize)
        if nCopied < nSize - 1:
            break
        nSize = nSize + 64
    return lpBuffer.value


def GlobalGetAtomNameW(nAtom):
    _GlobalGetAtomNameW = windll.kernel32.GlobalGetAtomNameW
    _GlobalGetAtomNameW.argtypes = [ATOM, LPWSTR, ctypes.c_int]
    _GlobalGetAtomNameW.restype = UINT
    _GlobalGetAtomNameW.errcheck = RaiseIfZero

    nSize = 64
    while 1:
        lpBuffer = ctypes.create_unicode_buffer("", nSize)
        nCopied = _GlobalGetAtomNameW(nAtom, lpBuffer, nSize)
        if nCopied < nSize - 1:
            break
        nSize = nSize + 64
    return lpBuffer.value


GlobalGetAtomName = GuessStringType(GlobalGetAtomNameA, GlobalGetAtomNameW)


# ATOM GlobalDeleteAtom(
#   __in  ATOM nAtom
# );
def GlobalDeleteAtom(nAtom):
    _GlobalDeleteAtom = windll.kernel32.GlobalDeleteAtom
    _GlobalDeleteAtom.argtypes
    _GlobalDeleteAtom.restype
    SetLastError(ERROR_SUCCESS)
    _GlobalDeleteAtom(nAtom)
    error = GetLastError()
    if error != ERROR_SUCCESS:
        raise ctypes.WinError(error)


# ------------------------------------------------------------------------------
# Wow64


# DWORD WINAPI Wow64SuspendThread(
#   _In_  HANDLE hThread
# );
def Wow64SuspendThread(hThread):
    _Wow64SuspendThread = windll.kernel32.Wow64SuspendThread
    _Wow64SuspendThread.argtypes = [HANDLE]
    _Wow64SuspendThread.restype = DWORD

    previousCount = _Wow64SuspendThread(hThread)
    if previousCount == DWORD(-1).value:
        raise ctypes.WinError()
    return previousCount


# BOOLEAN WINAPI Wow64EnableWow64FsRedirection(
#   __in  BOOLEAN Wow64FsEnableRedirection
# );
def Wow64EnableWow64FsRedirection(Wow64FsEnableRedirection):
    """
    This function may not work reliably when there are nested calls. Therefore,
    this function has been replaced by the L{Wow64DisableWow64FsRedirection}
    and L{Wow64RevertWow64FsRedirection} functions.

    @see: U{http://msdn.microsoft.com/en-us/library/windows/desktop/aa365744(v=vs.85).aspx}
    """
    _Wow64EnableWow64FsRedirection = windll.kernel32.Wow64EnableWow64FsRedirection
    _Wow64EnableWow64FsRedirection.argtypes = [BOOLEAN]
    _Wow64EnableWow64FsRedirection.restype = BOOLEAN
    _Wow64EnableWow64FsRedirection.errcheck = RaiseIfZero


# BOOL WINAPI Wow64DisableWow64FsRedirection(
#   __out  PVOID *OldValue
# );
def Wow64DisableWow64FsRedirection():
    _Wow64DisableWow64FsRedirection = windll.kernel32.Wow64DisableWow64FsRedirection
    _Wow64DisableWow64FsRedirection.argtypes = [PPVOID]
    _Wow64DisableWow64FsRedirection.restype = BOOL
    _Wow64DisableWow64FsRedirection.errcheck = RaiseIfZero

    OldValue = PVOID(None)
    _Wow64DisableWow64FsRedirection(byref(OldValue))
    return OldValue


# BOOL WINAPI Wow64RevertWow64FsRedirection(
#   __in  PVOID OldValue
# );
def Wow64RevertWow64FsRedirection(OldValue):
    _Wow64RevertWow64FsRedirection = windll.kernel32.Wow64RevertWow64FsRedirection
    _Wow64RevertWow64FsRedirection.argtypes = [PVOID]
    _Wow64RevertWow64FsRedirection.restype = BOOL
    _Wow64RevertWow64FsRedirection.errcheck = RaiseIfZero
    _Wow64RevertWow64FsRedirection(OldValue)


# ==============================================================================
# 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()
# ==============================================================================

# ==============================================================================
# Mark functions that Psyco cannot compile.
# In your programs, don't use psyco.full().
# Call psyco.bind() on your main function instead.

try:
    import psyco

    psyco.cannotcompile(WaitForDebugEvent)
    psyco.cannotcompile(WaitForSingleObject)
    psyco.cannotcompile(WaitForSingleObjectEx)
    psyco.cannotcompile(WaitForMultipleObjects)
    psyco.cannotcompile(WaitForMultipleObjectsEx)
except ImportError:
    pass
# ==============================================================================
