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

__revision__ = "$Id$"

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

# XXX TODO
# + add transacted registry operations

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

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

# Privilege constants
SE_ASSIGNPRIMARYTOKEN_NAME = "SeAssignPrimaryTokenPrivilege"
SE_AUDIT_NAME = "SeAuditPrivilege"
SE_BACKUP_NAME = "SeBackupPrivilege"
SE_CHANGE_NOTIFY_NAME = "SeChangeNotifyPrivilege"
SE_CREATE_GLOBAL_NAME = "SeCreateGlobalPrivilege"
SE_CREATE_PAGEFILE_NAME = "SeCreatePagefilePrivilege"
SE_CREATE_PERMANENT_NAME = "SeCreatePermanentPrivilege"
SE_CREATE_SYMBOLIC_LINK_NAME = "SeCreateSymbolicLinkPrivilege"
SE_CREATE_TOKEN_NAME = "SeCreateTokenPrivilege"
SE_DEBUG_NAME = "SeDebugPrivilege"
SE_ENABLE_DELEGATION_NAME = "SeEnableDelegationPrivilege"
SE_IMPERSONATE_NAME = "SeImpersonatePrivilege"
SE_INC_BASE_PRIORITY_NAME = "SeIncreaseBasePriorityPrivilege"
SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege"
SE_INC_WORKING_SET_NAME = "SeIncreaseWorkingSetPrivilege"
SE_LOAD_DRIVER_NAME = "SeLoadDriverPrivilege"
SE_LOCK_MEMORY_NAME = "SeLockMemoryPrivilege"
SE_MACHINE_ACCOUNT_NAME = "SeMachineAccountPrivilege"
SE_MANAGE_VOLUME_NAME = "SeManageVolumePrivilege"
SE_PROF_SINGLE_PROCESS_NAME = "SeProfileSingleProcessPrivilege"
SE_RELABEL_NAME = "SeRelabelPrivilege"
SE_REMOTE_SHUTDOWN_NAME = "SeRemoteShutdownPrivilege"
SE_RESTORE_NAME = "SeRestorePrivilege"
SE_SECURITY_NAME = "SeSecurityPrivilege"
SE_SHUTDOWN_NAME = "SeShutdownPrivilege"
SE_SYNC_AGENT_NAME = "SeSyncAgentPrivilege"
SE_SYSTEM_ENVIRONMENT_NAME = "SeSystemEnvironmentPrivilege"
SE_SYSTEM_PROFILE_NAME = "SeSystemProfilePrivilege"
SE_SYSTEMTIME_NAME = "SeSystemtimePrivilege"
SE_TAKE_OWNERSHIP_NAME = "SeTakeOwnershipPrivilege"
SE_TCB_NAME = "SeTcbPrivilege"
SE_TIME_ZONE_NAME = "SeTimeZonePrivilege"
SE_TRUSTED_CREDMAN_ACCESS_NAME = "SeTrustedCredManAccessPrivilege"
SE_UNDOCK_NAME = "SeUndockPrivilege"
SE_UNSOLICITED_INPUT_NAME = "SeUnsolicitedInputPrivilege"

SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001
SE_PRIVILEGE_ENABLED = 0x00000002
SE_PRIVILEGE_REMOVED = 0x00000004
SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000

TOKEN_ADJUST_PRIVILEGES = 0x00000020

LOGON_WITH_PROFILE = 0x00000001
LOGON_NETCREDENTIALS_ONLY = 0x00000002

# Token access rights
TOKEN_ASSIGN_PRIMARY = 0x0001
TOKEN_DUPLICATE = 0x0002
TOKEN_IMPERSONATE = 0x0004
TOKEN_QUERY = 0x0008
TOKEN_QUERY_SOURCE = 0x0010
TOKEN_ADJUST_PRIVILEGES = 0x0020
TOKEN_ADJUST_GROUPS = 0x0040
TOKEN_ADJUST_DEFAULT = 0x0080
TOKEN_ADJUST_SESSIONID = 0x0100
TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY
TOKEN_ALL_ACCESS = (
    STANDARD_RIGHTS_REQUIRED
    | TOKEN_ASSIGN_PRIMARY
    | TOKEN_DUPLICATE
    | TOKEN_IMPERSONATE
    | TOKEN_QUERY
    | TOKEN_QUERY_SOURCE
    | TOKEN_ADJUST_PRIVILEGES
    | TOKEN_ADJUST_GROUPS
    | TOKEN_ADJUST_DEFAULT
    | TOKEN_ADJUST_SESSIONID
)

# Predefined HKEY values
HKEY_CLASSES_ROOT = 0x80000000
HKEY_CURRENT_USER = 0x80000001
HKEY_LOCAL_MACHINE = 0x80000002
HKEY_USERS = 0x80000003
HKEY_PERFORMANCE_DATA = 0x80000004
HKEY_CURRENT_CONFIG = 0x80000005

# Registry access rights
KEY_ALL_ACCESS = 0xF003F
KEY_CREATE_LINK = 0x0020
KEY_CREATE_SUB_KEY = 0x0004
KEY_ENUMERATE_SUB_KEYS = 0x0008
KEY_EXECUTE = 0x20019
KEY_NOTIFY = 0x0010
KEY_QUERY_VALUE = 0x0001
KEY_READ = 0x20019
KEY_SET_VALUE = 0x0002
KEY_WOW64_32KEY = 0x0200
KEY_WOW64_64KEY = 0x0100
KEY_WRITE = 0x20006

# Registry value types
REG_NONE = 0
REG_SZ = 1
REG_EXPAND_SZ = 2
REG_BINARY = 3
REG_DWORD = 4
REG_DWORD_LITTLE_ENDIAN = REG_DWORD
REG_DWORD_BIG_ENDIAN = 5
REG_LINK = 6
REG_MULTI_SZ = 7
REG_RESOURCE_LIST = 8
REG_FULL_RESOURCE_DESCRIPTOR = 9
REG_RESOURCE_REQUIREMENTS_LIST = 10
REG_QWORD = 11
REG_QWORD_LITTLE_ENDIAN = REG_QWORD

# --- TOKEN_PRIVILEGE structure ------------------------------------------------


# typedef struct _LUID {
#   DWORD LowPart;
#   LONG HighPart;
# } LUID,
#  *PLUID;
class LUID(Structure):
    _fields_ = [
        ("LowPart", DWORD),
        ("HighPart", LONG),
    ]


PLUID = POINTER(LUID)


# typedef struct _LUID_AND_ATTRIBUTES {
#   LUID Luid;
#   DWORD Attributes;
# } LUID_AND_ATTRIBUTES,
#  *PLUID_AND_ATTRIBUTES;
class LUID_AND_ATTRIBUTES(Structure):
    _fields_ = [
        ("Luid", LUID),
        ("Attributes", DWORD),
    ]


# typedef struct _TOKEN_PRIVILEGES {
#   DWORD PrivilegeCount;
#   LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY];
# } TOKEN_PRIVILEGES,
#  *PTOKEN_PRIVILEGES;
class TOKEN_PRIVILEGES(Structure):
    _fields_ = [
        ("PrivilegeCount", DWORD),
        ##        ("Privileges",      LUID_AND_ATTRIBUTES * ANYSIZE_ARRAY),
        ("Privileges", LUID_AND_ATTRIBUTES),
    ]
    # See comments on AdjustTokenPrivileges about this structure


PTOKEN_PRIVILEGES = POINTER(TOKEN_PRIVILEGES)

# --- GetTokenInformation enums and structures ---------------------------------

# typedef enum _TOKEN_INFORMATION_CLASS {
#   TokenUser                              = 1,
#   TokenGroups,
#   TokenPrivileges,
#   TokenOwner,
#   TokenPrimaryGroup,
#   TokenDefaultDacl,
#   TokenSource,
#   TokenType,
#   TokenImpersonationLevel,
#   TokenStatistics,
#   TokenRestrictedSids,
#   TokenSessionId,
#   TokenGroupsAndPrivileges,
#   TokenSessionReference,
#   TokenSandBoxInert,
#   TokenAuditPolicy,
#   TokenOrigin,
#   TokenElevationType,
#   TokenLinkedToken,
#   TokenElevation,
#   TokenHasRestrictions,
#   TokenAccessInformation,
#   TokenVirtualizationAllowed,
#   TokenVirtualizationEnabled,
#   TokenIntegrityLevel,
#   TokenUIAccess,
#   TokenMandatoryPolicy,
#   TokenLogonSid,
#   TokenIsAppContainer,
#   TokenCapabilities,
#   TokenAppContainerSid,
#   TokenAppContainerNumber,
#   TokenUserClaimAttributes,
#   TokenDeviceClaimAttributes,
#   TokenRestrictedUserClaimAttributes,
#   TokenRestrictedDeviceClaimAttributes,
#   TokenDeviceGroups,
#   TokenRestrictedDeviceGroups,
#   TokenSecurityAttributes,
#   TokenIsRestricted,
#   MaxTokenInfoClass
# } TOKEN_INFORMATION_CLASS, *PTOKEN_INFORMATION_CLASS;

TOKEN_INFORMATION_CLASS = ctypes.c_int

TokenUser = 1
TokenGroups = 2
TokenPrivileges = 3
TokenOwner = 4
TokenPrimaryGroup = 5
TokenDefaultDacl = 6
TokenSource = 7
TokenType = 8
TokenImpersonationLevel = 9
TokenStatistics = 10
TokenRestrictedSids = 11
TokenSessionId = 12
TokenGroupsAndPrivileges = 13
TokenSessionReference = 14
TokenSandBoxInert = 15
TokenAuditPolicy = 16
TokenOrigin = 17
TokenElevationType = 18
TokenLinkedToken = 19
TokenElevation = 20
TokenHasRestrictions = 21
TokenAccessInformation = 22
TokenVirtualizationAllowed = 23
TokenVirtualizationEnabled = 24
TokenIntegrityLevel = 25
TokenUIAccess = 26
TokenMandatoryPolicy = 27
TokenLogonSid = 28
TokenIsAppContainer = 29
TokenCapabilities = 30
TokenAppContainerSid = 31
TokenAppContainerNumber = 32
TokenUserClaimAttributes = 33
TokenDeviceClaimAttributes = 34
TokenRestrictedUserClaimAttributes = 35
TokenRestrictedDeviceClaimAttributes = 36
TokenDeviceGroups = 37
TokenRestrictedDeviceGroups = 38
TokenSecurityAttributes = 39
TokenIsRestricted = 40
MaxTokenInfoClass = 41

# typedef enum tagTOKEN_TYPE {
#   TokenPrimary         = 1,
#   TokenImpersonation
# } TOKEN_TYPE, *PTOKEN_TYPE;

TOKEN_TYPE = ctypes.c_int
PTOKEN_TYPE = POINTER(TOKEN_TYPE)

TokenPrimary = 1
TokenImpersonation = 2

# typedef enum  {
#   TokenElevationTypeDefault   = 1,
#   TokenElevationTypeFull,
#   TokenElevationTypeLimited
# } TOKEN_ELEVATION_TYPE , *PTOKEN_ELEVATION_TYPE;

TokenElevationTypeDefault = 1
TokenElevationTypeFull = 2
TokenElevationTypeLimited = 3

TOKEN_ELEVATION_TYPE = ctypes.c_int
PTOKEN_ELEVATION_TYPE = POINTER(TOKEN_ELEVATION_TYPE)

# typedef enum _SECURITY_IMPERSONATION_LEVEL {
#   SecurityAnonymous,
#   SecurityIdentification,
#   SecurityImpersonation,
#   SecurityDelegation
# } SECURITY_IMPERSONATION_LEVEL, *PSECURITY_IMPERSONATION_LEVEL;

SecurityAnonymous = 0
SecurityIdentification = 1
SecurityImpersonation = 2
SecurityDelegation = 3

SECURITY_IMPERSONATION_LEVEL = ctypes.c_int
PSECURITY_IMPERSONATION_LEVEL = POINTER(SECURITY_IMPERSONATION_LEVEL)


# typedef struct _SID_AND_ATTRIBUTES {
#   PSID  Sid;
#   DWORD Attributes;
# } SID_AND_ATTRIBUTES, *PSID_AND_ATTRIBUTES;
class SID_AND_ATTRIBUTES(Structure):
    _fields_ = [
        ("Sid", PSID),
        ("Attributes", DWORD),
    ]


PSID_AND_ATTRIBUTES = POINTER(SID_AND_ATTRIBUTES)


# typedef struct _TOKEN_USER {
#   SID_AND_ATTRIBUTES User;
# } TOKEN_USER, *PTOKEN_USER;
class TOKEN_USER(Structure):
    _fields_ = [
        ("User", SID_AND_ATTRIBUTES),
    ]


PTOKEN_USER = POINTER(TOKEN_USER)


# typedef struct _TOKEN_MANDATORY_LABEL {
#   SID_AND_ATTRIBUTES Label;
# } TOKEN_MANDATORY_LABEL, *PTOKEN_MANDATORY_LABEL;
class TOKEN_MANDATORY_LABEL(Structure):
    _fields_ = [
        ("Label", SID_AND_ATTRIBUTES),
    ]


PTOKEN_MANDATORY_LABEL = POINTER(TOKEN_MANDATORY_LABEL)


# typedef struct _TOKEN_OWNER {
#   PSID Owner;
# } TOKEN_OWNER, *PTOKEN_OWNER;
class TOKEN_OWNER(Structure):
    _fields_ = [
        ("Owner", PSID),
    ]


PTOKEN_OWNER = POINTER(TOKEN_OWNER)


# typedef struct _TOKEN_PRIMARY_GROUP {
#   PSID PrimaryGroup;
# } TOKEN_PRIMARY_GROUP, *PTOKEN_PRIMARY_GROUP;
class TOKEN_PRIMARY_GROUP(Structure):
    _fields_ = [
        ("PrimaryGroup", PSID),
    ]


PTOKEN_PRIMARY_GROUP = POINTER(TOKEN_PRIMARY_GROUP)


# typedef struct _TOKEN_APPCONTAINER_INFORMATION {
#   	PSID TokenAppContainer;
# } TOKEN_APPCONTAINER_INFORMATION, *PTOKEN_APPCONTAINER_INFORMATION;
class TOKEN_APPCONTAINER_INFORMATION(Structure):
    _fields_ = [
        ("TokenAppContainer", PSID),
    ]


PTOKEN_APPCONTAINER_INFORMATION = POINTER(TOKEN_APPCONTAINER_INFORMATION)


# typedef struct _TOKEN_ORIGIN {
#   LUID OriginatingLogonSession;
# } TOKEN_ORIGIN, *PTOKEN_ORIGIN;
class TOKEN_ORIGIN(Structure):
    _fields_ = [
        ("OriginatingLogonSession", LUID),
    ]


PTOKEN_ORIGIN = POINTER(TOKEN_ORIGIN)


# typedef struct _TOKEN_LINKED_TOKEN {
#   HANDLE LinkedToken;
# } TOKEN_LINKED_TOKEN, *PTOKEN_LINKED_TOKEN;
class TOKEN_LINKED_TOKEN(Structure):
    _fields_ = [
        ("LinkedToken", HANDLE),
    ]


PTOKEN_LINKED_TOKEN = POINTER(TOKEN_LINKED_TOKEN)


# typedef struct _TOKEN_STATISTICS {
#   LUID                         TokenId;
#   LUID                         AuthenticationId;
#   LARGE_INTEGER                ExpirationTime;
#   TOKEN_TYPE                   TokenType;
#   SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
#   DWORD                        DynamicCharged;
#   DWORD                        DynamicAvailable;
#   DWORD                        GroupCount;
#   DWORD                        PrivilegeCount;
#   LUID                         ModifiedId;
# } TOKEN_STATISTICS, *PTOKEN_STATISTICS;
class TOKEN_STATISTICS(Structure):
    _fields_ = [
        ("TokenId", LUID),
        ("AuthenticationId", LUID),
        ("ExpirationTime", LONGLONG),  # LARGE_INTEGER
        ("TokenType", TOKEN_TYPE),
        ("ImpersonationLevel", SECURITY_IMPERSONATION_LEVEL),
        ("DynamicCharged", DWORD),
        ("DynamicAvailable", DWORD),
        ("GroupCount", DWORD),
        ("PrivilegeCount", DWORD),
        ("ModifiedId", LUID),
    ]


PTOKEN_STATISTICS = POINTER(TOKEN_STATISTICS)

# --- SID_NAME_USE enum --------------------------------------------------------

# typedef enum _SID_NAME_USE {
#   SidTypeUser             = 1,
#   SidTypeGroup,
#   SidTypeDomain,
#   SidTypeAlias,
#   SidTypeWellKnownGroup,
#   SidTypeDeletedAccount,
#   SidTypeInvalid,
#   SidTypeUnknown,
#   SidTypeComputer,
#   SidTypeLabel
# } SID_NAME_USE, *PSID_NAME_USE;

SidTypeUser = 1
SidTypeGroup = 2
SidTypeDomain = 3
SidTypeAlias = 4
SidTypeWellKnownGroup = 5
SidTypeDeletedAccount = 6
SidTypeInvalid = 7
SidTypeUnknown = 8
SidTypeComputer = 9
SidTypeLabel = 10

# --- WAITCHAIN_NODE_INFO structure and types ----------------------------------

WCT_MAX_NODE_COUNT = 16
WCT_OBJNAME_LENGTH = 128
WCT_ASYNC_OPEN_FLAG = 1
WCTP_OPEN_ALL_FLAGS = WCT_ASYNC_OPEN_FLAG
WCT_OUT_OF_PROC_FLAG = 1
WCT_OUT_OF_PROC_COM_FLAG = 2
WCT_OUT_OF_PROC_CS_FLAG = 4
WCTP_GETINFO_ALL_FLAGS = WCT_OUT_OF_PROC_FLAG | WCT_OUT_OF_PROC_COM_FLAG | WCT_OUT_OF_PROC_CS_FLAG

HWCT = LPVOID

# typedef enum _WCT_OBJECT_TYPE
# {
#     WctCriticalSectionType = 1,
#     WctSendMessageType,
#     WctMutexType,
#     WctAlpcType,
#     WctComType,
#     WctThreadWaitType,
#     WctProcessWaitType,
#     WctThreadType,
#     WctComActivationType,
#     WctUnknownType,
#     WctMaxType
# } WCT_OBJECT_TYPE;

WCT_OBJECT_TYPE = DWORD

WctCriticalSectionType = 1
WctSendMessageType = 2
WctMutexType = 3
WctAlpcType = 4
WctComType = 5
WctThreadWaitType = 6
WctProcessWaitType = 7
WctThreadType = 8
WctComActivationType = 9
WctUnknownType = 10
WctMaxType = 11

# typedef enum _WCT_OBJECT_STATUS
# {
#     WctStatusNoAccess = 1,            // ACCESS_DENIED for this object
#     WctStatusRunning,                 // Thread status
#     WctStatusBlocked,                 // Thread status
#     WctStatusPidOnly,                 // Thread status
#     WctStatusPidOnlyRpcss,            // Thread status
#     WctStatusOwned,                   // Dispatcher object status
#     WctStatusNotOwned,                // Dispatcher object status
#     WctStatusAbandoned,               // Dispatcher object status
#     WctStatusUnknown,                 // All objects
#     WctStatusError,                   // All objects
#     WctStatusMax
# } WCT_OBJECT_STATUS;

WCT_OBJECT_STATUS = DWORD

WctStatusNoAccess = 1  # ACCESS_DENIED for this object
WctStatusRunning = 2  # Thread status
WctStatusBlocked = 3  # Thread status
WctStatusPidOnly = 4  # Thread status
WctStatusPidOnlyRpcss = 5  # Thread status
WctStatusOwned = 6  # Dispatcher object status
WctStatusNotOwned = 7  # Dispatcher object status
WctStatusAbandoned = 8  # Dispatcher object status
WctStatusUnknown = 9  # All objects
WctStatusError = 10  # All objects
WctStatusMax = 11

# typedef struct _WAITCHAIN_NODE_INFO {
#   WCT_OBJECT_TYPE   ObjectType;
#   WCT_OBJECT_STATUS ObjectStatus;
#   union {
#     struct {
#       WCHAR ObjectName[WCT_OBJNAME_LENGTH];
#       LARGE_INTEGER Timeout;
#       BOOL Alertable;
#     } LockObject;
#     struct {
#       DWORD ProcessId;
#       DWORD ThreadId;
#       DWORD WaitTime;
#       DWORD ContextSwitches;
#     } ThreadObject;
#   } ;
# }WAITCHAIN_NODE_INFO, *PWAITCHAIN_NODE_INFO;


class _WAITCHAIN_NODE_INFO_STRUCT_1(Structure):
    _fields_ = [
        ("ObjectName", WCHAR * WCT_OBJNAME_LENGTH),
        ("Timeout", LONGLONG),  # LARGE_INTEGER
        ("Alertable", BOOL),
    ]


class _WAITCHAIN_NODE_INFO_STRUCT_2(Structure):
    _fields_ = [
        ("ProcessId", DWORD),
        ("ThreadId", DWORD),
        ("WaitTime", DWORD),
        ("ContextSwitches", DWORD),
    ]


class _WAITCHAIN_NODE_INFO_UNION(Union):
    _fields_ = [
        ("LockObject", _WAITCHAIN_NODE_INFO_STRUCT_1),
        ("ThreadObject", _WAITCHAIN_NODE_INFO_STRUCT_2),
    ]


class WAITCHAIN_NODE_INFO(Structure):
    _fields_ = [
        ("ObjectType", WCT_OBJECT_TYPE),
        ("ObjectStatus", WCT_OBJECT_STATUS),
        ("u", _WAITCHAIN_NODE_INFO_UNION),
    ]


PWAITCHAIN_NODE_INFO = POINTER(WAITCHAIN_NODE_INFO)


class WaitChainNodeInfo(object):
    """
    Represents a node in the wait chain.

    It's a wrapper on the L{WAITCHAIN_NODE_INFO} structure.

    The following members are defined only
    if the node is of L{WctThreadType} type:
     - C{ProcessId}
     - C{ThreadId}
     - C{WaitTime}
     - C{ContextSwitches}

    @see: L{GetThreadWaitChain}

    @type ObjectName: unicode
    @ivar ObjectName: Object name. May be an empty string.

    @type ObjectType: int
    @ivar ObjectType: Object type.
        Should be one of the following values:
         - L{WctCriticalSectionType}
         - L{WctSendMessageType}
         - L{WctMutexType}
         - L{WctAlpcType}
         - L{WctComType}
         - L{WctThreadWaitType}
         - L{WctProcessWaitType}
         - L{WctThreadType}
         - L{WctComActivationType}
         - L{WctUnknownType}

    @type ObjectStatus: int
    @ivar ObjectStatus: Wait status.
        Should be one of the following values:
         - L{WctStatusNoAccess} I{(ACCESS_DENIED for this object)}
         - L{WctStatusRunning} I{(Thread status)}
         - L{WctStatusBlocked} I{(Thread status)}
         - L{WctStatusPidOnly} I{(Thread status)}
         - L{WctStatusPidOnlyRpcss} I{(Thread status)}
         - L{WctStatusOwned} I{(Dispatcher object status)}
         - L{WctStatusNotOwned} I{(Dispatcher object status)}
         - L{WctStatusAbandoned} I{(Dispatcher object status)}
         - L{WctStatusUnknown} I{(All objects)}
         - L{WctStatusError} I{(All objects)}

    @type ProcessId: int
    @ivar ProcessId: Process global ID.

    @type ThreadId: int
    @ivar ThreadId: Thread global ID.

    @type WaitTime: int
    @ivar WaitTime: Wait time.

    @type ContextSwitches: int
    @ivar ContextSwitches: Number of context switches.
    """

    # @type Timeout: int
    # @ivar Timeout: Currently not documented in MSDN.
    #
    # @type Alertable: bool
    # @ivar Alertable: Currently not documented in MSDN.

    # TODO: __repr__

    def __init__(self, aStructure):
        self.ObjectType = aStructure.ObjectType
        self.ObjectStatus = aStructure.ObjectStatus
        if self.ObjectType == WctThreadType:
            self.ProcessId = aStructure.u.ThreadObject.ProcessId
            self.ThreadId = aStructure.u.ThreadObject.ThreadId
            self.WaitTime = aStructure.u.ThreadObject.WaitTime
            self.ContextSwitches = aStructure.u.ThreadObject.ContextSwitches
            self.ObjectName = ""
        else:
            self.ObjectName = aStructure.u.LockObject.ObjectName.value
            # self.Timeout = aStructure.u.LockObject.Timeout
            # self.Alertable = bool(aStructure.u.LockObject.Alertable)


class ThreadWaitChainSessionHandle(Handle):
    """
    Thread wait chain session handle.

    Returned by L{OpenThreadWaitChainSession}.

    @see: L{Handle}
    """

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

    def _close(self):
        if self.value is None:
            raise ValueError("Handle was already closed!")
        CloseThreadWaitChainSession(self.value)

    def dup(self):
        raise NotImplementedError()

    def wait(self, dwMilliseconds=None):
        raise NotImplementedError()

    @property
    def inherit(self):
        return False

    @property
    def protectFromClose(self):
        return False


# --- Privilege dropping -------------------------------------------------------

SAFER_LEVEL_HANDLE = HANDLE

SAFER_SCOPEID_MACHINE = 1
SAFER_SCOPEID_USER = 2

SAFER_LEVEL_OPEN = 1

SAFER_LEVELID_DISALLOWED = 0x00000
SAFER_LEVELID_UNTRUSTED = 0x01000
SAFER_LEVELID_CONSTRAINED = 0x10000
SAFER_LEVELID_NORMALUSER = 0x20000
SAFER_LEVELID_FULLYTRUSTED = 0x40000

SAFER_POLICY_INFO_CLASS = DWORD
SaferPolicyLevelList = 1
SaferPolicyEnableTransparentEnforcement = 2
SaferPolicyDefaultLevel = 3
SaferPolicyEvaluateUserScope = 4
SaferPolicyScopeFlags = 5

SAFER_TOKEN_NULL_IF_EQUAL = 1
SAFER_TOKEN_COMPARE_ONLY = 2
SAFER_TOKEN_MAKE_INERT = 4
SAFER_TOKEN_WANT_FLAGS = 8
SAFER_TOKEN_MASK = 15

# --- Service Control Manager types, constants and structures ------------------

SC_HANDLE = HANDLE

SERVICES_ACTIVE_DATABASEW = "ServicesActive"
SERVICES_FAILED_DATABASEW = "ServicesFailed"

SERVICES_ACTIVE_DATABASEA = "ServicesActive"
SERVICES_FAILED_DATABASEA = "ServicesFailed"

SC_GROUP_IDENTIFIERW = "+"
SC_GROUP_IDENTIFIERA = "+"

SERVICE_NO_CHANGE = 0xFFFFFFFF

# enum SC_STATUS_TYPE
SC_STATUS_TYPE = ctypes.c_int
SC_STATUS_PROCESS_INFO = 0

# enum SC_ENUM_TYPE
SC_ENUM_TYPE = ctypes.c_int
SC_ENUM_PROCESS_INFO = 0

# Access rights
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms685981(v=vs.85).aspx

SERVICE_ALL_ACCESS = 0xF01FF
SERVICE_QUERY_CONFIG = 0x0001
SERVICE_CHANGE_CONFIG = 0x0002
SERVICE_QUERY_STATUS = 0x0004
SERVICE_ENUMERATE_DEPENDENTS = 0x0008
SERVICE_START = 0x0010
SERVICE_STOP = 0x0020
SERVICE_PAUSE_CONTINUE = 0x0040
SERVICE_INTERROGATE = 0x0080
SERVICE_USER_DEFINED_CONTROL = 0x0100

SC_MANAGER_ALL_ACCESS = 0xF003F
SC_MANAGER_CONNECT = 0x0001
SC_MANAGER_CREATE_SERVICE = 0x0002
SC_MANAGER_ENUMERATE_SERVICE = 0x0004
SC_MANAGER_LOCK = 0x0008
SC_MANAGER_QUERY_LOCK_STATUS = 0x0010
SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020

# CreateService() service start type
SERVICE_BOOT_START = 0x00000000
SERVICE_SYSTEM_START = 0x00000001
SERVICE_AUTO_START = 0x00000002
SERVICE_DEMAND_START = 0x00000003
SERVICE_DISABLED = 0x00000004

# CreateService() error control flags
SERVICE_ERROR_IGNORE = 0x00000000
SERVICE_ERROR_NORMAL = 0x00000001
SERVICE_ERROR_SEVERE = 0x00000002
SERVICE_ERROR_CRITICAL = 0x00000003

# EnumServicesStatusEx() service state filters
SERVICE_ACTIVE = 1
SERVICE_INACTIVE = 2
SERVICE_STATE_ALL = 3

# SERVICE_STATUS_PROCESS.dwServiceType
SERVICE_KERNEL_DRIVER = 0x00000001
SERVICE_FILE_SYSTEM_DRIVER = 0x00000002
SERVICE_ADAPTER = 0x00000004
SERVICE_RECOGNIZER_DRIVER = 0x00000008
SERVICE_WIN32_OWN_PROCESS = 0x00000010
SERVICE_WIN32_SHARE_PROCESS = 0x00000020
SERVICE_INTERACTIVE_PROCESS = 0x00000100

# EnumServicesStatusEx() service type filters (in addition to actual types)
SERVICE_DRIVER = 0x0000000B  # SERVICE_KERNEL_DRIVER and SERVICE_FILE_SYSTEM_DRIVER
SERVICE_WIN32 = 0x00000030  # SERVICE_WIN32_OWN_PROCESS and SERVICE_WIN32_SHARE_PROCESS

# SERVICE_STATUS_PROCESS.dwCurrentState
SERVICE_STOPPED = 0x00000001
SERVICE_START_PENDING = 0x00000002
SERVICE_STOP_PENDING = 0x00000003
SERVICE_RUNNING = 0x00000004
SERVICE_CONTINUE_PENDING = 0x00000005
SERVICE_PAUSE_PENDING = 0x00000006
SERVICE_PAUSED = 0x00000007

# SERVICE_STATUS_PROCESS.dwControlsAccepted
SERVICE_ACCEPT_STOP = 0x00000001
SERVICE_ACCEPT_PAUSE_CONTINUE = 0x00000002
SERVICE_ACCEPT_SHUTDOWN = 0x00000004
SERVICE_ACCEPT_PARAMCHANGE = 0x00000008
SERVICE_ACCEPT_NETBINDCHANGE = 0x00000010
SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x00000020
SERVICE_ACCEPT_POWEREVENT = 0x00000040
SERVICE_ACCEPT_SESSIONCHANGE = 0x00000080
SERVICE_ACCEPT_PRESHUTDOWN = 0x00000100

# SERVICE_STATUS_PROCESS.dwServiceFlags
SERVICE_RUNS_IN_SYSTEM_PROCESS = 0x00000001

# Service control flags
SERVICE_CONTROL_STOP = 0x00000001
SERVICE_CONTROL_PAUSE = 0x00000002
SERVICE_CONTROL_CONTINUE = 0x00000003
SERVICE_CONTROL_INTERROGATE = 0x00000004
SERVICE_CONTROL_SHUTDOWN = 0x00000005
SERVICE_CONTROL_PARAMCHANGE = 0x00000006
SERVICE_CONTROL_NETBINDADD = 0x00000007
SERVICE_CONTROL_NETBINDREMOVE = 0x00000008
SERVICE_CONTROL_NETBINDENABLE = 0x00000009
SERVICE_CONTROL_NETBINDDISABLE = 0x0000000A
SERVICE_CONTROL_DEVICEEVENT = 0x0000000B
SERVICE_CONTROL_HARDWAREPROFILECHANGE = 0x0000000C
SERVICE_CONTROL_POWEREVENT = 0x0000000D
SERVICE_CONTROL_SESSIONCHANGE = 0x0000000E

# Service control accepted bitmasks
SERVICE_ACCEPT_STOP = 0x00000001
SERVICE_ACCEPT_PAUSE_CONTINUE = 0x00000002
SERVICE_ACCEPT_SHUTDOWN = 0x00000004
SERVICE_ACCEPT_PARAMCHANGE = 0x00000008
SERVICE_ACCEPT_NETBINDCHANGE = 0x00000010
SERVICE_ACCEPT_HARDWAREPROFILECHANGE = 0x00000020
SERVICE_ACCEPT_POWEREVENT = 0x00000040
SERVICE_ACCEPT_SESSIONCHANGE = 0x00000080
SERVICE_ACCEPT_PRESHUTDOWN = 0x00000100
SERVICE_ACCEPT_TIMECHANGE = 0x00000200
SERVICE_ACCEPT_TRIGGEREVENT = 0x00000400
SERVICE_ACCEPT_USERMODEREBOOT = 0x00000800

# enum SC_ACTION_TYPE
SC_ACTION_NONE = 0
SC_ACTION_RESTART = 1
SC_ACTION_REBOOT = 2
SC_ACTION_RUN_COMMAND = 3

# QueryServiceConfig2
SERVICE_CONFIG_DESCRIPTION = 1
SERVICE_CONFIG_FAILURE_ACTIONS = 2


# typedef struct _SERVICE_STATUS {
#   DWORD dwServiceType;
#   DWORD dwCurrentState;
#   DWORD dwControlsAccepted;
#   DWORD dwWin32ExitCode;
#   DWORD dwServiceSpecificExitCode;
#   DWORD dwCheckPoint;
#   DWORD dwWaitHint;
# } SERVICE_STATUS, *LPSERVICE_STATUS;
class SERVICE_STATUS(Structure):
    _fields_ = [
        ("dwServiceType", DWORD),
        ("dwCurrentState", DWORD),
        ("dwControlsAccepted", DWORD),
        ("dwWin32ExitCode", DWORD),
        ("dwServiceSpecificExitCode", DWORD),
        ("dwCheckPoint", DWORD),
        ("dwWaitHint", DWORD),
    ]


LPSERVICE_STATUS = POINTER(SERVICE_STATUS)


# typedef struct _SERVICE_STATUS_PROCESS {
#   DWORD dwServiceType;
#   DWORD dwCurrentState;
#   DWORD dwControlsAccepted;
#   DWORD dwWin32ExitCode;
#   DWORD dwServiceSpecificExitCode;
#   DWORD dwCheckPoint;
#   DWORD dwWaitHint;
#   DWORD dwProcessId;
#   DWORD dwServiceFlags;
# } SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS;
class SERVICE_STATUS_PROCESS(Structure):
    _fields_ = SERVICE_STATUS._fields_ + [
        ("dwProcessId", DWORD),
        ("dwServiceFlags", DWORD),
    ]


LPSERVICE_STATUS_PROCESS = POINTER(SERVICE_STATUS_PROCESS)


# typedef struct _ENUM_SERVICE_STATUS {
#   LPTSTR         lpServiceName;
#   LPTSTR         lpDisplayName;
#   SERVICE_STATUS ServiceStatus;
# } ENUM_SERVICE_STATUS, *LPENUM_SERVICE_STATUS;
class ENUM_SERVICE_STATUSA(Structure):
    _fields_ = [
        ("lpServiceName", LPSTR),
        ("lpDisplayName", LPSTR),
        ("ServiceStatus", SERVICE_STATUS),
    ]


class ENUM_SERVICE_STATUSW(Structure):
    _fields_ = [
        ("lpServiceName", LPWSTR),
        ("lpDisplayName", LPWSTR),
        ("ServiceStatus", SERVICE_STATUS),
    ]


LPENUM_SERVICE_STATUSA = POINTER(ENUM_SERVICE_STATUSA)
LPENUM_SERVICE_STATUSW = POINTER(ENUM_SERVICE_STATUSW)


# typedef struct _ENUM_SERVICE_STATUS_PROCESS {
#   LPTSTR                 lpServiceName;
#   LPTSTR                 lpDisplayName;
#   SERVICE_STATUS_PROCESS ServiceStatusProcess;
# } ENUM_SERVICE_STATUS_PROCESS, *LPENUM_SERVICE_STATUS_PROCESS;
class ENUM_SERVICE_STATUS_PROCESSA(Structure):
    _fields_ = [
        ("lpServiceName", LPSTR),
        ("lpDisplayName", LPSTR),
        ("ServiceStatusProcess", SERVICE_STATUS_PROCESS),
    ]


class ENUM_SERVICE_STATUS_PROCESSW(Structure):
    _fields_ = [
        ("lpServiceName", LPWSTR),
        ("lpDisplayName", LPWSTR),
        ("ServiceStatusProcess", SERVICE_STATUS_PROCESS),
    ]


LPENUM_SERVICE_STATUS_PROCESSA = POINTER(ENUM_SERVICE_STATUS_PROCESSA)
LPENUM_SERVICE_STATUS_PROCESSW = POINTER(ENUM_SERVICE_STATUS_PROCESSW)


class ServiceStatus(object):
    """
    Wrapper for the L{SERVICE_STATUS} structure.
    """

    def __init__(self, raw):
        """
        @type  raw: L{SERVICE_STATUS}
        @param raw: Raw structure for this service status data.
        """
        self.ServiceType = raw.dwServiceType
        self.CurrentState = raw.dwCurrentState
        self.ControlsAccepted = raw.dwControlsAccepted
        self.Win32ExitCode = raw.dwWin32ExitCode
        self.ServiceSpecificExitCode = raw.dwServiceSpecificExitCode
        self.CheckPoint = raw.dwCheckPoint
        self.WaitHint = raw.dwWaitHint


class ServiceStatusProcess(object):
    """
    Wrapper for the L{SERVICE_STATUS_PROCESS} structure.
    """

    def __init__(self, raw):
        """
        @type  raw: L{SERVICE_STATUS_PROCESS}
        @param raw: Raw structure for this service status data.
        """
        self.ServiceType = raw.dwServiceType
        self.CurrentState = raw.dwCurrentState
        self.ControlsAccepted = raw.dwControlsAccepted
        self.Win32ExitCode = raw.dwWin32ExitCode
        self.ServiceSpecificExitCode = raw.dwServiceSpecificExitCode
        self.CheckPoint = raw.dwCheckPoint
        self.WaitHint = raw.dwWaitHint
        self.ProcessId = raw.dwProcessId
        self.ServiceFlags = raw.dwServiceFlags


class ServiceStatusEntry(object):
    """
    Service status entry returned by L{EnumServicesStatus}.
    """

    def __init__(self, raw):
        """
        @type  raw: L{ENUM_SERVICE_STATUSA} or L{ENUM_SERVICE_STATUSW}
        @param raw: Raw structure for this service status entry.
        """
        self.ServiceName = raw.lpServiceName
        self.DisplayName = raw.lpDisplayName
        self.ServiceType = raw.ServiceStatus.dwServiceType
        self.CurrentState = raw.ServiceStatus.dwCurrentState
        self.ControlsAccepted = raw.ServiceStatus.dwControlsAccepted
        self.Win32ExitCode = raw.ServiceStatus.dwWin32ExitCode
        self.ServiceSpecificExitCode = raw.ServiceStatus.dwServiceSpecificExitCode
        self.CheckPoint = raw.ServiceStatus.dwCheckPoint
        self.WaitHint = raw.ServiceStatus.dwWaitHint

    def __str__(self):
        output = []
        if self.ServiceType & SERVICE_INTERACTIVE_PROCESS:
            output.append("Interactive service")
        else:
            output.append("Service")
        if self.DisplayName:
            output.append('"%s" (%s)' % (self.DisplayName, self.ServiceName))
        else:
            output.append('"%s"' % self.ServiceName)
        if self.CurrentState == SERVICE_CONTINUE_PENDING:
            output.append("is about to continue.")
        elif self.CurrentState == SERVICE_PAUSE_PENDING:
            output.append("is pausing.")
        elif self.CurrentState == SERVICE_PAUSED:
            output.append("is paused.")
        elif self.CurrentState == SERVICE_RUNNING:
            output.append("is running.")
        elif self.CurrentState == SERVICE_START_PENDING:
            output.append("is starting.")
        elif self.CurrentState == SERVICE_STOP_PENDING:
            output.append("is stopping.")
        elif self.CurrentState == SERVICE_STOPPED:
            output.append("is stopped.")
        return " ".join(output)


class ServiceStatusProcessEntry(object):
    """
    Service status entry returned by L{EnumServicesStatusEx}.
    """

    def __init__(self, raw):
        """
        @type  raw: L{ENUM_SERVICE_STATUS_PROCESSA} or L{ENUM_SERVICE_STATUS_PROCESSW}
        @param raw: Raw structure for this service status entry.
        """
        self.ServiceName = raw.lpServiceName
        self.DisplayName = raw.lpDisplayName
        self.ServiceType = raw.ServiceStatusProcess.dwServiceType
        self.CurrentState = raw.ServiceStatusProcess.dwCurrentState
        self.ControlsAccepted = raw.ServiceStatusProcess.dwControlsAccepted
        self.Win32ExitCode = raw.ServiceStatusProcess.dwWin32ExitCode
        self.ServiceSpecificExitCode = raw.ServiceStatusProcess.dwServiceSpecificExitCode
        self.CheckPoint = raw.ServiceStatusProcess.dwCheckPoint
        self.WaitHint = raw.ServiceStatusProcess.dwWaitHint
        self.ProcessId = raw.ServiceStatusProcess.dwProcessId
        self.ServiceFlags = raw.ServiceStatusProcess.dwServiceFlags

    def __str__(self):
        output = []
        if self.ServiceType & SERVICE_INTERACTIVE_PROCESS:
            output.append("Interactive service ")
        else:
            output.append("Service ")
        if self.DisplayName:
            output.append('"%s" (%s)' % (self.DisplayName, self.ServiceName))
        else:
            output.append('"%s"' % self.ServiceName)
        if self.CurrentState == SERVICE_CONTINUE_PENDING:
            output.append(" is about to continue")
        elif self.CurrentState == SERVICE_PAUSE_PENDING:
            output.append(" is pausing")
        elif self.CurrentState == SERVICE_PAUSED:
            output.append(" is paused")
        elif self.CurrentState == SERVICE_RUNNING:
            output.append(" is running")
        elif self.CurrentState == SERVICE_START_PENDING:
            output.append(" is starting")
        elif self.CurrentState == SERVICE_STOP_PENDING:
            output.append(" is stopping")
        elif self.CurrentState == SERVICE_STOPPED:
            output.append(" is stopped")
        if self.ProcessId:
            output.append(" at process %d" % self.ProcessId)
        output.append(".")
        return "".join(output)


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


# XXX maybe add functions related to the tokens here?
class TokenHandle(Handle):
    """
    Access token handle.

    @see: L{Handle}
    """

    pass


class RegistryKeyHandle(UserModeHandle):
    """
    Registry key handle.
    """

    _TYPE = HKEY

    def _close(self):
        RegCloseKey(self.value)


class SaferLevelHandle(UserModeHandle):
    """
    Safer level handle.

    @see: U{http://msdn.microsoft.com/en-us/library/ms722425(VS.85).aspx}
    """

    _TYPE = SAFER_LEVEL_HANDLE

    def _close(self):
        SaferCloseLevel(self.value)


class ServiceHandle(UserModeHandle):
    """
    Service handle.

    @see: U{http://msdn.microsoft.com/en-us/library/windows/desktop/ms684330(v=vs.85).aspx}
    """

    _TYPE = SC_HANDLE

    def _close(self):
        CloseServiceHandle(self.value)


class ServiceControlManagerHandle(UserModeHandle):
    """
    Service Control Manager (SCM) handle.

    @see: U{http://msdn.microsoft.com/en-us/library/windows/desktop/ms684323(v=vs.85).aspx}
    """

    _TYPE = SC_HANDLE

    def _close(self):
        CloseServiceHandle(self.value)


# --- advapi32.dll -------------------------------------------------------------


# BOOL WINAPI GetUserName(
#   __out    LPTSTR lpBuffer,
#   __inout  LPDWORD lpnSize
# );
def GetUserNameA():
    _GetUserNameA = windll.advapi32.GetUserNameA
    _GetUserNameA.argtypes = [LPSTR, LPDWORD]
    _GetUserNameA.restype = bool

    nSize = DWORD(0)
    _GetUserNameA(None, byref(nSize))
    error = GetLastError()
    if error != ERROR_INSUFFICIENT_BUFFER:
        raise ctypes.WinError(error)
    lpBuffer = ctypes.create_string_buffer("", nSize.value + 1)
    success = _GetUserNameA(lpBuffer, byref(nSize))
    if not success:
        raise ctypes.WinError()
    return lpBuffer.value


def GetUserNameW():
    _GetUserNameW = windll.advapi32.GetUserNameW
    _GetUserNameW.argtypes = [LPWSTR, LPDWORD]
    _GetUserNameW.restype = bool

    nSize = DWORD(0)
    _GetUserNameW(None, byref(nSize))
    error = GetLastError()
    if error != ERROR_INSUFFICIENT_BUFFER:
        raise ctypes.WinError(error)
    lpBuffer = ctypes.create_unicode_buffer("", nSize.value + 1)
    success = _GetUserNameW(lpBuffer, byref(nSize))
    if not success:
        raise ctypes.WinError()
    return lpBuffer.value


GetUserName = DefaultStringType(GetUserNameA, GetUserNameW)

# BOOL WINAPI LookupAccountName(
#   __in_opt   LPCTSTR lpSystemName,
#   __in       LPCTSTR lpAccountName,
#   __out_opt  PSID Sid,
#   __inout    LPDWORD cbSid,
#   __out_opt  LPTSTR ReferencedDomainName,
#   __inout    LPDWORD cchReferencedDomainName,
#   __out      PSID_NAME_USE peUse
# );

# XXX TO DO


# BOOL WINAPI LookupAccountSid(
#   __in_opt   LPCTSTR lpSystemName,
#   __in       PSID lpSid,
#   __out_opt  LPTSTR lpName,
#   __inout    LPDWORD cchName,
#   __out_opt  LPTSTR lpReferencedDomainName,
#   __inout    LPDWORD cchReferencedDomainName,
#   __out      PSID_NAME_USE peUse
# );
def LookupAccountSidA(lpSystemName, lpSid):
    _LookupAccountSidA = windll.advapi32.LookupAccountSidA
    _LookupAccountSidA.argtypes = [LPSTR, PSID, LPSTR, LPDWORD, LPSTR, LPDWORD, LPDWORD]
    _LookupAccountSidA.restype = bool

    cchName = DWORD(0)
    cchReferencedDomainName = DWORD(0)
    peUse = DWORD(0)
    _LookupAccountSidA(lpSystemName, lpSid, None, byref(cchName), None, byref(cchReferencedDomainName), byref(peUse))
    error = GetLastError()
    if error != ERROR_INSUFFICIENT_BUFFER:
        raise ctypes.WinError(error)
    lpName = ctypes.create_string_buffer("", cchName + 1)
    lpReferencedDomainName = ctypes.create_string_buffer("", cchReferencedDomainName + 1)
    success = _LookupAccountSidA(
        lpSystemName, lpSid, lpName, byref(cchName), lpReferencedDomainName, byref(cchReferencedDomainName), byref(peUse)
    )
    if not success:
        raise ctypes.WinError()
    return lpName.value, lpReferencedDomainName.value, peUse.value


def LookupAccountSidW(lpSystemName, lpSid):
    _LookupAccountSidW = windll.advapi32.LookupAccountSidA
    _LookupAccountSidW.argtypes = [LPSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, LPDWORD]
    _LookupAccountSidW.restype = bool

    cchName = DWORD(0)
    cchReferencedDomainName = DWORD(0)
    peUse = DWORD(0)
    _LookupAccountSidW(lpSystemName, lpSid, None, byref(cchName), None, byref(cchReferencedDomainName), byref(peUse))
    error = GetLastError()
    if error != ERROR_INSUFFICIENT_BUFFER:
        raise ctypes.WinError(error)
    lpName = ctypes.create_unicode_buffer("", cchName + 1)
    lpReferencedDomainName = ctypes.create_unicode_buffer("", cchReferencedDomainName + 1)
    success = _LookupAccountSidW(
        lpSystemName, lpSid, lpName, byref(cchName), lpReferencedDomainName, byref(cchReferencedDomainName), byref(peUse)
    )
    if not success:
        raise ctypes.WinError()
    return lpName.value, lpReferencedDomainName.value, peUse.value


LookupAccountSid = GuessStringType(LookupAccountSidA, LookupAccountSidW)


# BOOL ConvertSidToStringSid(
#   __in   PSID Sid,
#   __out  LPTSTR *StringSid
# );
def ConvertSidToStringSidA(Sid):
    _ConvertSidToStringSidA = windll.advapi32.ConvertSidToStringSidA
    _ConvertSidToStringSidA.argtypes = [PSID, LPSTR]
    _ConvertSidToStringSidA.restype = bool
    _ConvertSidToStringSidA.errcheck = RaiseIfZero

    pStringSid = LPSTR()
    _ConvertSidToStringSidA(Sid, byref(pStringSid))
    try:
        StringSid = pStringSid.value
    finally:
        LocalFree(pStringSid)
    return StringSid


def ConvertSidToStringSidW(Sid):
    _ConvertSidToStringSidW = windll.advapi32.ConvertSidToStringSidW
    _ConvertSidToStringSidW.argtypes = [PSID, LPWSTR]
    _ConvertSidToStringSidW.restype = bool
    _ConvertSidToStringSidW.errcheck = RaiseIfZero

    pStringSid = LPWSTR()
    _ConvertSidToStringSidW(Sid, byref(pStringSid))
    try:
        StringSid = pStringSid.value
    finally:
        LocalFree(pStringSid)
    return StringSid


ConvertSidToStringSid = DefaultStringType(ConvertSidToStringSidA, ConvertSidToStringSidW)


# BOOL WINAPI ConvertStringSidToSid(
#   __in   LPCTSTR StringSid,
#   __out  PSID *Sid
# );
def ConvertStringSidToSidA(StringSid):
    _ConvertStringSidToSidA = windll.advapi32.ConvertStringSidToSidA
    _ConvertStringSidToSidA.argtypes = [LPSTR, PVOID]
    _ConvertStringSidToSidA.restype = bool
    _ConvertStringSidToSidA.errcheck = RaiseIfZero

    Sid = PVOID()
    _ConvertStringSidToSidA(StringSid, ctypes.pointer(Sid))
    return Sid.value


def ConvertStringSidToSidW(StringSid):
    _ConvertStringSidToSidW = windll.advapi32.ConvertStringSidToSidW
    _ConvertStringSidToSidW.argtypes = [LPWSTR, PVOID]
    _ConvertStringSidToSidW.restype = bool
    _ConvertStringSidToSidW.errcheck = RaiseIfZero

    Sid = PVOID()
    _ConvertStringSidToSidW(StringSid, ctypes.pointer(Sid))
    return Sid.value


ConvertStringSidToSid = GuessStringType(ConvertStringSidToSidA, ConvertStringSidToSidW)


# BOOL WINAPI IsValidSid(
#   __in  PSID pSid
# );
def IsValidSid(pSid):
    _IsValidSid = windll.advapi32.IsValidSid
    _IsValidSid.argtypes = [PSID]
    _IsValidSid.restype = bool
    return _IsValidSid(pSid)


# BOOL WINAPI EqualSid(
#   __in  PSID pSid1,
#   __in  PSID pSid2
# );
def EqualSid(pSid1, pSid2):
    _EqualSid = windll.advapi32.EqualSid
    _EqualSid.argtypes = [PSID, PSID]
    _EqualSid.restype = bool
    return _EqualSid(pSid1, pSid2)


# DWORD WINAPI GetLengthSid(
#   __in  PSID pSid
# );
def GetLengthSid(pSid):
    _GetLengthSid = windll.advapi32.GetLengthSid
    _GetLengthSid.argtypes = [PSID]
    _GetLengthSid.restype = DWORD
    return _GetLengthSid(pSid)


# BOOL WINAPI CopySid(
#   __in   DWORD nDestinationSidLength,
#   __out  PSID pDestinationSid,
#   __in   PSID pSourceSid
# );
def CopySid(pSourceSid):
    _CopySid = windll.advapi32.CopySid
    _CopySid.argtypes = [DWORD, PVOID, PSID]
    _CopySid.restype = bool
    _CopySid.errcheck = RaiseIfZero

    nDestinationSidLength = GetLengthSid(pSourceSid)
    DestinationSid = ctypes.create_string_buffer("", nDestinationSidLength)
    pDestinationSid = ctypes.cast(ctypes.pointer(DestinationSid), PVOID)
    _CopySid(nDestinationSidLength, pDestinationSid, pSourceSid)
    return ctypes.cast(pDestinationSid, PSID)


# PVOID WINAPI FreeSid(
#   __in  PSID pSid
# );
def FreeSid(pSid):
    _FreeSid = windll.advapi32.FreeSid
    _FreeSid.argtypes = [PSID]
    _FreeSid.restype = PSID
    _FreeSid.errcheck = RaiseIfNotZero
    _FreeSid(pSid)


# BOOL WINAPI OpenProcessToken(
#   __in   HANDLE ProcessHandle,
#   __in   DWORD DesiredAccess,
#   __out  PHANDLE TokenHandle
# );
def OpenProcessToken(ProcessHandle, DesiredAccess=TOKEN_ALL_ACCESS):
    _OpenProcessToken = windll.advapi32.OpenProcessToken
    _OpenProcessToken.argtypes = [HANDLE, DWORD, PHANDLE]
    _OpenProcessToken.restype = bool
    _OpenProcessToken.errcheck = RaiseIfZero

    NewTokenHandle = HANDLE(INVALID_HANDLE_VALUE)
    _OpenProcessToken(ProcessHandle, DesiredAccess, byref(NewTokenHandle))
    return TokenHandle(NewTokenHandle.value)


# BOOL WINAPI OpenThreadToken(
#   __in   HANDLE ThreadHandle,
#   __in   DWORD DesiredAccess,
#   __in   BOOL OpenAsSelf,
#   __out  PHANDLE TokenHandle
# );
def OpenThreadToken(ThreadHandle, DesiredAccess, OpenAsSelf=True):
    _OpenThreadToken = windll.advapi32.OpenThreadToken
    _OpenThreadToken.argtypes = [HANDLE, DWORD, BOOL, PHANDLE]
    _OpenThreadToken.restype = bool
    _OpenThreadToken.errcheck = RaiseIfZero

    NewTokenHandle = HANDLE(INVALID_HANDLE_VALUE)
    _OpenThreadToken(ThreadHandle, DesiredAccess, OpenAsSelf, byref(NewTokenHandle))
    return TokenHandle(NewTokenHandle.value)


# BOOL WINAPI DuplicateToken(
#   _In_   HANDLE ExistingTokenHandle,
#   _In_   SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
#   _Out_  PHANDLE DuplicateTokenHandle
# );
def DuplicateToken(ExistingTokenHandle, ImpersonationLevel=SecurityImpersonation):
    _DuplicateToken = windll.advapi32.DuplicateToken
    _DuplicateToken.argtypes = [HANDLE, SECURITY_IMPERSONATION_LEVEL, PHANDLE]
    _DuplicateToken.restype = bool
    _DuplicateToken.errcheck = RaiseIfZero

    DuplicateTokenHandle = HANDLE(INVALID_HANDLE_VALUE)
    _DuplicateToken(ExistingTokenHandle, ImpersonationLevel, byref(DuplicateTokenHandle))
    return TokenHandle(DuplicateTokenHandle.value)


# BOOL WINAPI DuplicateTokenEx(
#   _In_      HANDLE hExistingToken,
#   _In_      DWORD dwDesiredAccess,
#   _In_opt_  LPSECURITY_ATTRIBUTES lpTokenAttributes,
#   _In_      SECURITY_IMPERSONATION_LEVEL ImpersonationLevel,
#   _In_      TOKEN_TYPE TokenType,
#   _Out_     PHANDLE phNewToken
# );
def DuplicateTokenEx(
    hExistingToken,
    dwDesiredAccess=TOKEN_ALL_ACCESS,
    lpTokenAttributes=None,
    ImpersonationLevel=SecurityImpersonation,
    TokenType=TokenPrimary,
):
    _DuplicateTokenEx = windll.advapi32.DuplicateTokenEx
    _DuplicateTokenEx.argtypes = [HANDLE, DWORD, LPSECURITY_ATTRIBUTES, SECURITY_IMPERSONATION_LEVEL, TOKEN_TYPE, PHANDLE]
    _DuplicateTokenEx.restype = bool
    _DuplicateTokenEx.errcheck = RaiseIfZero

    DuplicateTokenHandle = HANDLE(INVALID_HANDLE_VALUE)
    _DuplicateTokenEx(hExistingToken, dwDesiredAccess, lpTokenAttributes, ImpersonationLevel, TokenType, byref(DuplicateTokenHandle))
    return TokenHandle(DuplicateTokenHandle.value)


# BOOL WINAPI IsTokenRestricted(
#   __in  HANDLE TokenHandle
# );
def IsTokenRestricted(hTokenHandle):
    _IsTokenRestricted = windll.advapi32.IsTokenRestricted
    _IsTokenRestricted.argtypes = [HANDLE]
    _IsTokenRestricted.restype = bool
    _IsTokenRestricted.errcheck = RaiseIfNotErrorSuccess

    SetLastError(ERROR_SUCCESS)
    return _IsTokenRestricted(hTokenHandle)


# BOOL WINAPI LookupPrivilegeValue(
#   __in_opt  LPCTSTR lpSystemName,
#   __in      LPCTSTR lpName,
#   __out     PLUID lpLuid
# );
def LookupPrivilegeValueA(lpSystemName, lpName):
    _LookupPrivilegeValueA = windll.advapi32.LookupPrivilegeValueA
    _LookupPrivilegeValueA.argtypes = [LPSTR, LPSTR, PLUID]
    _LookupPrivilegeValueA.restype = bool
    _LookupPrivilegeValueA.errcheck = RaiseIfZero

    lpLuid = LUID()
    if not lpSystemName:
        lpSystemName = None
    _LookupPrivilegeValueA(lpSystemName, lpName, byref(lpLuid))
    return lpLuid


def LookupPrivilegeValueW(lpSystemName, lpName):
    _LookupPrivilegeValueW = windll.advapi32.LookupPrivilegeValueW
    _LookupPrivilegeValueW.argtypes = [LPWSTR, LPWSTR, PLUID]
    _LookupPrivilegeValueW.restype = bool
    _LookupPrivilegeValueW.errcheck = RaiseIfZero

    lpLuid = LUID()
    if not lpSystemName:
        lpSystemName = None
    _LookupPrivilegeValueW(lpSystemName, lpName, byref(lpLuid))
    return lpLuid


LookupPrivilegeValue = GuessStringType(LookupPrivilegeValueA, LookupPrivilegeValueW)

# BOOL WINAPI LookupPrivilegeName(
#   __in_opt   LPCTSTR lpSystemName,
#   __in       PLUID lpLuid,
#   __out_opt  LPTSTR lpName,
#   __inout    LPDWORD cchName
# );


def LookupPrivilegeNameA(lpSystemName, lpLuid):
    _LookupPrivilegeNameA = windll.advapi32.LookupPrivilegeNameA
    _LookupPrivilegeNameA.argtypes = [LPSTR, PLUID, LPSTR, LPDWORD]
    _LookupPrivilegeNameA.restype = bool
    _LookupPrivilegeNameA.errcheck = RaiseIfZero

    cchName = DWORD(0)
    _LookupPrivilegeNameA(lpSystemName, byref(lpLuid), NULL, byref(cchName))
    lpName = ctypes.create_string_buffer("", cchName.value)
    _LookupPrivilegeNameA(lpSystemName, byref(lpLuid), byref(lpName), byref(cchName))
    return lpName.value


def LookupPrivilegeNameW(lpSystemName, lpLuid):
    _LookupPrivilegeNameW = windll.advapi32.LookupPrivilegeNameW
    _LookupPrivilegeNameW.argtypes = [LPWSTR, PLUID, LPWSTR, LPDWORD]
    _LookupPrivilegeNameW.restype = bool
    _LookupPrivilegeNameW.errcheck = RaiseIfZero

    cchName = DWORD(0)
    _LookupPrivilegeNameW(lpSystemName, byref(lpLuid), NULL, byref(cchName))
    lpName = ctypes.create_unicode_buffer("", cchName.value)
    _LookupPrivilegeNameW(lpSystemName, byref(lpLuid), byref(lpName), byref(cchName))
    return lpName.value


LookupPrivilegeName = GuessStringType(LookupPrivilegeNameA, LookupPrivilegeNameW)


# BOOL WINAPI AdjustTokenPrivileges(
#   __in       HANDLE TokenHandle,
#   __in       BOOL DisableAllPrivileges,
#   __in_opt   PTOKEN_PRIVILEGES NewState,
#   __in       DWORD BufferLength,
#   __out_opt  PTOKEN_PRIVILEGES PreviousState,
#   __out_opt  PDWORD ReturnLength
# );
def AdjustTokenPrivileges(TokenHandle, NewState=()):
    _AdjustTokenPrivileges = windll.advapi32.AdjustTokenPrivileges
    _AdjustTokenPrivileges.argtypes = [HANDLE, BOOL, LPVOID, DWORD, LPVOID, LPVOID]
    _AdjustTokenPrivileges.restype = bool
    _AdjustTokenPrivileges.errcheck = RaiseIfZero
    #
    # I don't know how to allocate variable sized structures in ctypes :(
    # so this hack will work by using always TOKEN_PRIVILEGES of one element
    # and calling the API many times. This also means the PreviousState
    # parameter won't be supported yet as it's too much hassle. In a future
    # version I look forward to implementing this function correctly.
    #
    if not NewState:
        _AdjustTokenPrivileges(TokenHandle, TRUE, NULL, 0, NULL, NULL)
    else:
        success = True
        for privilege, enabled in NewState:
            if not isinstance(privilege, LUID):
                privilege = LookupPrivilegeValue(NULL, privilege)
            if enabled == True:
                flags = SE_PRIVILEGE_ENABLED
            elif enabled == False:
                flags = SE_PRIVILEGE_REMOVED
            elif enabled == None:
                flags = 0
            else:
                flags = enabled
            laa = LUID_AND_ATTRIBUTES(privilege, flags)
            tp = TOKEN_PRIVILEGES(1, laa)
            _AdjustTokenPrivileges(TokenHandle, FALSE, byref(tp), sizeof(tp), NULL, NULL)


# BOOL WINAPI GetTokenInformation(
#   __in       HANDLE TokenHandle,
#   __in       TOKEN_INFORMATION_CLASS TokenInformationClass,
#   __out_opt  LPVOID TokenInformation,
#   __in       DWORD TokenInformationLength,
#   __out      PDWORD ReturnLength
# );
def GetTokenInformation(hTokenHandle, TokenInformationClass):
    if TokenInformationClass <= 0 or TokenInformationClass > MaxTokenInfoClass:
        raise ValueError("Invalid value for TokenInformationClass (%i)" % TokenInformationClass)

    # User SID.
    if TokenInformationClass == TokenUser:
        TokenInformation = TOKEN_USER()
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return TokenInformation.User.Sid.value

    # Owner SID.
    if TokenInformationClass == TokenOwner:
        TokenInformation = TOKEN_OWNER()
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return TokenInformation.Owner.value

    # Primary group SID.
    if TokenInformationClass == TokenOwner:
        TokenInformation = TOKEN_PRIMARY_GROUP()
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return TokenInformation.PrimaryGroup.value

    # App container SID.
    if TokenInformationClass == TokenAppContainerSid:
        TokenInformation = TOKEN_APPCONTAINER_INFORMATION()
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return TokenInformation.TokenAppContainer.value

    # Integrity level SID.
    if TokenInformationClass == TokenIntegrityLevel:
        TokenInformation = TOKEN_MANDATORY_LABEL()
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return TokenInformation.Label.Sid.value, TokenInformation.Label.Attributes

    # Logon session LUID.
    if TokenInformationClass == TokenOrigin:
        TokenInformation = TOKEN_ORIGIN()
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return TokenInformation.OriginatingLogonSession

    # Primary or impersonation token.
    if TokenInformationClass == TokenType:
        TokenInformation = TOKEN_TYPE(0)
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return TokenInformation.value

    # Elevated token.
    if TokenInformationClass == TokenElevation:
        TokenInformation = TOKEN_ELEVATION(0)
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return TokenInformation.value

    # Security impersonation level.
    if TokenInformationClass == TokenElevation:
        TokenInformation = SECURITY_IMPERSONATION_LEVEL(0)
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return TokenInformation.value

    # Session ID and other DWORD values.
    if TokenInformationClass in (TokenSessionId, TokenAppContainerNumber):
        TokenInformation = DWORD(0)
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return TokenInformation.value

    # Various boolean flags.
    if TokenInformationClass in (
        TokenSandBoxInert,
        TokenHasRestrictions,
        TokenUIAccess,
        TokenVirtualizationAllowed,
        TokenVirtualizationEnabled,
    ):
        TokenInformation = DWORD(0)
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return bool(TokenInformation.value)

    # Linked token.
    if TokenInformationClass == TokenLinkedToken:
        TokenInformation = TOKEN_LINKED_TOKEN(0)
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return TokenHandle(TokenInformation.LinkedToken.value, bOwnership=True)

    # Token statistics.
    if TokenInformationClass == TokenStatistics:
        TokenInformation = TOKEN_STATISTICS()
        _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation)
        return TokenInformation  # TODO add a class wrapper?

    # Currently unsupported flags.
    raise NotImplementedError("TokenInformationClass(%i) not yet supported!" % TokenInformationClass)


def _internal_GetTokenInformation(hTokenHandle, TokenInformationClass, TokenInformation):
    _GetTokenInformation = windll.advapi32.GetTokenInformation
    _GetTokenInformation.argtypes = [HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD]
    _GetTokenInformation.restype = bool
    _GetTokenInformation.errcheck = RaiseIfZero

    ReturnLength = DWORD(0)
    TokenInformationLength = SIZEOF(TokenInformation)
    _GetTokenInformation(hTokenHandle, TokenInformationClass, byref(TokenInformation), TokenInformationLength, byref(ReturnLength))
    if ReturnLength.value != TokenInformationLength:
        raise ctypes.WinError(ERROR_INSUFFICIENT_BUFFER)
    return TokenInformation


# BOOL WINAPI SetTokenInformation(
#   __in  HANDLE TokenHandle,
#   __in  TOKEN_INFORMATION_CLASS TokenInformationClass,
#   __in  LPVOID TokenInformation,
#   __in  DWORD TokenInformationLength
# );

# XXX TODO


# BOOL WINAPI CreateProcessWithLogonW(
#   __in         LPCWSTR lpUsername,
#   __in_opt     LPCWSTR lpDomain,
#   __in         LPCWSTR lpPassword,
#   __in         DWORD dwLogonFlags,
#   __in_opt     LPCWSTR lpApplicationName,
#   __inout_opt  LPWSTR lpCommandLine,
#   __in         DWORD dwCreationFlags,
#   __in_opt     LPVOID lpEnvironment,
#   __in_opt     LPCWSTR lpCurrentDirectory,
#   __in         LPSTARTUPINFOW lpStartupInfo,
#   __out        LPPROCESS_INFORMATION lpProcessInfo
# );
def CreateProcessWithLogonW(
    lpUsername=None,
    lpDomain=None,
    lpPassword=None,
    dwLogonFlags=0,
    lpApplicationName=None,
    lpCommandLine=None,
    dwCreationFlags=0,
    lpEnvironment=None,
    lpCurrentDirectory=None,
    lpStartupInfo=None,
):
    _CreateProcessWithLogonW = windll.advapi32.CreateProcessWithLogonW
    _CreateProcessWithLogonW.argtypes = [
        LPWSTR,
        LPWSTR,
        LPWSTR,
        DWORD,
        LPWSTR,
        LPWSTR,
        DWORD,
        LPVOID,
        LPWSTR,
        LPVOID,
        LPPROCESS_INFORMATION,
    ]
    _CreateProcessWithLogonW.restype = bool
    _CreateProcessWithLogonW.errcheck = RaiseIfZero

    if not lpUsername:
        lpUsername = None
    if not lpDomain:
        lpDomain = None
    if not lpPassword:
        lpPassword = None
    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 lpStartupInfo:
        lpStartupInfo = STARTUPINFOW()
        lpStartupInfo.cb = sizeof(STARTUPINFOW)
        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
    _CreateProcessWithLogonW(
        lpUsername,
        lpDomain,
        lpPassword,
        dwLogonFlags,
        lpApplicationName,
        lpCommandLine,
        dwCreationFlags,
        lpEnvironment,
        lpCurrentDirectory,
        byref(lpStartupInfo),
        byref(lpProcessInformation),
    )
    return ProcessInformation(lpProcessInformation)


CreateProcessWithLogonA = MakeANSIVersion(CreateProcessWithLogonW)
CreateProcessWithLogon = DefaultStringType(CreateProcessWithLogonA, CreateProcessWithLogonW)


# BOOL WINAPI CreateProcessWithTokenW(
#   __in         HANDLE hToken,
#   __in         DWORD dwLogonFlags,
#   __in_opt     LPCWSTR lpApplicationName,
#   __inout_opt  LPWSTR lpCommandLine,
#   __in         DWORD dwCreationFlags,
#   __in_opt     LPVOID lpEnvironment,
#   __in_opt     LPCWSTR lpCurrentDirectory,
#   __in         LPSTARTUPINFOW lpStartupInfo,
#   __out        LPPROCESS_INFORMATION lpProcessInfo
# );
def CreateProcessWithTokenW(
    hToken=None,
    dwLogonFlags=0,
    lpApplicationName=None,
    lpCommandLine=None,
    dwCreationFlags=0,
    lpEnvironment=None,
    lpCurrentDirectory=None,
    lpStartupInfo=None,
):
    _CreateProcessWithTokenW = windll.advapi32.CreateProcessWithTokenW
    _CreateProcessWithTokenW.argtypes = [HANDLE, DWORD, LPWSTR, LPWSTR, DWORD, LPVOID, LPWSTR, LPVOID, LPPROCESS_INFORMATION]
    _CreateProcessWithTokenW.restype = bool
    _CreateProcessWithTokenW.errcheck = RaiseIfZero

    if not hToken:
        hToken = None
    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 lpStartupInfo:
        lpStartupInfo = STARTUPINFOW()
        lpStartupInfo.cb = sizeof(STARTUPINFOW)
        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
    _CreateProcessWithTokenW(
        hToken,
        dwLogonFlags,
        lpApplicationName,
        lpCommandLine,
        dwCreationFlags,
        lpEnvironment,
        lpCurrentDirectory,
        byref(lpStartupInfo),
        byref(lpProcessInformation),
    )
    return ProcessInformation(lpProcessInformation)


CreateProcessWithTokenA = MakeANSIVersion(CreateProcessWithTokenW)
CreateProcessWithToken = DefaultStringType(CreateProcessWithTokenA, CreateProcessWithTokenW)


# BOOL WINAPI CreateProcessAsUser(
#   __in_opt     HANDLE hToken,
#   __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 CreateProcessAsUserA(
    hToken=None,
    lpApplicationName=None,
    lpCommandLine=None,
    lpProcessAttributes=None,
    lpThreadAttributes=None,
    bInheritHandles=False,
    dwCreationFlags=0,
    lpEnvironment=None,
    lpCurrentDirectory=None,
    lpStartupInfo=None,
):
    _CreateProcessAsUserA = windll.advapi32.CreateProcessAsUserA
    _CreateProcessAsUserA.argtypes = [
        HANDLE,
        LPSTR,
        LPSTR,
        LPSECURITY_ATTRIBUTES,
        LPSECURITY_ATTRIBUTES,
        BOOL,
        DWORD,
        LPVOID,
        LPSTR,
        LPVOID,
        LPPROCESS_INFORMATION,
    ]
    _CreateProcessAsUserA.restype = bool
    _CreateProcessAsUserA.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
    _CreateProcessAsUserA(
        hToken,
        lpApplicationName,
        lpCommandLine,
        lpProcessAttributes,
        lpThreadAttributes,
        bool(bInheritHandles),
        dwCreationFlags,
        lpEnvironment,
        lpCurrentDirectory,
        byref(lpStartupInfo),
        byref(lpProcessInformation),
    )
    return ProcessInformation(lpProcessInformation)


def CreateProcessAsUserW(
    hToken=None,
    lpApplicationName=None,
    lpCommandLine=None,
    lpProcessAttributes=None,
    lpThreadAttributes=None,
    bInheritHandles=False,
    dwCreationFlags=0,
    lpEnvironment=None,
    lpCurrentDirectory=None,
    lpStartupInfo=None,
):
    _CreateProcessAsUserW = windll.advapi32.CreateProcessAsUserW
    _CreateProcessAsUserW.argtypes = [
        HANDLE,
        LPWSTR,
        LPWSTR,
        LPSECURITY_ATTRIBUTES,
        LPSECURITY_ATTRIBUTES,
        BOOL,
        DWORD,
        LPVOID,
        LPWSTR,
        LPVOID,
        LPPROCESS_INFORMATION,
    ]
    _CreateProcessAsUserW.restype = bool
    _CreateProcessAsUserW.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
    _CreateProcessAsUserW(
        hToken,
        lpApplicationName,
        lpCommandLine,
        lpProcessAttributes,
        lpThreadAttributes,
        bool(bInheritHandles),
        dwCreationFlags,
        lpEnvironment,
        lpCurrentDirectory,
        byref(lpStartupInfo),
        byref(lpProcessInformation),
    )
    return ProcessInformation(lpProcessInformation)


CreateProcessAsUser = GuessStringType(CreateProcessAsUserA, CreateProcessAsUserW)

# VOID CALLBACK WaitChainCallback(
#     HWCT WctHandle,
#     DWORD_PTR Context,
#     DWORD CallbackStatus,
#     LPDWORD NodeCount,
#     PWAITCHAIN_NODE_INFO NodeInfoArray,
#     LPBOOL IsCycle
# );
PWAITCHAINCALLBACK = WINFUNCTYPE(HWCT, DWORD_PTR, DWORD, LPDWORD, PWAITCHAIN_NODE_INFO, LPBOOL)


# HWCT WINAPI OpenThreadWaitChainSession(
#   __in      DWORD Flags,
#   __in_opt  PWAITCHAINCALLBACK callback
# );
def OpenThreadWaitChainSession(Flags=0, callback=None):
    _OpenThreadWaitChainSession = windll.advapi32.OpenThreadWaitChainSession
    _OpenThreadWaitChainSession.argtypes = [DWORD, PVOID]
    _OpenThreadWaitChainSession.restype = HWCT
    _OpenThreadWaitChainSession.errcheck = RaiseIfZero

    if callback is not None:
        callback = PWAITCHAINCALLBACK(callback)
    aHandle = _OpenThreadWaitChainSession(Flags, callback)
    return ThreadWaitChainSessionHandle(aHandle)


# BOOL WINAPI GetThreadWaitChain(
#   _In_      HWCT WctHandle,
#   _In_opt_  DWORD_PTR Context,
#   _In_      DWORD Flags,
#   _In_      DWORD ThreadId,
#   _Inout_   LPDWORD NodeCount,
#   _Out_     PWAITCHAIN_NODE_INFO NodeInfoArray,
#   _Out_     LPBOOL IsCycle
# );
def GetThreadWaitChain(WctHandle, Context=None, Flags=WCTP_GETINFO_ALL_FLAGS, ThreadId=-1, NodeCount=WCT_MAX_NODE_COUNT):
    _GetThreadWaitChain = windll.advapi32.GetThreadWaitChain
    _GetThreadWaitChain.argtypes = [HWCT, LPDWORD, DWORD, DWORD, LPDWORD, PWAITCHAIN_NODE_INFO, LPBOOL]
    _GetThreadWaitChain.restype = bool
    _GetThreadWaitChain.errcheck = RaiseIfZero

    dwNodeCount = DWORD(NodeCount)
    NodeInfoArray = (WAITCHAIN_NODE_INFO * NodeCount)()
    IsCycle = BOOL(0)
    _GetThreadWaitChain(
        WctHandle,
        Context,
        Flags,
        ThreadId,
        byref(dwNodeCount),
        ctypes.cast(ctypes.pointer(NodeInfoArray), PWAITCHAIN_NODE_INFO),
        byref(IsCycle),
    )
    while dwNodeCount.value > NodeCount:
        NodeCount = dwNodeCount.value
        NodeInfoArray = (WAITCHAIN_NODE_INFO * NodeCount)()
        _GetThreadWaitChain(
            WctHandle,
            Context,
            Flags,
            ThreadId,
            byref(dwNodeCount),
            ctypes.cast(ctypes.pointer(NodeInfoArray), PWAITCHAIN_NODE_INFO),
            byref(IsCycle),
        )
    return ([WaitChainNodeInfo(NodeInfoArray[index]) for index in compat.xrange(dwNodeCount.value)], bool(IsCycle.value))


# VOID WINAPI CloseThreadWaitChainSession(
#   __in  HWCT WctHandle
# );
def CloseThreadWaitChainSession(WctHandle):
    _CloseThreadWaitChainSession = windll.advapi32.CloseThreadWaitChainSession
    _CloseThreadWaitChainSession.argtypes = [HWCT]
    _CloseThreadWaitChainSession(WctHandle)


# BOOL WINAPI SaferCreateLevel(
#   __in        DWORD dwScopeId,
#   __in        DWORD dwLevelId,
#   __in        DWORD OpenFlags,
#   __out       SAFER_LEVEL_HANDLE *pLevelHandle,
#   __reserved  LPVOID lpReserved
# );
def SaferCreateLevel(dwScopeId=SAFER_SCOPEID_USER, dwLevelId=SAFER_LEVELID_NORMALUSER, OpenFlags=0):
    _SaferCreateLevel = windll.advapi32.SaferCreateLevel
    _SaferCreateLevel.argtypes = [DWORD, DWORD, DWORD, POINTER(SAFER_LEVEL_HANDLE), LPVOID]
    _SaferCreateLevel.restype = BOOL
    _SaferCreateLevel.errcheck = RaiseIfZero

    hLevelHandle = SAFER_LEVEL_HANDLE(INVALID_HANDLE_VALUE)
    _SaferCreateLevel(dwScopeId, dwLevelId, OpenFlags, byref(hLevelHandle), None)
    return SaferLevelHandle(hLevelHandle.value)


# BOOL WINAPI SaferIdentifyLevel(
#   __in        DWORD dwNumProperties,
#   __in_opt    PSAFER_CODE_PROPERTIES pCodeProperties,
#   __out       SAFER_LEVEL_HANDLE *pLevelHandle,
#   __reserved  LPVOID lpReserved
# );

# XXX TODO


# BOOL WINAPI SaferComputeTokenFromLevel(
#   __in         SAFER_LEVEL_HANDLE LevelHandle,
#   __in_opt     HANDLE InAccessToken,
#   __out        PHANDLE OutAccessToken,
#   __in         DWORD dwFlags,
#   __inout_opt  LPVOID lpReserved
# );
def SaferComputeTokenFromLevel(LevelHandle, InAccessToken=None, dwFlags=0):
    _SaferComputeTokenFromLevel = windll.advapi32.SaferComputeTokenFromLevel
    _SaferComputeTokenFromLevel.argtypes = [SAFER_LEVEL_HANDLE, HANDLE, PHANDLE, DWORD, LPDWORD]
    _SaferComputeTokenFromLevel.restype = BOOL
    _SaferComputeTokenFromLevel.errcheck = RaiseIfZero

    OutAccessToken = HANDLE(INVALID_HANDLE_VALUE)
    lpReserved = DWORD(0)
    _SaferComputeTokenFromLevel(LevelHandle, InAccessToken, byref(OutAccessToken), dwFlags, byref(lpReserved))
    return TokenHandle(OutAccessToken.value), lpReserved.value


# BOOL WINAPI SaferCloseLevel(
#   __in  SAFER_LEVEL_HANDLE hLevelHandle
# );
def SaferCloseLevel(hLevelHandle):
    _SaferCloseLevel = windll.advapi32.SaferCloseLevel
    _SaferCloseLevel.argtypes = [SAFER_LEVEL_HANDLE]
    _SaferCloseLevel.restype = BOOL
    _SaferCloseLevel.errcheck = RaiseIfZero

    if hasattr(hLevelHandle, "value"):
        _SaferCloseLevel(hLevelHandle.value)
    else:
        _SaferCloseLevel(hLevelHandle)


# BOOL SaferiIsExecutableFileType(
#   __in  LPCWSTR szFullPath,
#   __in  BOOLEAN bFromShellExecute
# );
def SaferiIsExecutableFileType(szFullPath, bFromShellExecute=False):
    _SaferiIsExecutableFileType = windll.advapi32.SaferiIsExecutableFileType
    _SaferiIsExecutableFileType.argtypes = [LPWSTR, BOOLEAN]
    _SaferiIsExecutableFileType.restype = BOOL
    _SaferiIsExecutableFileType.errcheck = RaiseIfLastError

    SetLastError(ERROR_SUCCESS)
    return bool(_SaferiIsExecutableFileType(compat.unicode(szFullPath), bFromShellExecute))


# useful alias since I'm likely to misspell it :P
SaferIsExecutableFileType = SaferiIsExecutableFileType

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


# LONG WINAPI RegCloseKey(
#   __in  HKEY hKey
# );
def RegCloseKey(hKey):
    if hasattr(hKey, "value"):
        value = hKey.value
    else:
        value = hKey

    if value in (HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_USERS, HKEY_PERFORMANCE_DATA, HKEY_CURRENT_CONFIG):
        return

    _RegCloseKey = windll.advapi32.RegCloseKey
    _RegCloseKey.argtypes = [HKEY]
    _RegCloseKey.restype = LONG
    _RegCloseKey.errcheck = RaiseIfNotErrorSuccess
    _RegCloseKey(hKey)


# LONG WINAPI RegConnectRegistry(
#   __in_opt  LPCTSTR lpMachineName,
#   __in      HKEY hKey,
#   __out     PHKEY phkResult
# );
def RegConnectRegistryA(lpMachineName=None, hKey=HKEY_LOCAL_MACHINE):
    _RegConnectRegistryA = windll.advapi32.RegConnectRegistryA
    _RegConnectRegistryA.argtypes = [LPSTR, HKEY, PHKEY]
    _RegConnectRegistryA.restype = LONG
    _RegConnectRegistryA.errcheck = RaiseIfNotErrorSuccess

    hkResult = HKEY(INVALID_HANDLE_VALUE)
    _RegConnectRegistryA(lpMachineName, hKey, byref(hkResult))
    return RegistryKeyHandle(hkResult.value)


def RegConnectRegistryW(lpMachineName=None, hKey=HKEY_LOCAL_MACHINE):
    _RegConnectRegistryW = windll.advapi32.RegConnectRegistryW
    _RegConnectRegistryW.argtypes = [LPWSTR, HKEY, PHKEY]
    _RegConnectRegistryW.restype = LONG
    _RegConnectRegistryW.errcheck = RaiseIfNotErrorSuccess

    hkResult = HKEY(INVALID_HANDLE_VALUE)
    _RegConnectRegistryW(lpMachineName, hKey, byref(hkResult))
    return RegistryKeyHandle(hkResult.value)


RegConnectRegistry = GuessStringType(RegConnectRegistryA, RegConnectRegistryW)


# LONG WINAPI RegCreateKey(
#   __in      HKEY hKey,
#   __in_opt  LPCTSTR lpSubKey,
#   __out     PHKEY phkResult
# );
def RegCreateKeyA(hKey=HKEY_LOCAL_MACHINE, lpSubKey=None):
    _RegCreateKeyA = windll.advapi32.RegCreateKeyA
    _RegCreateKeyA.argtypes = [HKEY, LPSTR, PHKEY]
    _RegCreateKeyA.restype = LONG
    _RegCreateKeyA.errcheck = RaiseIfNotErrorSuccess

    hkResult = HKEY(INVALID_HANDLE_VALUE)
    _RegCreateKeyA(hKey, lpSubKey, byref(hkResult))
    return RegistryKeyHandle(hkResult.value)


def RegCreateKeyW(hKey=HKEY_LOCAL_MACHINE, lpSubKey=None):
    _RegCreateKeyW = windll.advapi32.RegCreateKeyW
    _RegCreateKeyW.argtypes = [HKEY, LPWSTR, PHKEY]
    _RegCreateKeyW.restype = LONG
    _RegCreateKeyW.errcheck = RaiseIfNotErrorSuccess

    hkResult = HKEY(INVALID_HANDLE_VALUE)
    _RegCreateKeyW(hKey, lpSubKey, byref(hkResult))
    return RegistryKeyHandle(hkResult.value)


RegCreateKey = GuessStringType(RegCreateKeyA, RegCreateKeyW)

# LONG WINAPI RegCreateKeyEx(
#   __in        HKEY hKey,
#   __in        LPCTSTR lpSubKey,
#   __reserved  DWORD Reserved,
#   __in_opt    LPTSTR lpClass,
#   __in        DWORD dwOptions,
#   __in        REGSAM samDesired,
#   __in_opt    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
#   __out       PHKEY phkResult,
#   __out_opt   LPDWORD lpdwDisposition
# );

# XXX TODO


# LONG WINAPI RegOpenKey(
#   __in      HKEY hKey,
#   __in_opt  LPCTSTR lpSubKey,
#   __out     PHKEY phkResult
# );
def RegOpenKeyA(hKey=HKEY_LOCAL_MACHINE, lpSubKey=None):
    _RegOpenKeyA = windll.advapi32.RegOpenKeyA
    _RegOpenKeyA.argtypes = [HKEY, LPSTR, PHKEY]
    _RegOpenKeyA.restype = LONG
    _RegOpenKeyA.errcheck = RaiseIfNotErrorSuccess

    hkResult = HKEY(INVALID_HANDLE_VALUE)
    _RegOpenKeyA(hKey, lpSubKey, byref(hkResult))
    return RegistryKeyHandle(hkResult.value)


def RegOpenKeyW(hKey=HKEY_LOCAL_MACHINE, lpSubKey=None):
    _RegOpenKeyW = windll.advapi32.RegOpenKeyW
    _RegOpenKeyW.argtypes = [HKEY, LPWSTR, PHKEY]
    _RegOpenKeyW.restype = LONG
    _RegOpenKeyW.errcheck = RaiseIfNotErrorSuccess

    hkResult = HKEY(INVALID_HANDLE_VALUE)
    _RegOpenKeyW(hKey, lpSubKey, byref(hkResult))
    return RegistryKeyHandle(hkResult.value)


RegOpenKey = GuessStringType(RegOpenKeyA, RegOpenKeyW)


# LONG WINAPI RegOpenKeyEx(
#   __in        HKEY hKey,
#   __in_opt    LPCTSTR lpSubKey,
#   __reserved  DWORD ulOptions,
#   __in        REGSAM samDesired,
#   __out       PHKEY phkResult
# );
def RegOpenKeyExA(hKey=HKEY_LOCAL_MACHINE, lpSubKey=None, samDesired=KEY_ALL_ACCESS):
    _RegOpenKeyExA = windll.advapi32.RegOpenKeyExA
    _RegOpenKeyExA.argtypes = [HKEY, LPSTR, DWORD, REGSAM, PHKEY]
    _RegOpenKeyExA.restype = LONG
    _RegOpenKeyExA.errcheck = RaiseIfNotErrorSuccess

    hkResult = HKEY(INVALID_HANDLE_VALUE)
    _RegOpenKeyExA(hKey, lpSubKey, 0, samDesired, byref(hkResult))
    return RegistryKeyHandle(hkResult.value)


def RegOpenKeyExW(hKey=HKEY_LOCAL_MACHINE, lpSubKey=None, samDesired=KEY_ALL_ACCESS):
    _RegOpenKeyExW = windll.advapi32.RegOpenKeyExW
    _RegOpenKeyExW.argtypes = [HKEY, LPWSTR, DWORD, REGSAM, PHKEY]
    _RegOpenKeyExW.restype = LONG
    _RegOpenKeyExW.errcheck = RaiseIfNotErrorSuccess

    hkResult = HKEY(INVALID_HANDLE_VALUE)
    _RegOpenKeyExW(hKey, lpSubKey, 0, samDesired, byref(hkResult))
    return RegistryKeyHandle(hkResult.value)


RegOpenKeyEx = GuessStringType(RegOpenKeyExA, RegOpenKeyExW)


# LONG WINAPI RegOpenCurrentUser(
#   __in   REGSAM samDesired,
#   __out  PHKEY phkResult
# );
def RegOpenCurrentUser(samDesired=KEY_ALL_ACCESS):
    _RegOpenCurrentUser = windll.advapi32.RegOpenCurrentUser
    _RegOpenCurrentUser.argtypes = [REGSAM, PHKEY]
    _RegOpenCurrentUser.restype = LONG
    _RegOpenCurrentUser.errcheck = RaiseIfNotErrorSuccess

    hkResult = HKEY(INVALID_HANDLE_VALUE)
    _RegOpenCurrentUser(samDesired, byref(hkResult))
    return RegistryKeyHandle(hkResult.value)


# LONG WINAPI RegOpenUserClassesRoot(
#   __in        HANDLE hToken,
#   __reserved  DWORD dwOptions,
#   __in        REGSAM samDesired,
#   __out       PHKEY phkResult
# );
def RegOpenUserClassesRoot(hToken, samDesired=KEY_ALL_ACCESS):
    _RegOpenUserClassesRoot = windll.advapi32.RegOpenUserClassesRoot
    _RegOpenUserClassesRoot.argtypes = [HANDLE, DWORD, REGSAM, PHKEY]
    _RegOpenUserClassesRoot.restype = LONG
    _RegOpenUserClassesRoot.errcheck = RaiseIfNotErrorSuccess

    hkResult = HKEY(INVALID_HANDLE_VALUE)
    _RegOpenUserClassesRoot(hToken, 0, samDesired, byref(hkResult))
    return RegistryKeyHandle(hkResult.value)


# LONG WINAPI RegQueryValue(
#   __in         HKEY hKey,
#   __in_opt     LPCTSTR lpSubKey,
#   __out_opt    LPTSTR lpValue,
#   __inout_opt  PLONG lpcbValue
# );
def RegQueryValueA(hKey, lpSubKey=None):
    _RegQueryValueA = windll.advapi32.RegQueryValueA
    _RegQueryValueA.argtypes = [HKEY, LPSTR, LPVOID, PLONG]
    _RegQueryValueA.restype = LONG
    _RegQueryValueA.errcheck = RaiseIfNotErrorSuccess

    cbValue = LONG(0)
    _RegQueryValueA(hKey, lpSubKey, None, byref(cbValue))
    lpValue = ctypes.create_string_buffer(cbValue.value)
    _RegQueryValueA(hKey, lpSubKey, lpValue, byref(cbValue))
    return lpValue.value


def RegQueryValueW(hKey, lpSubKey=None):
    _RegQueryValueW = windll.advapi32.RegQueryValueW
    _RegQueryValueW.argtypes = [HKEY, LPWSTR, LPVOID, PLONG]
    _RegQueryValueW.restype = LONG
    _RegQueryValueW.errcheck = RaiseIfNotErrorSuccess

    cbValue = LONG(0)
    _RegQueryValueW(hKey, lpSubKey, None, byref(cbValue))
    lpValue = ctypes.create_unicode_buffer(cbValue.value * sizeof(WCHAR))
    _RegQueryValueW(hKey, lpSubKey, lpValue, byref(cbValue))
    return lpValue.value


RegQueryValue = GuessStringType(RegQueryValueA, RegQueryValueW)


# LONG WINAPI RegQueryValueEx(
#   __in         HKEY hKey,
#   __in_opt     LPCTSTR lpValueName,
#   __reserved   LPDWORD lpReserved,
#   __out_opt    LPDWORD lpType,
#   __out_opt    LPBYTE lpData,
#   __inout_opt  LPDWORD lpcbData
# );
def _internal_RegQueryValueEx(ansi, hKey, lpValueName=None, bGetData=True):
    _RegQueryValueEx = _caller_RegQueryValueEx(ansi)

    cbData = DWORD(0)
    dwType = DWORD(-1)
    _RegQueryValueEx(hKey, lpValueName, None, byref(dwType), None, byref(cbData))
    Type = dwType.value

    if not bGetData:
        return cbData.value, Type

    if Type in (REG_DWORD, REG_DWORD_BIG_ENDIAN):  # REG_DWORD_LITTLE_ENDIAN
        if cbData.value != 4:
            raise ValueError("REG_DWORD value of size %d" % cbData.value)
        dwData = DWORD(0)
        _RegQueryValueEx(hKey, lpValueName, None, None, byref(dwData), byref(cbData))
        return dwData.value, Type

    if Type == REG_QWORD:  # REG_QWORD_LITTLE_ENDIAN
        if cbData.value != 8:
            raise ValueError("REG_QWORD value of size %d" % cbData.value)
        qwData = QWORD(long(0))
        _RegQueryValueEx(hKey, lpValueName, None, None, byref(qwData), byref(cbData))
        return qwData.value, Type

    if Type in (REG_SZ, REG_EXPAND_SZ):
        if ansi:
            szData = ctypes.create_string_buffer(cbData.value)
        else:
            szData = ctypes.create_unicode_buffer(cbData.value)
        _RegQueryValueEx(hKey, lpValueName, None, None, byref(szData), byref(cbData))
        return szData.value, Type

    if Type == REG_MULTI_SZ:
        if ansi:
            szData = ctypes.create_string_buffer(cbData.value)
        else:
            szData = ctypes.create_unicode_buffer(cbData.value)
        _RegQueryValueEx(hKey, lpValueName, None, None, byref(szData), byref(cbData))
        Data = szData[:]
        if ansi:
            aData = Data.split("\0")
        else:
            aData = Data.split("\0")
        aData = [token for token in aData if token]
        return aData, Type

    if Type == REG_LINK:
        szData = ctypes.create_unicode_buffer(cbData.value)
        _RegQueryValueEx(hKey, lpValueName, None, None, byref(szData), byref(cbData))
        return szData.value, Type

    # REG_BINARY, REG_NONE, and any future types
    szData = ctypes.create_string_buffer(cbData.value)
    _RegQueryValueEx(hKey, lpValueName, None, None, byref(szData), byref(cbData))
    return szData.raw, Type


def _caller_RegQueryValueEx(ansi):
    if ansi:
        _RegQueryValueEx = windll.advapi32.RegQueryValueExA
        _RegQueryValueEx.argtypes = [HKEY, LPSTR, LPVOID, PDWORD, LPVOID, PDWORD]
    else:
        _RegQueryValueEx = windll.advapi32.RegQueryValueExW
        _RegQueryValueEx.argtypes = [HKEY, LPWSTR, LPVOID, PDWORD, LPVOID, PDWORD]
    _RegQueryValueEx.restype = LONG
    _RegQueryValueEx.errcheck = RaiseIfNotErrorSuccess
    return _RegQueryValueEx


# see _internal_RegQueryValueEx
def RegQueryValueExA(hKey, lpValueName=None, bGetData=True):
    return _internal_RegQueryValueEx(True, hKey, lpValueName, bGetData)


# see _internal_RegQueryValueEx
def RegQueryValueExW(hKey, lpValueName=None, bGetData=True):
    return _internal_RegQueryValueEx(False, hKey, lpValueName, bGetData)


RegQueryValueEx = GuessStringType(RegQueryValueExA, RegQueryValueExW)


# LONG WINAPI RegSetValueEx(
#   __in        HKEY hKey,
#   __in_opt    LPCTSTR lpValueName,
#   __reserved  DWORD Reserved,
#   __in        DWORD dwType,
#   __in_opt    const BYTE *lpData,
#   __in        DWORD cbData
# );
def RegSetValueEx(hKey, lpValueName=None, lpData=None, dwType=None):
    # Determine which version of the API to use, ANSI or Widechar.
    if lpValueName is None:
        if isinstance(lpData, GuessStringType.t_ansi):
            ansi = True
        elif isinstance(lpData, GuessStringType.t_unicode):
            ansi = False
        else:
            ansi = GuessStringType.t_ansi == GuessStringType.t_default
    elif isinstance(lpValueName, GuessStringType.t_ansi):
        ansi = True
    elif isinstance(lpValueName, GuessStringType.t_unicode):
        ansi = False
    else:
        raise TypeError("String expected, got %s instead" % type(lpValueName))

    # Autodetect the type when not given.
    # TODO: improve detection of DWORD and QWORD by seeing if the value "fits".
    if dwType is None:
        if lpValueName is None:
            dwType = REG_SZ
        elif lpData is None:
            dwType = REG_NONE
        elif isinstance(lpData, GuessStringType.t_ansi):
            dwType = REG_SZ
        elif isinstance(lpData, GuessStringType.t_unicode):
            dwType = REG_SZ
        elif isinstance(lpData, int):
            dwType = REG_DWORD
        elif isinstance(lpData, long):
            dwType = REG_QWORD
        else:
            dwType = REG_BINARY

    # Load the ctypes caller.
    if ansi:
        _RegSetValueEx = windll.advapi32.RegSetValueExA
        _RegSetValueEx.argtypes = [HKEY, LPSTR, DWORD, DWORD, LPVOID, DWORD]
    else:
        _RegSetValueEx = windll.advapi32.RegSetValueExW
        _RegSetValueEx.argtypes = [HKEY, LPWSTR, DWORD, DWORD, LPVOID, DWORD]
    _RegSetValueEx.restype = LONG
    _RegSetValueEx.errcheck = RaiseIfNotErrorSuccess

    # Convert the arguments so ctypes can understand them.
    if lpData is None:
        DataRef = None
        DataSize = 0
    else:
        if dwType in (REG_DWORD, REG_DWORD_BIG_ENDIAN):  # REG_DWORD_LITTLE_ENDIAN
            Data = DWORD(lpData)
        elif dwType == REG_QWORD:  # REG_QWORD_LITTLE_ENDIAN
            Data = QWORD(lpData)
        elif dwType in (REG_SZ, REG_EXPAND_SZ):
            if ansi:
                Data = ctypes.create_string_buffer(lpData)
            else:
                Data = ctypes.create_unicode_buffer(lpData)
        elif dwType == REG_MULTI_SZ:
            if ansi:
                Data = ctypes.create_string_buffer("\0".join(lpData) + "\0\0")
            else:
                Data = ctypes.create_unicode_buffer("\0".join(lpData) + "\0\0")
        elif dwType == REG_LINK:
            Data = ctypes.create_unicode_buffer(lpData)
        else:
            Data = ctypes.create_string_buffer(lpData)
        DataRef = byref(Data)
        DataSize = sizeof(Data)

    # Call the API with the converted arguments.
    _RegSetValueEx(hKey, lpValueName, 0, dwType, DataRef, DataSize)


# No "GuessStringType" here since detection is done inside.
RegSetValueExA = RegSetValueExW = RegSetValueEx


# LONG WINAPI RegEnumKey(
#   __in   HKEY hKey,
#   __in   DWORD dwIndex,
#   __out  LPTSTR lpName,
#   __in   DWORD cchName
# );
def RegEnumKeyA(hKey, dwIndex):
    _RegEnumKeyA = windll.advapi32.RegEnumKeyA
    _RegEnumKeyA.argtypes = [HKEY, DWORD, LPSTR, DWORD]
    _RegEnumKeyA.restype = LONG

    cchName = 1024
    while True:
        lpName = ctypes.create_string_buffer(cchName)
        errcode = _RegEnumKeyA(hKey, dwIndex, lpName, cchName)
        if errcode != ERROR_MORE_DATA:
            break
        cchName = cchName + 1024
        if cchName > 65536:
            raise ctypes.WinError(errcode)
    if errcode == ERROR_NO_MORE_ITEMS:
        return None
    if errcode != ERROR_SUCCESS:
        raise ctypes.WinError(errcode)
    return lpName.value


def RegEnumKeyW(hKey, dwIndex):
    _RegEnumKeyW = windll.advapi32.RegEnumKeyW
    _RegEnumKeyW.argtypes = [HKEY, DWORD, LPWSTR, DWORD]
    _RegEnumKeyW.restype = LONG

    cchName = 512
    while True:
        lpName = ctypes.create_unicode_buffer(cchName)
        errcode = _RegEnumKeyW(hKey, dwIndex, lpName, cchName * 2)
        if errcode != ERROR_MORE_DATA:
            break
        cchName = cchName + 512
        if cchName > 32768:
            raise ctypes.WinError(errcode)
    if errcode == ERROR_NO_MORE_ITEMS:
        return None
    if errcode != ERROR_SUCCESS:
        raise ctypes.WinError(errcode)
    return lpName.value


RegEnumKey = DefaultStringType(RegEnumKeyA, RegEnumKeyW)

# LONG WINAPI RegEnumKeyEx(
#   __in         HKEY hKey,
#   __in         DWORD dwIndex,
#   __out        LPTSTR lpName,
#   __inout      LPDWORD lpcName,
#   __reserved   LPDWORD lpReserved,
#   __inout      LPTSTR lpClass,
#   __inout_opt  LPDWORD lpcClass,
#   __out_opt    PFILETIME lpftLastWriteTime
# );

# XXX TODO


# LONG WINAPI RegEnumValue(
#   __in         HKEY hKey,
#   __in         DWORD dwIndex,
#   __out        LPTSTR lpValueName,
#   __inout      LPDWORD lpcchValueName,
#   __reserved   LPDWORD lpReserved,
#   __out_opt    LPDWORD lpType,
#   __out_opt    LPBYTE lpData,
#   __inout_opt  LPDWORD lpcbData
# );
def _internal_RegEnumValue(ansi, hKey, dwIndex, bGetData=True):
    if ansi:
        _RegEnumValue = windll.advapi32.RegEnumValueA
        _RegEnumValue.argtypes = [HKEY, DWORD, LPSTR, LPDWORD, LPVOID, LPDWORD, LPVOID, LPDWORD]
    else:
        _RegEnumValue = windll.advapi32.RegEnumValueW
        _RegEnumValue.argtypes = [HKEY, DWORD, LPWSTR, LPDWORD, LPVOID, LPDWORD, LPVOID, LPDWORD]
    _RegEnumValue.restype = LONG

    cchValueName = DWORD(1024)
    dwType = DWORD(-1)
    lpcchValueName = byref(cchValueName)
    lpType = byref(dwType)
    if ansi:
        lpValueName = ctypes.create_string_buffer(cchValueName.value)
    else:
        lpValueName = ctypes.create_unicode_buffer(cchValueName.value)
    if bGetData:
        cbData = DWORD(0)
        lpcbData = byref(cbData)
    else:
        lpcbData = None
    lpData = None
    errcode = _RegEnumValue(hKey, dwIndex, lpValueName, lpcchValueName, None, lpType, lpData, lpcbData)

    if errcode == ERROR_MORE_DATA or (bGetData and errcode == ERROR_SUCCESS):
        if ansi:
            cchValueName.value = cchValueName.value + sizeof(CHAR)
            lpValueName = ctypes.create_string_buffer(cchValueName.value)
        else:
            cchValueName.value = cchValueName.value + sizeof(WCHAR)
            lpValueName = ctypes.create_unicode_buffer(cchValueName.value)

        if bGetData:
            Type = dwType.value

            if Type in (REG_DWORD, REG_DWORD_BIG_ENDIAN):  # REG_DWORD_LITTLE_ENDIAN
                if cbData.value != sizeof(DWORD):
                    raise ValueError("REG_DWORD value of size %d" % cbData.value)
                Data = DWORD(0)

            elif Type == REG_QWORD:  # REG_QWORD_LITTLE_ENDIAN
                if cbData.value != sizeof(QWORD):
                    raise ValueError("REG_QWORD value of size %d" % cbData.value)
                Data = QWORD(long(0))

            elif Type in (REG_SZ, REG_EXPAND_SZ, REG_MULTI_SZ):
                if ansi:
                    Data = ctypes.create_string_buffer(cbData.value)
                else:
                    Data = ctypes.create_unicode_buffer(cbData.value)

            elif Type == REG_LINK:
                Data = ctypes.create_unicode_buffer(cbData.value)

            else:  # REG_BINARY, REG_NONE, and any future types
                Data = ctypes.create_string_buffer(cbData.value)

            lpData = byref(Data)

        errcode = _RegEnumValue(hKey, dwIndex, lpValueName, lpcchValueName, None, lpType, lpData, lpcbData)

    if errcode == ERROR_NO_MORE_ITEMS:
        return None
    # if errcode  != ERROR_SUCCESS:
    #    raise ctypes.WinError(errcode)

    if not bGetData:
        return lpValueName.value, dwType.value

    if Type in (
        REG_DWORD,
        REG_DWORD_BIG_ENDIAN,
        REG_QWORD,
        REG_SZ,
        REG_EXPAND_SZ,
        REG_LINK,
    ):  # REG_DWORD_LITTLE_ENDIAN, REG_QWORD_LITTLE_ENDIAN
        return lpValueName.value, dwType.value, Data.value

    if Type == REG_MULTI_SZ:
        sData = Data[:]
        del Data
        if ansi:
            aData = sData.split("\0")
        else:
            aData = sData.split("\0")
        aData = [token for token in aData if token]
        return lpValueName.value, dwType.value, aData

    # REG_BINARY, REG_NONE, and any future types
    return lpValueName.value, dwType.value, Data.raw


def RegEnumValueA(hKey, dwIndex, bGetData=True):
    return _internal_RegEnumValue(True, hKey, dwIndex, bGetData)


def RegEnumValueW(hKey, dwIndex, bGetData=True):
    return _internal_RegEnumValue(False, hKey, dwIndex, bGetData)


RegEnumValue = DefaultStringType(RegEnumValueA, RegEnumValueW)

# XXX TODO

# LONG WINAPI RegSetKeyValue(
#   __in      HKEY hKey,
#   __in_opt  LPCTSTR lpSubKey,
#   __in_opt  LPCTSTR lpValueName,
#   __in      DWORD dwType,
#   __in_opt  LPCVOID lpData,
#   __in      DWORD cbData
# );

# XXX TODO

# LONG WINAPI RegQueryMultipleValues(
#   __in         HKEY hKey,
#   __out        PVALENT val_list,
#   __in         DWORD num_vals,
#   __out_opt    LPTSTR lpValueBuf,
#   __inout_opt  LPDWORD ldwTotsize
# );

# XXX TODO


# LONG WINAPI RegDeleteValue(
#   __in      HKEY hKey,
#   __in_opt  LPCTSTR lpValueName
# );
def RegDeleteValueA(hKeySrc, lpValueName=None):
    _RegDeleteValueA = windll.advapi32.RegDeleteValueA
    _RegDeleteValueA.argtypes = [HKEY, LPSTR]
    _RegDeleteValueA.restype = LONG
    _RegDeleteValueA.errcheck = RaiseIfNotErrorSuccess
    _RegDeleteValueA(hKeySrc, lpValueName)


def RegDeleteValueW(hKeySrc, lpValueName=None):
    _RegDeleteValueW = windll.advapi32.RegDeleteValueW
    _RegDeleteValueW.argtypes = [HKEY, LPWSTR]
    _RegDeleteValueW.restype = LONG
    _RegDeleteValueW.errcheck = RaiseIfNotErrorSuccess
    _RegDeleteValueW(hKeySrc, lpValueName)


RegDeleteValue = GuessStringType(RegDeleteValueA, RegDeleteValueW)


# LONG WINAPI RegDeleteKeyValue(
#   __in      HKEY hKey,
#   __in_opt  LPCTSTR lpSubKey,
#   __in_opt  LPCTSTR lpValueName
# );
def RegDeleteKeyValueA(hKeySrc, lpSubKey=None, lpValueName=None):
    _RegDeleteKeyValueA = windll.advapi32.RegDeleteKeyValueA
    _RegDeleteKeyValueA.argtypes = [HKEY, LPSTR, LPSTR]
    _RegDeleteKeyValueA.restype = LONG
    _RegDeleteKeyValueA.errcheck = RaiseIfNotErrorSuccess
    _RegDeleteKeyValueA(hKeySrc, lpSubKey, lpValueName)


def RegDeleteKeyValueW(hKeySrc, lpSubKey=None, lpValueName=None):
    _RegDeleteKeyValueW = windll.advapi32.RegDeleteKeyValueW
    _RegDeleteKeyValueW.argtypes = [HKEY, LPWSTR, LPWSTR]
    _RegDeleteKeyValueW.restype = LONG
    _RegDeleteKeyValueW.errcheck = RaiseIfNotErrorSuccess
    _RegDeleteKeyValueW(hKeySrc, lpSubKey, lpValueName)


RegDeleteKeyValue = GuessStringType(RegDeleteKeyValueA, RegDeleteKeyValueW)


# LONG WINAPI RegDeleteKey(
#   __in  HKEY hKey,
#   __in  LPCTSTR lpSubKey
# );
def RegDeleteKeyA(hKeySrc, lpSubKey=None):
    _RegDeleteKeyA = windll.advapi32.RegDeleteKeyA
    _RegDeleteKeyA.argtypes = [HKEY, LPSTR]
    _RegDeleteKeyA.restype = LONG
    _RegDeleteKeyA.errcheck = RaiseIfNotErrorSuccess
    _RegDeleteKeyA(hKeySrc, lpSubKey)


def RegDeleteKeyW(hKeySrc, lpSubKey=None):
    _RegDeleteKeyW = windll.advapi32.RegDeleteKeyW
    _RegDeleteKeyW.argtypes = [HKEY, LPWSTR]
    _RegDeleteKeyW.restype = LONG
    _RegDeleteKeyW.errcheck = RaiseIfNotErrorSuccess
    _RegDeleteKeyW(hKeySrc, lpSubKey)


RegDeleteKey = GuessStringType(RegDeleteKeyA, RegDeleteKeyW)

# LONG WINAPI RegDeleteKeyEx(
#   __in        HKEY hKey,
#   __in        LPCTSTR lpSubKey,
#   __in        REGSAM samDesired,
#   __reserved  DWORD Reserved
# );


def RegDeleteKeyExA(hKeySrc, lpSubKey=None, samDesired=KEY_WOW64_32KEY):
    _RegDeleteKeyExA = windll.advapi32.RegDeleteKeyExA
    _RegDeleteKeyExA.argtypes = [HKEY, LPSTR, REGSAM, DWORD]
    _RegDeleteKeyExA.restype = LONG
    _RegDeleteKeyExA.errcheck = RaiseIfNotErrorSuccess
    _RegDeleteKeyExA(hKeySrc, lpSubKey, samDesired, 0)


def RegDeleteKeyExW(hKeySrc, lpSubKey=None, samDesired=KEY_WOW64_32KEY):
    _RegDeleteKeyExW = windll.advapi32.RegDeleteKeyExW
    _RegDeleteKeyExW.argtypes = [HKEY, LPWSTR, REGSAM, DWORD]
    _RegDeleteKeyExW.restype = LONG
    _RegDeleteKeyExW.errcheck = RaiseIfNotErrorSuccess
    _RegDeleteKeyExW(hKeySrc, lpSubKey, samDesired, 0)


RegDeleteKeyEx = GuessStringType(RegDeleteKeyExA, RegDeleteKeyExW)


# LONG WINAPI RegCopyTree(
#   __in      HKEY hKeySrc,
#   __in_opt  LPCTSTR lpSubKey,
#   __in      HKEY hKeyDest
# );
def RegCopyTreeA(hKeySrc, lpSubKey, hKeyDest):
    _RegCopyTreeA = windll.advapi32.RegCopyTreeA
    _RegCopyTreeA.argtypes = [HKEY, LPSTR, HKEY]
    _RegCopyTreeA.restype = LONG
    _RegCopyTreeA.errcheck = RaiseIfNotErrorSuccess
    _RegCopyTreeA(hKeySrc, lpSubKey, hKeyDest)


def RegCopyTreeW(hKeySrc, lpSubKey, hKeyDest):
    _RegCopyTreeW = windll.advapi32.RegCopyTreeW
    _RegCopyTreeW.argtypes = [HKEY, LPWSTR, HKEY]
    _RegCopyTreeW.restype = LONG
    _RegCopyTreeW.errcheck = RaiseIfNotErrorSuccess
    _RegCopyTreeW(hKeySrc, lpSubKey, hKeyDest)


RegCopyTree = GuessStringType(RegCopyTreeA, RegCopyTreeW)


# LONG WINAPI RegDeleteTree(
#   __in      HKEY hKey,
#   __in_opt  LPCTSTR lpSubKey
# );
def RegDeleteTreeA(hKey, lpSubKey=None):
    _RegDeleteTreeA = windll.advapi32.RegDeleteTreeA
    _RegDeleteTreeA.argtypes = [HKEY, LPWSTR]
    _RegDeleteTreeA.restype = LONG
    _RegDeleteTreeA.errcheck = RaiseIfNotErrorSuccess
    _RegDeleteTreeA(hKey, lpSubKey)


def RegDeleteTreeW(hKey, lpSubKey=None):
    _RegDeleteTreeW = windll.advapi32.RegDeleteTreeW
    _RegDeleteTreeW.argtypes = [HKEY, LPWSTR]
    _RegDeleteTreeW.restype = LONG
    _RegDeleteTreeW.errcheck = RaiseIfNotErrorSuccess
    _RegDeleteTreeW(hKey, lpSubKey)


RegDeleteTree = GuessStringType(RegDeleteTreeA, RegDeleteTreeW)


# LONG WINAPI RegFlushKey(
#   __in  HKEY hKey
# );
def RegFlushKey(hKey):
    _RegFlushKey = windll.advapi32.RegFlushKey
    _RegFlushKey.argtypes = [HKEY]
    _RegFlushKey.restype = LONG
    _RegFlushKey.errcheck = RaiseIfNotErrorSuccess
    _RegFlushKey(hKey)


# LONG WINAPI RegLoadMUIString(
#   _In_       HKEY hKey,
#   _In_opt_   LPCTSTR pszValue,
#   _Out_opt_  LPTSTR pszOutBuf,
#   _In_       DWORD cbOutBuf,
#   _Out_opt_  LPDWORD pcbData,
#   _In_       DWORD Flags,
#   _In_opt_   LPCTSTR pszDirectory
# );

# TO DO

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


# BOOL WINAPI CloseServiceHandle(
#   _In_  SC_HANDLE hSCObject
# );
def CloseServiceHandle(hSCObject):
    _CloseServiceHandle = windll.advapi32.CloseServiceHandle
    _CloseServiceHandle.argtypes = [SC_HANDLE]
    _CloseServiceHandle.restype = bool
    _CloseServiceHandle.errcheck = RaiseIfZero

    if isinstance(hSCObject, Handle):
        # Prevents the handle from being closed without notifying the Handle object.
        hSCObject.close()
    else:
        _CloseServiceHandle(hSCObject)


# SC_HANDLE WINAPI OpenSCManager(
#   _In_opt_  LPCTSTR lpMachineName,
#   _In_opt_  LPCTSTR lpDatabaseName,
#   _In_      DWORD dwDesiredAccess
# );
def OpenSCManagerA(lpMachineName=None, lpDatabaseName=None, dwDesiredAccess=SC_MANAGER_ALL_ACCESS):
    _OpenSCManagerA = windll.advapi32.OpenSCManagerA
    _OpenSCManagerA.argtypes = [LPSTR, LPSTR, DWORD]
    _OpenSCManagerA.restype = SC_HANDLE
    _OpenSCManagerA.errcheck = RaiseIfZero

    hSCObject = _OpenSCManagerA(lpMachineName, lpDatabaseName, dwDesiredAccess)
    return ServiceControlManagerHandle(hSCObject)


def OpenSCManagerW(lpMachineName=None, lpDatabaseName=None, dwDesiredAccess=SC_MANAGER_ALL_ACCESS):
    _OpenSCManagerW = windll.advapi32.OpenSCManagerW
    _OpenSCManagerW.argtypes = [LPWSTR, LPWSTR, DWORD]
    _OpenSCManagerW.restype = SC_HANDLE
    _OpenSCManagerW.errcheck = RaiseIfZero

    hSCObject = _OpenSCManagerA(lpMachineName, lpDatabaseName, dwDesiredAccess)
    return ServiceControlManagerHandle(hSCObject)


OpenSCManager = GuessStringType(OpenSCManagerA, OpenSCManagerW)


# SC_HANDLE WINAPI OpenService(
#   _In_  SC_HANDLE hSCManager,
#   _In_  LPCTSTR lpServiceName,
#   _In_  DWORD dwDesiredAccess
# );
def OpenServiceA(hSCManager, lpServiceName, dwDesiredAccess=SERVICE_ALL_ACCESS):
    _OpenServiceA = windll.advapi32.OpenServiceA
    _OpenServiceA.argtypes = [SC_HANDLE, LPSTR, DWORD]
    _OpenServiceA.restype = SC_HANDLE
    _OpenServiceA.errcheck = RaiseIfZero
    return ServiceHandle(_OpenServiceA(hSCManager, lpServiceName, dwDesiredAccess))


def OpenServiceW(hSCManager, lpServiceName, dwDesiredAccess=SERVICE_ALL_ACCESS):
    _OpenServiceW = windll.advapi32.OpenServiceW
    _OpenServiceW.argtypes = [SC_HANDLE, LPWSTR, DWORD]
    _OpenServiceW.restype = SC_HANDLE
    _OpenServiceW.errcheck = RaiseIfZero
    return ServiceHandle(_OpenServiceW(hSCManager, lpServiceName, dwDesiredAccess))


OpenService = GuessStringType(OpenServiceA, OpenServiceW)


# SC_HANDLE WINAPI CreateService(
#   _In_       SC_HANDLE hSCManager,
#   _In_       LPCTSTR lpServiceName,
#   _In_opt_   LPCTSTR lpDisplayName,
#   _In_       DWORD dwDesiredAccess,
#   _In_       DWORD dwServiceType,
#   _In_       DWORD dwStartType,
#   _In_       DWORD dwErrorControl,
#   _In_opt_   LPCTSTR lpBinaryPathName,
#   _In_opt_   LPCTSTR lpLoadOrderGroup,
#   _Out_opt_  LPDWORD lpdwTagId,
#   _In_opt_   LPCTSTR lpDependencies,
#   _In_opt_   LPCTSTR lpServiceStartName,
#   _In_opt_   LPCTSTR lpPassword
# );
def CreateServiceA(
    hSCManager,
    lpServiceName,
    lpDisplayName=None,
    dwDesiredAccess=SERVICE_ALL_ACCESS,
    dwServiceType=SERVICE_WIN32_OWN_PROCESS,
    dwStartType=SERVICE_DEMAND_START,
    dwErrorControl=SERVICE_ERROR_NORMAL,
    lpBinaryPathName=None,
    lpLoadOrderGroup=None,
    lpDependencies=None,
    lpServiceStartName=None,
    lpPassword=None,
):
    _CreateServiceA = windll.advapi32.CreateServiceA
    _CreateServiceA.argtypes = [SC_HANDLE, LPSTR, LPSTR, DWORD, DWORD, DWORD, DWORD, LPSTR, LPSTR, LPDWORD, LPSTR, LPSTR, LPSTR]
    _CreateServiceA.restype = SC_HANDLE
    _CreateServiceA.errcheck = RaiseIfZero

    dwTagId = DWORD(0)
    hService = _CreateServiceA(
        hSCManager,
        lpServiceName,
        dwDesiredAccess,
        dwServiceType,
        dwStartType,
        dwErrorControl,
        lpBinaryPathName,
        lpLoadOrderGroup,
        byref(dwTagId),
        lpDependencies,
        lpServiceStartName,
        lpPassword,
    )
    return ServiceHandle(hService), dwTagId.value


def CreateServiceW(
    hSCManager,
    lpServiceName,
    lpDisplayName=None,
    dwDesiredAccess=SERVICE_ALL_ACCESS,
    dwServiceType=SERVICE_WIN32_OWN_PROCESS,
    dwStartType=SERVICE_DEMAND_START,
    dwErrorControl=SERVICE_ERROR_NORMAL,
    lpBinaryPathName=None,
    lpLoadOrderGroup=None,
    lpDependencies=None,
    lpServiceStartName=None,
    lpPassword=None,
):
    _CreateServiceW = windll.advapi32.CreateServiceW
    _CreateServiceW.argtypes = [SC_HANDLE, LPWSTR, LPWSTR, DWORD, DWORD, DWORD, DWORD, LPWSTR, LPWSTR, LPDWORD, LPWSTR, LPWSTR, LPWSTR]
    _CreateServiceW.restype = SC_HANDLE
    _CreateServiceW.errcheck = RaiseIfZero

    dwTagId = DWORD(0)
    hService = _CreateServiceW(
        hSCManager,
        lpServiceName,
        dwDesiredAccess,
        dwServiceType,
        dwStartType,
        dwErrorControl,
        lpBinaryPathName,
        lpLoadOrderGroup,
        byref(dwTagId),
        lpDependencies,
        lpServiceStartName,
        lpPassword,
    )
    return ServiceHandle(hService), dwTagId.value


CreateService = GuessStringType(CreateServiceA, CreateServiceW)


# BOOL WINAPI DeleteService(
#   _In_  SC_HANDLE hService
# );
def DeleteService(hService):
    _DeleteService = windll.advapi32.DeleteService
    _DeleteService.argtypes = [SC_HANDLE]
    _DeleteService.restype = bool
    _DeleteService.errcheck = RaiseIfZero
    _DeleteService(hService)


# BOOL WINAPI GetServiceKeyName(
#   _In_       SC_HANDLE hSCManager,
#   _In_       LPCTSTR lpDisplayName,
#   _Out_opt_  LPTSTR lpServiceName,
#   _Inout_    LPDWORD lpcchBuffer
# );
def GetServiceKeyNameA(hSCManager, lpDisplayName):
    _GetServiceKeyNameA = windll.advapi32.GetServiceKeyNameA
    _GetServiceKeyNameA.argtypes = [SC_HANDLE, LPSTR, LPSTR, LPDWORD]
    _GetServiceKeyNameA.restype = bool

    cchBuffer = DWORD(0)
    _GetServiceKeyNameA(hSCManager, lpDisplayName, None, byref(cchBuffer))
    if cchBuffer.value == 0:
        raise ctypes.WinError()
    lpServiceName = ctypes.create_string_buffer(cchBuffer.value + 1)
    cchBuffer.value = sizeof(lpServiceName)
    success = _GetServiceKeyNameA(hSCManager, lpDisplayName, lpServiceName, byref(cchBuffer))
    if not success:
        raise ctypes.WinError()
    return lpServiceName.value


def GetServiceKeyNameW(hSCManager, lpDisplayName):
    _GetServiceKeyNameW = windll.advapi32.GetServiceKeyNameW
    _GetServiceKeyNameW.argtypes = [SC_HANDLE, LPWSTR, LPWSTR, LPDWORD]
    _GetServiceKeyNameW.restype = bool

    cchBuffer = DWORD(0)
    _GetServiceKeyNameW(hSCManager, lpDisplayName, None, byref(cchBuffer))
    if cchBuffer.value == 0:
        raise ctypes.WinError()
    lpServiceName = ctypes.create_unicode_buffer(cchBuffer.value + 2)
    cchBuffer.value = sizeof(lpServiceName)
    success = _GetServiceKeyNameW(hSCManager, lpDisplayName, lpServiceName, byref(cchBuffer))
    if not success:
        raise ctypes.WinError()
    return lpServiceName.value


GetServiceKeyName = GuessStringType(GetServiceKeyNameA, GetServiceKeyNameW)


# BOOL WINAPI GetServiceDisplayName(
#   _In_       SC_HANDLE hSCManager,
#   _In_       LPCTSTR lpServiceName,
#   _Out_opt_  LPTSTR lpDisplayName,
#   _Inout_    LPDWORD lpcchBuffer
# );
def GetServiceDisplayNameA(hSCManager, lpServiceName):
    _GetServiceDisplayNameA = windll.advapi32.GetServiceDisplayNameA
    _GetServiceDisplayNameA.argtypes = [SC_HANDLE, LPSTR, LPSTR, LPDWORD]
    _GetServiceDisplayNameA.restype = bool

    cchBuffer = DWORD(0)
    _GetServiceDisplayNameA(hSCManager, lpServiceName, None, byref(cchBuffer))
    if cchBuffer.value == 0:
        raise ctypes.WinError()
    lpDisplayName = ctypes.create_string_buffer(cchBuffer.value + 1)
    cchBuffer.value = sizeof(lpDisplayName)
    success = _GetServiceDisplayNameA(hSCManager, lpServiceName, lpDisplayName, byref(cchBuffer))
    if not success:
        raise ctypes.WinError()
    return lpDisplayName.value


def GetServiceDisplayNameW(hSCManager, lpServiceName):
    _GetServiceDisplayNameW = windll.advapi32.GetServiceDisplayNameW
    _GetServiceDisplayNameW.argtypes = [SC_HANDLE, LPWSTR, LPWSTR, LPDWORD]
    _GetServiceDisplayNameW.restype = bool

    cchBuffer = DWORD(0)
    _GetServiceDisplayNameW(hSCManager, lpServiceName, None, byref(cchBuffer))
    if cchBuffer.value == 0:
        raise ctypes.WinError()
    lpDisplayName = ctypes.create_unicode_buffer(cchBuffer.value + 2)
    cchBuffer.value = sizeof(lpDisplayName)
    success = _GetServiceDisplayNameW(hSCManager, lpServiceName, lpDisplayName, byref(cchBuffer))
    if not success:
        raise ctypes.WinError()
    return lpDisplayName.value


GetServiceDisplayName = GuessStringType(GetServiceDisplayNameA, GetServiceDisplayNameW)

# BOOL WINAPI QueryServiceConfig(
#   _In_       SC_HANDLE hService,
#   _Out_opt_  LPQUERY_SERVICE_CONFIG lpServiceConfig,
#   _In_       DWORD cbBufSize,
#   _Out_      LPDWORD pcbBytesNeeded
# );

# TO DO

# BOOL WINAPI QueryServiceConfig2(
#   _In_       SC_HANDLE hService,
#   _In_       DWORD dwInfoLevel,
#   _Out_opt_  LPBYTE lpBuffer,
#   _In_       DWORD cbBufSize,
#   _Out_      LPDWORD pcbBytesNeeded
# );

# TO DO

# BOOL WINAPI ChangeServiceConfig(
#   _In_       SC_HANDLE hService,
#   _In_       DWORD dwServiceType,
#   _In_       DWORD dwStartType,
#   _In_       DWORD dwErrorControl,
#   _In_opt_   LPCTSTR lpBinaryPathName,
#   _In_opt_   LPCTSTR lpLoadOrderGroup,
#   _Out_opt_  LPDWORD lpdwTagId,
#   _In_opt_   LPCTSTR lpDependencies,
#   _In_opt_   LPCTSTR lpServiceStartName,
#   _In_opt_   LPCTSTR lpPassword,
#   _In_opt_   LPCTSTR lpDisplayName
# );

# TO DO

# BOOL WINAPI ChangeServiceConfig2(
#   _In_      SC_HANDLE hService,
#   _In_      DWORD dwInfoLevel,
#   _In_opt_  LPVOID lpInfo
# );

# TO DO


# BOOL WINAPI StartService(
#   _In_      SC_HANDLE hService,
#   _In_      DWORD dwNumServiceArgs,
#   _In_opt_  LPCTSTR *lpServiceArgVectors
# );
def StartServiceA(hService, ServiceArgVectors=None):
    _StartServiceA = windll.advapi32.StartServiceA
    _StartServiceA.argtypes = [SC_HANDLE, DWORD, LPVOID]
    _StartServiceA.restype = bool
    _StartServiceA.errcheck = RaiseIfZero

    if ServiceArgVectors:
        dwNumServiceArgs = len(ServiceArgVectors)
        CServiceArgVectors = (LPSTR * dwNumServiceArgs)(*ServiceArgVectors)
        lpServiceArgVectors = ctypes.pointer(CServiceArgVectors)
    else:
        dwNumServiceArgs = 0
        lpServiceArgVectors = None
    _StartServiceA(hService, dwNumServiceArgs, lpServiceArgVectors)


def StartServiceW(hService, ServiceArgVectors=None):
    _StartServiceW = windll.advapi32.StartServiceW
    _StartServiceW.argtypes = [SC_HANDLE, DWORD, LPVOID]
    _StartServiceW.restype = bool
    _StartServiceW.errcheck = RaiseIfZero

    if ServiceArgVectors:
        dwNumServiceArgs = len(ServiceArgVectors)
        CServiceArgVectors = (LPWSTR * dwNumServiceArgs)(*ServiceArgVectors)
        lpServiceArgVectors = ctypes.pointer(CServiceArgVectors)
    else:
        dwNumServiceArgs = 0
        lpServiceArgVectors = None
    _StartServiceW(hService, dwNumServiceArgs, lpServiceArgVectors)


StartService = GuessStringType(StartServiceA, StartServiceW)


# BOOL WINAPI ControlService(
#   _In_   SC_HANDLE hService,
#   _In_   DWORD dwControl,
#   _Out_  LPSERVICE_STATUS lpServiceStatus
# );
def ControlService(hService, dwControl):
    _ControlService = windll.advapi32.ControlService
    _ControlService.argtypes = [SC_HANDLE, DWORD, LPSERVICE_STATUS]
    _ControlService.restype = bool
    _ControlService.errcheck = RaiseIfZero

    rawServiceStatus = SERVICE_STATUS()
    _ControlService(hService, dwControl, byref(rawServiceStatus))
    return ServiceStatus(rawServiceStatus)


# BOOL WINAPI ControlServiceEx(
#   _In_     SC_HANDLE hService,
#   _In_     DWORD dwControl,
#   _In_     DWORD dwInfoLevel,
#   _Inout_  PVOID pControlParams
# );

# TO DO

# DWORD WINAPI NotifyServiceStatusChange(
#   _In_  SC_HANDLE hService,
#   _In_  DWORD dwNotifyMask,
#   _In_  PSERVICE_NOTIFY pNotifyBuffer
# );

# TO DO


# BOOL WINAPI QueryServiceStatus(
#   _In_   SC_HANDLE hService,
#   _Out_  LPSERVICE_STATUS lpServiceStatus
# );
def QueryServiceStatus(hService):
    _QueryServiceStatus = windll.advapi32.QueryServiceStatus
    _QueryServiceStatus.argtypes = [SC_HANDLE, LPSERVICE_STATUS]
    _QueryServiceStatus.restype = bool
    _QueryServiceStatus.errcheck = RaiseIfZero

    rawServiceStatus = SERVICE_STATUS()
    _QueryServiceStatus(hService, byref(rawServiceStatus))
    return ServiceStatus(rawServiceStatus)


# BOOL WINAPI QueryServiceStatusEx(
#   _In_       SC_HANDLE hService,
#   _In_       SC_STATUS_TYPE InfoLevel,
#   _Out_opt_  LPBYTE lpBuffer,
#   _In_       DWORD cbBufSize,
#   _Out_      LPDWORD pcbBytesNeeded
# );
def QueryServiceStatusEx(hService, InfoLevel=SC_STATUS_PROCESS_INFO):
    if InfoLevel != SC_STATUS_PROCESS_INFO:
        raise NotImplementedError()

    _QueryServiceStatusEx = windll.advapi32.QueryServiceStatusEx
    _QueryServiceStatusEx.argtypes = [SC_HANDLE, SC_STATUS_TYPE, LPVOID, DWORD, LPDWORD]
    _QueryServiceStatusEx.restype = bool
    _QueryServiceStatusEx.errcheck = RaiseIfZero

    lpBuffer = SERVICE_STATUS_PROCESS()
    cbBytesNeeded = DWORD(sizeof(lpBuffer))
    _QueryServiceStatusEx(hService, InfoLevel, byref(lpBuffer), sizeof(lpBuffer), byref(cbBytesNeeded))
    return ServiceStatusProcess(lpBuffer)


# BOOL WINAPI EnumServicesStatus(
#   _In_         SC_HANDLE hSCManager,
#   _In_         DWORD dwServiceType,
#   _In_         DWORD dwServiceState,
#   _Out_opt_    LPENUM_SERVICE_STATUS lpServices,
#   _In_         DWORD cbBufSize,
#   _Out_        LPDWORD pcbBytesNeeded,
#   _Out_        LPDWORD lpServicesReturned,
#   _Inout_opt_  LPDWORD lpResumeHandle
# );
def EnumServicesStatusA(hSCManager, dwServiceType=SERVICE_DRIVER | SERVICE_WIN32, dwServiceState=SERVICE_STATE_ALL):
    _EnumServicesStatusA = windll.advapi32.EnumServicesStatusA
    _EnumServicesStatusA.argtypes = [SC_HANDLE, DWORD, DWORD, LPVOID, DWORD, LPDWORD, LPDWORD, LPDWORD]
    _EnumServicesStatusA.restype = bool

    cbBytesNeeded = DWORD(0)
    ServicesReturned = DWORD(0)
    ResumeHandle = DWORD(0)

    _EnumServicesStatusA(
        hSCManager, dwServiceType, dwServiceState, None, 0, byref(cbBytesNeeded), byref(ServicesReturned), byref(ResumeHandle)
    )

    Services = []
    success = False
    while GetLastError() == ERROR_MORE_DATA:
        if cbBytesNeeded.value < sizeof(ENUM_SERVICE_STATUSA):
            break
        ServicesBuffer = ctypes.create_string_buffer("", cbBytesNeeded.value)
        success = _EnumServicesStatusA(
            hSCManager,
            dwServiceType,
            dwServiceState,
            byref(ServicesBuffer),
            sizeof(ServicesBuffer),
            byref(cbBytesNeeded),
            byref(ServicesReturned),
            byref(ResumeHandle),
        )
        if sizeof(ServicesBuffer) < (sizeof(ENUM_SERVICE_STATUSA) * ServicesReturned.value):
            raise ctypes.WinError()
        lpServicesArray = ctypes.cast(ctypes.cast(ctypes.pointer(ServicesBuffer), ctypes.c_void_p), LPENUM_SERVICE_STATUSA)
        for index in compat.xrange(0, ServicesReturned.value):
            Services.append(ServiceStatusEntry(lpServicesArray[index]))
        if success:
            break
    if not success:
        raise ctypes.WinError()

    return Services


def EnumServicesStatusW(hSCManager, dwServiceType=SERVICE_DRIVER | SERVICE_WIN32, dwServiceState=SERVICE_STATE_ALL):
    _EnumServicesStatusW = windll.advapi32.EnumServicesStatusW
    _EnumServicesStatusW.argtypes = [SC_HANDLE, DWORD, DWORD, LPVOID, DWORD, LPDWORD, LPDWORD, LPDWORD]
    _EnumServicesStatusW.restype = bool

    cbBytesNeeded = DWORD(0)
    ServicesReturned = DWORD(0)
    ResumeHandle = DWORD(0)

    _EnumServicesStatusW(
        hSCManager, dwServiceType, dwServiceState, None, 0, byref(cbBytesNeeded), byref(ServicesReturned), byref(ResumeHandle)
    )

    Services = []
    success = False
    while GetLastError() == ERROR_MORE_DATA:
        if cbBytesNeeded.value < sizeof(ENUM_SERVICE_STATUSW):
            break
        ServicesBuffer = ctypes.create_string_buffer("", cbBytesNeeded.value)
        success = _EnumServicesStatusW(
            hSCManager,
            dwServiceType,
            dwServiceState,
            byref(ServicesBuffer),
            sizeof(ServicesBuffer),
            byref(cbBytesNeeded),
            byref(ServicesReturned),
            byref(ResumeHandle),
        )
        if sizeof(ServicesBuffer) < (sizeof(ENUM_SERVICE_STATUSW) * ServicesReturned.value):
            raise ctypes.WinError()
        lpServicesArray = ctypes.cast(ctypes.cast(ctypes.pointer(ServicesBuffer), ctypes.c_void_p), LPENUM_SERVICE_STATUSW)
        for index in compat.xrange(0, ServicesReturned.value):
            Services.append(ServiceStatusEntry(lpServicesArray[index]))
        if success:
            break
    if not success:
        raise ctypes.WinError()

    return Services


EnumServicesStatus = DefaultStringType(EnumServicesStatusA, EnumServicesStatusW)


# BOOL WINAPI EnumServicesStatusEx(
#   _In_         SC_HANDLE hSCManager,
#   _In_         SC_ENUM_TYPE InfoLevel,
#   _In_         DWORD dwServiceType,
#   _In_         DWORD dwServiceState,
#   _Out_opt_    LPBYTE lpServices,
#   _In_         DWORD cbBufSize,
#   _Out_        LPDWORD pcbBytesNeeded,
#   _Out_        LPDWORD lpServicesReturned,
#   _Inout_opt_  LPDWORD lpResumeHandle,
#   _In_opt_     LPCTSTR pszGroupName
# );
def EnumServicesStatusExA(
    hSCManager,
    InfoLevel=SC_ENUM_PROCESS_INFO,
    dwServiceType=SERVICE_DRIVER | SERVICE_WIN32,
    dwServiceState=SERVICE_STATE_ALL,
    pszGroupName=None,
):
    if InfoLevel != SC_ENUM_PROCESS_INFO:
        raise NotImplementedError()

    _EnumServicesStatusExA = windll.advapi32.EnumServicesStatusExA
    _EnumServicesStatusExA.argtypes = [SC_HANDLE, SC_ENUM_TYPE, DWORD, DWORD, LPVOID, DWORD, LPDWORD, LPDWORD, LPDWORD, LPSTR]
    _EnumServicesStatusExA.restype = bool

    cbBytesNeeded = DWORD(0)
    ServicesReturned = DWORD(0)
    ResumeHandle = DWORD(0)

    _EnumServicesStatusExA(
        hSCManager,
        InfoLevel,
        dwServiceType,
        dwServiceState,
        None,
        0,
        byref(cbBytesNeeded),
        byref(ServicesReturned),
        byref(ResumeHandle),
        pszGroupName,
    )

    Services = []
    success = False
    while GetLastError() == ERROR_MORE_DATA:
        if cbBytesNeeded.value < sizeof(ENUM_SERVICE_STATUS_PROCESSA):
            break
        ServicesBuffer = ctypes.create_string_buffer("", cbBytesNeeded.value)
        success = _EnumServicesStatusExA(
            hSCManager,
            InfoLevel,
            dwServiceType,
            dwServiceState,
            byref(ServicesBuffer),
            sizeof(ServicesBuffer),
            byref(cbBytesNeeded),
            byref(ServicesReturned),
            byref(ResumeHandle),
            pszGroupName,
        )
        if sizeof(ServicesBuffer) < (sizeof(ENUM_SERVICE_STATUS_PROCESSA) * ServicesReturned.value):
            raise ctypes.WinError()
        lpServicesArray = ctypes.cast(ctypes.cast(ctypes.pointer(ServicesBuffer), ctypes.c_void_p), LPENUM_SERVICE_STATUS_PROCESSA)
        for index in compat.xrange(0, ServicesReturned.value):
            Services.append(ServiceStatusProcessEntry(lpServicesArray[index]))
        if success:
            break
    if not success:
        raise ctypes.WinError()

    return Services


def EnumServicesStatusExW(
    hSCManager,
    InfoLevel=SC_ENUM_PROCESS_INFO,
    dwServiceType=SERVICE_DRIVER | SERVICE_WIN32,
    dwServiceState=SERVICE_STATE_ALL,
    pszGroupName=None,
):
    _EnumServicesStatusExW = windll.advapi32.EnumServicesStatusExW
    _EnumServicesStatusExW.argtypes = [SC_HANDLE, SC_ENUM_TYPE, DWORD, DWORD, LPVOID, DWORD, LPDWORD, LPDWORD, LPDWORD, LPWSTR]
    _EnumServicesStatusExW.restype = bool

    if InfoLevel != SC_ENUM_PROCESS_INFO:
        raise NotImplementedError()

    cbBytesNeeded = DWORD(0)
    ServicesReturned = DWORD(0)
    ResumeHandle = DWORD(0)

    _EnumServicesStatusExW(
        hSCManager,
        InfoLevel,
        dwServiceType,
        dwServiceState,
        None,
        0,
        byref(cbBytesNeeded),
        byref(ServicesReturned),
        byref(ResumeHandle),
        pszGroupName,
    )

    Services = []
    success = False
    while GetLastError() == ERROR_MORE_DATA:
        if cbBytesNeeded.value < sizeof(ENUM_SERVICE_STATUS_PROCESSW):
            break
        ServicesBuffer = ctypes.create_string_buffer("", cbBytesNeeded.value)
        success = _EnumServicesStatusExW(
            hSCManager,
            InfoLevel,
            dwServiceType,
            dwServiceState,
            byref(ServicesBuffer),
            sizeof(ServicesBuffer),
            byref(cbBytesNeeded),
            byref(ServicesReturned),
            byref(ResumeHandle),
            pszGroupName,
        )
        if sizeof(ServicesBuffer) < (sizeof(ENUM_SERVICE_STATUS_PROCESSW) * ServicesReturned.value):
            raise ctypes.WinError()
        lpServicesArray = ctypes.cast(ctypes.cast(ctypes.pointer(ServicesBuffer), ctypes.c_void_p), LPENUM_SERVICE_STATUS_PROCESSW)
        for index in compat.xrange(0, ServicesReturned.value):
            Services.append(ServiceStatusProcessEntry(lpServicesArray[index]))
        if success:
            break
    if not success:
        raise ctypes.WinError()

    return Services


EnumServicesStatusEx = DefaultStringType(EnumServicesStatusExA, EnumServicesStatusExW)

# BOOL WINAPI EnumDependentServices(
#   _In_       SC_HANDLE hService,
#   _In_       DWORD dwServiceState,
#   _Out_opt_  LPENUM_SERVICE_STATUS lpServices,
#   _In_       DWORD cbBufSize,
#   _Out_      LPDWORD pcbBytesNeeded,
#   _Out_      LPDWORD lpServicesReturned
# );

# TO DO

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