Permalink
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
741 lines (648 sloc) 23.7 KB
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2009-2018, 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.
"""
Common definitions.
"""
# TODO
# + add TCHAR and related types?
import ctypes
import functools
#==============================================================================
# This is used later on to calculate the list of exported symbols.
_all = None
_all = set(vars().keys())
#==============================================================================
# Cygwin compatibility.
try:
WindowsError
except NameError:
_gle = None
class WindowsError(OSError):
def __init__(self, *args, **kwargs):
OSError.__init__(self, *args, **kwargs)
global _gle
if _gle is None:
from kernel32 import GetLastError as _gle
self.winerror = _gle()
from os import getenv as _real_getenv
def getenv(key, default=None):
value = _real_getenv(key, None)
if value is None:
value = _real_getenv(key.upper(), default)
return value
#------------------------------------------------------------------------------
# Some stuff from ctypes we'll be using very frequently.
addressof = ctypes.addressof
sizeof = ctypes.sizeof
SIZEOF = ctypes.sizeof
POINTER = ctypes.POINTER
WINFUNCTYPE = ctypes.WINFUNCTYPE
windll = ctypes.windll
# Automatically disable padding of structs and unions on 32 bits.
class Structure(ctypes.Structure):
if sizeof(ctypes.c_void_p) == 4:
_pack_ = 1
class Union(ctypes.Union):
if sizeof(ctypes.c_void_p) == 4:
_pack_ = 1
# The IronPython implementation of byref() was giving some problems,
# so it's best to replace it with the slower pointer() function.
try:
ctypes.c_void_p(ctypes.byref(ctypes.c_char())) # this fails in IronPython
byref = ctypes.byref
except TypeError:
byref = ctypes.pointer
# XXX DEBUG
# The following code can be enabled to make the Win32 API wrappers log to
# standard output the dll and function names, the parameter values and the
# return value for each call.
##WIN32_VERBOSE_MODE = True
WIN32_VERBOSE_MODE = False
if WIN32_VERBOSE_MODE:
class WinDllHook(object):
def __getattr__(self, name):
if name.startswith('_'):
return object.__getattr__(self, name)
return WinFuncHook(name)
class WinFuncHook(object):
def __init__(self, name):
self.__name = name
def __getattr__(self, name):
if name.startswith('_'):
return object.__getattr__(self, name)
return WinCallHook(self.__name, name)
class WinCallHook(object):
def __init__(self, dllname, funcname):
self.__dllname = dllname
self.__funcname = funcname
self.__func = getattr(getattr(ctypes.windll, dllname), funcname)
def __copy_attribute(self, attribute):
try:
value = getattr(self, attribute)
setattr(self.__func, attribute, value)
except AttributeError:
try:
delattr(self.__func, attribute)
except AttributeError:
pass
def __call__(self, *argv):
self.__copy_attribute('argtypes')
self.__copy_attribute('restype')
self.__copy_attribute('errcheck')
print "-"*10
print "%s ! %s %r" % (self.__dllname, self.__funcname, argv)
retval = self.__func(*argv)
print "== %r" % (retval,)
return retval
windll = WinDllHook()
#------------------------------------------------------------------------------
def RaiseIfZero(result, func = None, arguments = ()):
"""
Error checking for most Win32 API calls.
The function is assumed to return an integer, which is C{0} on error.
In that case the C{WindowsError} exception is raised.
"""
if not result:
raise ctypes.WinError()
return result
def RaiseIfNotZero(result, func = None, arguments = ()):
"""
Error checking for some odd Win32 API calls.
The function is assumed to return an integer, which is zero on success.
If the return value is nonzero the C{WindowsError} exception is raised.
This is mostly useful for free() like functions, where the return value is
the pointer to the memory block on failure or a C{NULL} pointer on success.
"""
if result:
raise ctypes.WinError()
return result
def RaiseIfNotErrorSuccess(result, func = None, arguments = ()):
"""
Error checking for Win32 Registry API calls.
The function is assumed to return a Win32 error code. If the code is not
C{ERROR_SUCCESS} then a C{WindowsError} exception is raised.
"""
if result != ERROR_SUCCESS:
raise ctypes.WinError(result)
return result
class GuessStringType(object):
"""
Decorator that guesses the correct version (A or W) to call
based on the types of the strings passed as parameters.
Calls the B{ANSI} version if the only string types are ANSI.
Calls the B{Unicode} version if Unicode or mixed string types are passed.
The default if no string arguments are passed depends on the value of the
L{t_default} class variable.
@type fn_ansi: function
@ivar fn_ansi: ANSI version of the API function to call.
@type fn_unicode: function
@ivar fn_unicode: Unicode (wide) version of the API function to call.
@type t_default: type
@cvar t_default: Default string type to use.
Possible values are:
- type('') for ANSI
- type(u'') for Unicode
"""
# ANSI and Unicode types
t_ansi = type('')
t_unicode = type(u'')
# Default is ANSI for Python 2.x
t_default = t_ansi
def __init__(self, fn_ansi, fn_unicode):
"""
@type fn_ansi: function
@param fn_ansi: ANSI version of the API function to call.
@type fn_unicode: function
@param fn_unicode: Unicode (wide) version of the API function to call.
"""
self.fn_ansi = fn_ansi
self.fn_unicode = fn_unicode
# Copy the wrapped function attributes.
try:
self.__name__ = self.fn_ansi.__name__[:-1] # remove the A or W
except AttributeError:
pass
try:
self.__module__ = self.fn_ansi.__module__
except AttributeError:
pass
try:
self.__doc__ = self.fn_ansi.__doc__
except AttributeError:
pass
def __call__(self, *argv, **argd):
# Shortcut to self.t_ansi
t_ansi = self.t_ansi
# Get the types of all arguments for the function
v_types = [ type(item) for item in argv ]
v_types.extend( [ type(value) for (key, value) in argd.iteritems() ] )
# Get the appropriate function for the default type
if self.t_default == t_ansi:
fn = self.fn_ansi
else:
fn = self.fn_unicode
# If at least one argument is a Unicode string...
if self.t_unicode in v_types:
# If al least one argument is an ANSI string,
# convert all ANSI strings to Unicode
if t_ansi in v_types:
argv = list(argv)
for index in xrange(len(argv)):
if v_types[index] == t_ansi:
argv[index] = unicode(argv[index])
for (key, value) in argd.items():
if type(value) == t_ansi:
argd[key] = unicode(value)
# Use the W version
fn = self.fn_unicode
# If at least one argument is an ANSI string,
# but there are no Unicode strings...
elif t_ansi in v_types:
# Use the A version
fn = self.fn_ansi
# Call the function and return the result
return fn(*argv, **argd)
class DefaultStringType(object):
"""
Decorator that uses the default version (A or W) to call
based on the configuration of the L{GuessStringType} decorator.
@see: L{GuessStringType.t_default}
@type fn_ansi: function
@ivar fn_ansi: ANSI version of the API function to call.
@type fn_unicode: function
@ivar fn_unicode: Unicode (wide) version of the API function to call.
"""
def __init__(self, fn_ansi, fn_unicode):
"""
@type fn_ansi: function
@param fn_ansi: ANSI version of the API function to call.
@type fn_unicode: function
@param fn_unicode: Unicode (wide) version of the API function to call.
"""
self.fn_ansi = fn_ansi
self.fn_unicode = fn_unicode
# Copy the wrapped function attributes.
try:
self.__name__ = self.fn_ansi.__name__[:-1] # remove the A or W
except AttributeError:
pass
try:
self.__module__ = self.fn_ansi.__module__
except AttributeError:
pass
try:
self.__doc__ = self.fn_ansi.__doc__
except AttributeError:
pass
def __call__(self, *argv, **argd):
# Get the appropriate function based on the default.
if GuessStringType.t_default == GuessStringType.t_ansi:
fn = self.fn_ansi
else:
fn = self.fn_unicode
# Call the function and return the result
return fn(*argv, **argd)
def MakeANSIVersion(fn):
"""
Decorator that generates an ANSI version of a Unicode (wide) only API call.
@type fn: callable
@param fn: Unicode (wide) version of the API function to call.
"""
@functools.wraps(fn)
def wrapper(*argv, **argd):
t_ansi = GuessStringType.t_ansi
t_unicode = GuessStringType.t_unicode
v_types = [ type(item) for item in argv ]
v_types.extend( [ type(value) for (key, value) in argd.iteritems() ] )
if t_ansi in v_types:
argv = list(argv)
for index in xrange(len(argv)):
if v_types[index] == t_ansi:
argv[index] = t_unicode(argv[index])
for key, value in argd.items():
if type(value) == t_ansi:
argd[key] = t_unicode(value)
return fn(*argv, **argd)
return wrapper
def MakeWideVersion(fn):
"""
Decorator that generates a Unicode (wide) version of an ANSI only API call.
@type fn: callable
@param fn: ANSI version of the API function to call.
"""
@functools.wraps(fn)
def wrapper(*argv, **argd):
t_ansi = GuessStringType.t_ansi
t_unicode = GuessStringType.t_unicode
v_types = [ type(item) for item in argv ]
v_types.extend( [ type(value) for (key, value) in argd.iteritems() ] )
if t_unicode in v_types:
argv = list(argv)
for index in xrange(len(argv)):
if v_types[index] == t_unicode:
argv[index] = t_ansi(argv[index])
for key, value in argd.items():
if type(value) == t_unicode:
argd[key] = t_ansi(value)
return fn(*argv, **argd)
return wrapper
#--- Types --------------------------------------------------------------------
# http://msdn.microsoft.com/en-us/library/aa383751(v=vs.85).aspx
# Map of basic C types to Win32 types
LPVOID = ctypes.c_void_p
CHAR = ctypes.c_char
WCHAR = ctypes.c_wchar
BYTE = ctypes.c_ubyte
SBYTE = ctypes.c_byte
WORD = ctypes.c_uint16
SWORD = ctypes.c_int16
DWORD = ctypes.c_uint32
SDWORD = ctypes.c_int32
QWORD = ctypes.c_uint64
SQWORD = ctypes.c_int64
SHORT = ctypes.c_int16
USHORT = ctypes.c_uint16
INT = ctypes.c_int32
UINT = ctypes.c_uint32
LONG = ctypes.c_int32
ULONG = ctypes.c_uint32
LONGLONG = ctypes.c_int64 # c_longlong
ULONGLONG = ctypes.c_uint64 # c_ulonglong
LPSTR = ctypes.c_char_p
LPWSTR = ctypes.c_wchar_p
INT8 = ctypes.c_int8
INT16 = ctypes.c_int16
INT32 = ctypes.c_int32
INT64 = ctypes.c_int64
UINT8 = ctypes.c_uint8
UINT16 = ctypes.c_uint16
UINT32 = ctypes.c_uint32
UINT64 = ctypes.c_uint64
LONG32 = ctypes.c_int32
LONG64 = ctypes.c_int64
ULONG32 = ctypes.c_uint32
ULONG64 = ctypes.c_uint64
DWORD32 = ctypes.c_uint32
DWORD64 = ctypes.c_uint64
BOOL = ctypes.c_int32
FLOAT = ctypes.c_float # not sure on cygwin
DOUBLE = ctypes.c_double # not sure on cygwin
# Map size_t to SIZE_T
try:
SIZE_T = ctypes.c_size_t
SSIZE_T = ctypes.c_ssize_t
except AttributeError:
# Size of a pointer
SIZE_T = {1:BYTE, 2:WORD, 4:DWORD, 8:QWORD}[sizeof(LPVOID)]
SSIZE_T = {1:SBYTE, 2:SWORD, 4:SDWORD, 8:SQWORD}[sizeof(LPVOID)]
PSIZE_T = POINTER(SIZE_T)
# Not really pointers but pointer-sized integers
DWORD_PTR = SIZE_T
ULONG_PTR = SIZE_T
LONG_PTR = SIZE_T
# Other Win32 types, more may be added as needed
PVOID = LPVOID
PPVOID = POINTER(PVOID)
PSTR = LPSTR
PWSTR = LPWSTR
PCHAR = LPSTR
PWCHAR = LPWSTR
LPBYTE = POINTER(BYTE)
LPSBYTE = POINTER(SBYTE)
LPWORD = POINTER(WORD)
LPSWORD = POINTER(SWORD)
LPDWORD = POINTER(DWORD)
LPSDWORD = POINTER(SDWORD)
LPULONG = POINTER(ULONG)
LPLONG = POINTER(LONG)
PDWORD = LPDWORD
PDWORD_PTR = POINTER(DWORD_PTR)
PULONG = LPULONG
PLONG = LPLONG
CCHAR = CHAR
BOOLEAN = BYTE
PBOOL = POINTER(BOOL)
LPBOOL = PBOOL
TCHAR = CHAR # XXX ANSI by default?
UCHAR = BYTE
DWORDLONG = ULONGLONG
LPDWORD32 = POINTER(DWORD32)
LPULONG32 = POINTER(ULONG32)
LPDWORD64 = POINTER(DWORD64)
LPULONG64 = POINTER(ULONG64)
PDWORD32 = LPDWORD32
PULONG32 = LPULONG32
PDWORD64 = LPDWORD64
PULONG64 = LPULONG64
ATOM = WORD
HANDLE = LPVOID
PHANDLE = POINTER(HANDLE)
LPHANDLE = PHANDLE
HMODULE = HANDLE
HINSTANCE = HANDLE
HTASK = HANDLE
HKEY = HANDLE
PHKEY = POINTER(HKEY)
HDESK = HANDLE
HRSRC = HANDLE
HSTR = HANDLE
HWINSTA = HANDLE
HKL = HANDLE
HDWP = HANDLE
HFILE = HANDLE
HRESULT = LONG
HGLOBAL = HANDLE
HLOCAL = HANDLE
HGDIOBJ = HANDLE
HDC = HGDIOBJ
HRGN = HGDIOBJ
HBITMAP = HGDIOBJ
HPALETTE = HGDIOBJ
HPEN = HGDIOBJ
HBRUSH = HGDIOBJ
HMF = HGDIOBJ
HEMF = HGDIOBJ
HENHMETAFILE = HGDIOBJ
HMETAFILE = HGDIOBJ
HMETAFILEPICT = HGDIOBJ
HWND = HANDLE
NTSTATUS = LONG
PNTSTATUS = POINTER(NTSTATUS)
KAFFINITY = ULONG_PTR
RVA = DWORD
RVA64 = QWORD
WPARAM = DWORD
LPARAM = LPVOID
LRESULT = LPVOID
ACCESS_MASK = DWORD
REGSAM = ACCESS_MASK
PACCESS_MASK = POINTER(ACCESS_MASK)
PREGSAM = POINTER(REGSAM)
# Since the SID is an opaque structure, let's treat its pointers as void*
PSID = PVOID
# typedef union _LARGE_INTEGER {
# struct {
# DWORD LowPart;
# LONG HighPart;
# } ;
# struct {
# DWORD LowPart;
# LONG HighPart;
# } u;
# LONGLONG QuadPart;
# } LARGE_INTEGER,
# *PLARGE_INTEGER;
# XXX TODO
# typedef struct _FLOAT128 {
# __int64 LowPart;
# __int64 HighPart;
# } FLOAT128;
class FLOAT128 (Structure):
_fields_ = [
("LowPart", QWORD),
("HighPart", QWORD),
]
PFLOAT128 = POINTER(FLOAT128)
# typedef struct DECLSPEC_ALIGN(16) _M128A {
# ULONGLONG Low;
# LONGLONG High;
# } M128A, *PM128A;
class M128A(Structure):
_fields_ = [
("Low", ULONGLONG),
("High", LONGLONG),
]
PM128A = POINTER(M128A)
#--- Constants ----------------------------------------------------------------
NULL = None
INFINITE = -1
TRUE = 1
FALSE = 0
# http://blogs.msdn.com/oldnewthing/archive/2004/08/26/220873.aspx
ANYSIZE_ARRAY = 1
# Invalid handle value is -1 casted to void pointer.
try:
INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value #-1 #0xFFFFFFFF
except TypeError:
if sizeof(ctypes.c_void_p) == 4:
INVALID_HANDLE_VALUE = 0xFFFFFFFF
elif sizeof(ctypes.c_void_p) == 8:
INVALID_HANDLE_VALUE = 0xFFFFFFFFFFFFFFFF
else:
raise
MAX_MODULE_NAME32 = 255
MAX_PATH = 260
# Error codes
# TODO maybe add more error codes?
# if they're too many they could be pickled instead,
# or at the very least put in a new file
ERROR_SUCCESS = 0
ERROR_INVALID_FUNCTION = 1
ERROR_FILE_NOT_FOUND = 2
ERROR_PATH_NOT_FOUND = 3
ERROR_ACCESS_DENIED = 5
ERROR_INVALID_HANDLE = 6
ERROR_NOT_ENOUGH_MEMORY = 8
ERROR_INVALID_DRIVE = 15
ERROR_NO_MORE_FILES = 18
ERROR_BAD_LENGTH = 24
ERROR_HANDLE_EOF = 38
ERROR_HANDLE_DISK_FULL = 39
ERROR_NOT_SUPPORTED = 50
ERROR_FILE_EXISTS = 80
ERROR_INVALID_PARAMETER = 87
ERROR_BUFFER_OVERFLOW = 111
ERROR_DISK_FULL = 112
ERROR_CALL_NOT_IMPLEMENTED = 120
ERROR_SEM_TIMEOUT = 121
ERROR_INSUFFICIENT_BUFFER = 122
ERROR_INVALID_NAME = 123
ERROR_MOD_NOT_FOUND = 126
ERROR_PROC_NOT_FOUND = 127
ERROR_DIR_NOT_EMPTY = 145
ERROR_BAD_THREADID_ADDR = 159
ERROR_BAD_ARGUMENTS = 160
ERROR_BAD_PATHNAME = 161
ERROR_ALREADY_EXISTS = 183
ERROR_INVALID_FLAG_NUMBER = 186
ERROR_ENVVAR_NOT_FOUND = 203
ERROR_FILENAME_EXCED_RANGE = 206
ERROR_MORE_DATA = 234
WAIT_TIMEOUT = 258
ERROR_NO_MORE_ITEMS = 259
ERROR_PARTIAL_COPY = 299
ERROR_INVALID_ADDRESS = 487
ERROR_THREAD_NOT_IN_PROCESS = 566
ERROR_CONTROL_C_EXIT = 572
ERROR_UNHANDLED_EXCEPTION = 574
ERROR_ASSERTION_FAILURE = 668
ERROR_WOW_ASSERTION = 670
ERROR_DBG_EXCEPTION_NOT_HANDLED = 688
ERROR_DBG_REPLY_LATER = 689
ERROR_DBG_UNABLE_TO_PROVIDE_HANDLE = 690
ERROR_DBG_TERMINATE_THREAD = 691
ERROR_DBG_TERMINATE_PROCESS = 692
ERROR_DBG_CONTROL_C = 693
ERROR_DBG_PRINTEXCEPTION_C = 694
ERROR_DBG_RIPEXCEPTION = 695
ERROR_DBG_CONTROL_BREAK = 696
ERROR_DBG_COMMAND_EXCEPTION = 697
ERROR_DBG_EXCEPTION_HANDLED = 766
ERROR_DBG_CONTINUE = 767
ERROR_ELEVATION_REQUIRED = 740
ERROR_NOACCESS = 998
ERROR_CIRCULAR_DEPENDENCY = 1059
ERROR_SERVICE_DOES_NOT_EXIST = 1060
ERROR_SERVICE_CANNOT_ACCEPT_CTRL = 1061
ERROR_SERVICE_NOT_ACTIVE = 1062
ERROR_FAILED_SERVICE_CONTROLLER_CONNECT = 1063
ERROR_EXCEPTION_IN_SERVICE = 1064
ERROR_DATABASE_DOES_NOT_EXIST = 1065
ERROR_SERVICE_SPECIFIC_ERROR = 1066
ERROR_PROCESS_ABORTED = 1067
ERROR_SERVICE_DEPENDENCY_FAIL = 1068
ERROR_SERVICE_LOGON_FAILED = 1069
ERROR_SERVICE_START_HANG = 1070
ERROR_INVALID_SERVICE_LOCK = 1071
ERROR_SERVICE_MARKED_FOR_DELETE = 1072
ERROR_SERVICE_EXISTS = 1073
ERROR_ALREADY_RUNNING_LKG = 1074
ERROR_SERVICE_DEPENDENCY_DELETED = 1075
ERROR_BOOT_ALREADY_ACCEPTED = 1076
ERROR_SERVICE_NEVER_STARTED = 1077
ERROR_DUPLICATE_SERVICE_NAME = 1078
ERROR_DIFFERENT_SERVICE_ACCOUNT = 1079
ERROR_CANNOT_DETECT_DRIVER_FAILURE = 1080
ERROR_CANNOT_DETECT_PROCESS_ABORT = 1081
ERROR_NO_RECOVERY_PROGRAM = 1082
ERROR_SERVICE_NOT_IN_EXE = 1083
ERROR_NOT_SAFEBOOT_SERVICE = 1084
ERROR_DEBUGGER_INACTIVE = 1284
ERROR_PRIVILEGE_NOT_HELD = 1314
ERROR_NONE_MAPPED = 1332
RPC_S_SERVER_UNAVAILABLE = 1722
# Standard access rights
DELETE = 0x00010000L
READ_CONTROL = 0x00020000L
WRITE_DAC = 0x00040000L
WRITE_OWNER = 0x00080000L
SYNCHRONIZE = 0x00100000L
STANDARD_RIGHTS_REQUIRED = 0x000F0000L
STANDARD_RIGHTS_READ = READ_CONTROL
STANDARD_RIGHTS_WRITE = READ_CONTROL
STANDARD_RIGHTS_EXECUTE = READ_CONTROL
STANDARD_RIGHTS_ALL = 0x001F0000L
SPECIFIC_RIGHTS_ALL = 0x0000FFFFL
#--- Structures ---------------------------------------------------------------
# typedef struct _LSA_UNICODE_STRING {
# USHORT Length;
# USHORT MaximumLength;
# PWSTR Buffer;
# } LSA_UNICODE_STRING,
# *PLSA_UNICODE_STRING,
# UNICODE_STRING,
# *PUNICODE_STRING;
class UNICODE_STRING(Structure):
_fields_ = [
("Length", USHORT),
("MaximumLength", USHORT),
("Buffer", PVOID),
]
# From MSDN:
#
# typedef struct _GUID {
# DWORD Data1;
# WORD Data2;
# WORD Data3;
# BYTE Data4[8];
# } GUID;
class GUID(Structure):
_fields_ = [
("Data1", DWORD),
("Data2", WORD),
("Data3", WORD),
("Data4", BYTE * 8),
]
# From MSDN:
#
# typedef struct _LIST_ENTRY {
# struct _LIST_ENTRY *Flink;
# struct _LIST_ENTRY *Blink;
# } LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
class LIST_ENTRY(Structure):
_fields_ = [
("Flink", PVOID), # POINTER(LIST_ENTRY)
("Blink", PVOID), # POINTER(LIST_ENTRY)
]
#==============================================================================
# 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()
#==============================================================================