Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 78 additions & 2 deletions ctypes_generation/com_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,88 @@ def parse_method(self):
self.assert_token_type(SemiColonToken)
return ret_type.value, method_name.value, args

def parse_macro_based_com_method(self, interface_name):
# STDMETHOD(NAME) | STDMETHOD_(RetType, NAME)
stddef_macro = self.assert_token_type(NameToken)
self.assert_token_type(OpenParenthesisToken)
assert stddef_macro.value in ("STDMETHOD", "STDMETHODV", "STDMETHOD_"), "Unknown COM definition macro : {0}".format(stddef_macro)
if stddef_macro.value == "STDMETHOD_":
ret_type = self.assert_token_type(NameToken).value
self.assert_token_type(CommaToken)
method_name = self.assert_token_type(NameToken)
else: # STDMETHOD(QueryInterface) | STDMETHODV(Name)(Arg, Arg, ...)
ret_type = "HRESULT"
method_name = self.assert_token_type(NameToken)
self.assert_token_type(CloseParenthesisToken)

#(THIS, ARG2, ARG3)
self.assert_token_type(OpenParenthesisToken)
# THIS / THIS_ may be present or not
potential_this = self.peek()
if type(potential_this) == NameToken and potential_this.value in ("THIS_", "THIS"):
self.assert_token_type(NameToken) # Pass over THIS/THIS_
# Emulate a full argument from the "THIS" macro
args = [(interface_name, 1, "This")]
# Parse the args
while type(self.peek()) != CloseParenthesisToken:
if self.peek().value == "...": #TODO: '...' token ?
self.next_token()
# '...' should be last token before ')'
args.append("...") # Put a type ?
assert type(self.peek()) == CloseParenthesisToken
continue
args.append(self.parse_midl_generated_com_argument())
#print("Pass <{0}>".format(p))
self.next_token()
self.assert_name("PURE")
self.assert_token_type(SemiColonToken)
return ret_type, method_name.value, args

def parse(self):
tok = self.peek()
if type(tok) == NameToken and tok.value == "@IID:":
if type(tok) == NameToken and tok.value == "@IID":
self.next_token()
iid = self.assert_token_type(NameToken).value
else:
iid = None

# Midl Generated or custom macro definition ?
first_token = self.peek()
if type(first_token) == NameToken and first_token.value == "DECLARE_INTERFACE_":
# Manual COM interface definition : not MIDL generated
# Ex: windbg definitions
return self.parse_macro_based_com_vtbl(iid=iid)
else:
return self.parse_midl_generated_com_vtbl(iid=iid)


def parse_macro_based_com_vtbl(self, iid):
self.assert_name("DECLARE_INTERFACE_")
self.assert_token_type(OpenParenthesisToken)
interface_name = self.assert_token_type(NameToken).value
vtable_name = "{interface_name}Vtbl".format(interface_name=interface_name)



self.assert_token_type(CommaToken)
parent_interface = self.assert_token_type(NameToken).value
self.assert_token_type(CloseParenthesisToken)
self.assert_token_type(OpenBracketToken)

res = WinCOMVTABLE(vtable_name, parent_interface=parent_interface)
res.iid = iid

while type(self.peek()) != CloseBracketToken:
ret_type, method_name, args = self.parse_macro_based_com_method(interface_name)
res.add_method(ret_type, method_name, args)

self.assert_token_type(CloseBracketToken)
self.assert_token_type(SemiColonToken)

res.typedefptr = None
return res

def parse_midl_generated_com_vtbl(self, iid):
self.assert_keyword("typedef")
self.assert_keyword("struct")

Expand Down Expand Up @@ -129,8 +204,9 @@ def parse(self):
Method = namedtuple("Method", ["ret_type", "name", "args", 'functype'])
MethodArg = namedtuple("MethodArg", ["type", "byreflevel", "name"])
class WinCOMVTABLE(object):
def __init__(self, vtbl_name):
def __init__(self, vtbl_name, parent_interface=None):
self.vtbl_name = vtbl_name
self.parent_interface = parent_interface
if not vtbl_name.endswith("Vtbl"):
raise ValueError("Com interface are expected to finish by <Vtbl> got <{0}".format(vtbl.name))
self.name = vtbl_name[:-len("Vtbl")]
Expand Down
13 changes: 11 additions & 2 deletions ctypes_generation/definitions/com/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,30 @@ def _default_errcheck(self, result, func, args):
ctypes._check_HRESULT(result)
return args

errcheck = _default_errcheck

def __getattr__(self, name):
if name in self._functions_:
winfunc = self._functions_[name]
# Hacking the HRESULT _check_retval_ and
# letting COMInterface.errcheck do the work of validating / raising
winfunc.restype = COMHRESULT
effective_errcheck = getattr(self, "errcheck", self._default_errcheck)
winfunc.errcheck = effective_errcheck
winfunc.errcheck = self.errcheck
return functools.partial(winfunc, self)
return super(COMInterface, self).__getattribute__(name)

def __repr__(self):
description = "<NULL>" if not self.value else ""
return "<{0}{1} at {2:#x}>".format(type(self).__name__, description, id(self))

# use the context protocol to allow Release() in exit
def __enter__(self):
return self

def __exit__(self, exc_type, exc, tb):
if bool(self):
self.Release()

# Simplified API for QueryInterface for interface embeding their IID
# Or for string/Obj
def query(self, interface, target=None):
Expand Down
59 changes: 59 additions & 0 deletions ctypes_generation/definitions/structures/com.txt
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,65 @@ union _ANON_TMP_variant_sub_union{
};




typedef enum _VARENUM{
VT_EMPTY = 0,
VT_NULL = 1,
VT_I2 = 2,
VT_I4 = 3,
VT_R4 = 4,
VT_R8 = 5,
VT_CY = 6,
VT_DATE = 7,
VT_BSTR = 8,
VT_DISPATCH = 9,
VT_ERROR = 10,
VT_BOOL = 11,
VT_VARIANT = 12,
VT_UNKNOWN = 13,
VT_DECIMAL = 14,
VT_I1 = 16,
VT_UI1 = 17,
VT_UI2 = 18,
VT_UI4 = 19,
VT_I8 = 20,
VT_UI8 = 21,
VT_INT = 22,
VT_UINT = 23,
VT_VOID = 24,
VT_HRESULT = 25,
VT_PTR = 26,
VT_SAFEARRAY = 27,
VT_CARRAY = 28,
VT_USERDEFINED = 29,
VT_LPSTR = 30,
VT_LPWSTR = 31,
VT_RECORD = 36,
VT_INT_PTR = 37,
VT_UINT_PTR = 38,
VT_FILETIME = 64,
VT_BLOB = 65,
VT_STREAM = 66,
VT_STORAGE = 67,
VT_STREAMED_OBJECT = 68,
VT_STORED_OBJECT = 69,
VT_BLOB_OBJECT = 70,
VT_CF = 71,
VT_CLSID = 72,
VT_VERSIONED_STREAM = 73,
VT_BSTR_BLOB = 0xfff,
VT_VECTOR = 0x1000,
VT_ARRAY = 0x2000,
VT_BYREF = 0x4000,
VT_RESERVED = 0x8000,
VT_ILLEGAL = 0xffff,
VT_ILLEGALMASKED = 0xfff,
VT_TYPEMASK = 0xfff
} VARENUM;

/* require previous definition of VARENUM for the extended structure */

typedef struct __tagVARIANT {
VARTYPE vt;
WORD wReserved1;
Expand Down
57 changes: 0 additions & 57 deletions ctypes_generation/definitions/structures/winstruct.txt
Original file line number Diff line number Diff line change
Expand Up @@ -382,63 +382,6 @@ typedef enum _SECTION_INHERIT {





typedef enum _VARENUM{
VT_EMPTY = 0,
VT_NULL = 1,
VT_I2 = 2,
VT_I4 = 3,
VT_R4 = 4,
VT_R8 = 5,
VT_CY = 6,
VT_DATE = 7,
VT_BSTR = 8,
VT_DISPATCH = 9,
VT_ERROR = 10,
VT_BOOL = 11,
VT_VARIANT = 12,
VT_UNKNOWN = 13,
VT_DECIMAL = 14,
VT_I1 = 16,
VT_UI1 = 17,
VT_UI2 = 18,
VT_UI4 = 19,
VT_I8 = 20,
VT_UI8 = 21,
VT_INT = 22,
VT_UINT = 23,
VT_VOID = 24,
VT_HRESULT = 25,
VT_PTR = 26,
VT_SAFEARRAY = 27,
VT_CARRAY = 28,
VT_USERDEFINED = 29,
VT_LPSTR = 30,
VT_LPWSTR = 31,
VT_RECORD = 36,
VT_INT_PTR = 37,
VT_UINT_PTR = 38,
VT_FILETIME = 64,
VT_BLOB = 65,
VT_STREAM = 66,
VT_STORAGE = 67,
VT_STREAMED_OBJECT = 68,
VT_STORED_OBJECT = 69,
VT_BLOB_OBJECT = 70,
VT_CF = 71,
VT_CLSID = 72,
VT_VERSIONED_STREAM = 73,
VT_BSTR_BLOB = 0xfff,
VT_VECTOR = 0x1000,
VT_ARRAY = 0x2000,
VT_BYREF = 0x4000,
VT_RESERVED = 0x8000,
VT_ILLEGAL = 0xffff,
VT_ILLEGALMASKED = 0xfff,
VT_TYPEMASK = 0xfff
} VARENUM;

typedef enum _UDP_TABLE_CLASS {
UDP_TABLE_BASIC,
UDP_TABLE_OWNER_PID,
Expand Down
112 changes: 112 additions & 0 deletions ctypes_generation/extended_structs/__tagVARIANT.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@

_original__tagVARIANT = __tagVARIANT
class __tagVARIANT(__tagVARIANT):
VT_TYPE_TO_FIELD = {
# Fundamental Types
VT_EMPTY: None,
VT_NULL: None,
VT_I2: "iVal",
VT_I4: "lVal",
VT_R4: "fltVal",
VT_R8: "dblVal",
VT_CY: "cyVal",
VT_DATE: "date",
VT_BSTR: "bstrVal",
VT_DISPATCH: "pdispVal",
VT_ERROR: "scode",
VT_BOOL: "boolVal",
VT_VARIANT: "pvarVal", # Only used with VT_BYREF
VT_UNKNOWN: "punkVal",
VT_DECIMAL: "decVal", # Note: decVal sits parallel to the inner struct layout, handled via union
VT_I1: "cVal",
VT_UI1: "bVal",
VT_UI2: "uiVal",
VT_UI4: "ulVal",
VT_I8: "llVal",
VT_UI8: "ullVal",
VT_INT: "intVal",
VT_UINT: "uintVal",
VT_VOID: "byref", # Generic raw pointer
VT_HRESULT: "scode",
VT_PTR: "byref",
VT_SAFEARRAY: "parray",
VT_CARRAY: "byref",
VT_USERDEFINED: "byref",
VT_RECORD: "pvRecord", # Access via __VARIANT_NAME_4 struct inside the union

# Pointer variants (VT_BYREF combinations)
VT_I2 | VT_BYREF: "piVal",
VT_I4 | VT_BYREF: "plVal",
VT_R4 | VT_BYREF: "pfltVal",
VT_R8 | VT_BYREF: "pdblVal",
VT_CY | VT_BYREF: "pcyVal",
VT_DATE | VT_BYREF: "pdate",
VT_BSTR | VT_BYREF: "pbstrVal",
VT_DISPATCH | VT_BYREF: "ppdispVal",
VT_ERROR | VT_BYREF: "pscode",
VT_BOOL | VT_BYREF: "pboolVal",
VT_VARIANT | VT_BYREF: "pvarVal",
VT_UNKNOWN | VT_BYREF: "ppunkVal",
VT_SAFEARRAY | VT_BYREF: "pparray",
VT_DECIMAL | VT_BYREF: "pdecVal",
VT_I1 | VT_BYREF: "pcVal",
VT_UI1 | VT_BYREF: "pbVal",
VT_UI2 | VT_BYREF: "puiVal",
VT_UI4 | VT_BYREF: "pulVal",
VT_I8 | VT_BYREF: "pllVal",
VT_UI8 | VT_BYREF: "pullVal",
VT_INT | VT_BYREF: "pintVal",
VT_UINT | VT_BYREF: "puintVal",
}

# # Copy raw-ctypes fields which is a descriptor :)
# Allow to redefine the vt with type and allow access to the undervalue here
rawvt = _original__tagVARIANT.vt

def get_vt(self):
# Depending on a typedef is not perfect, but name with __ at start break python
# NameError: name '_tagVARIANT__tagVARIANT' is not defined
rawvt = super(VARIANT, self).vt
return VARENUM.mapper[self.rawvt]

def set_vt(self, value):
self.rawvt = value

vt = property(get_vt, set_vt)

@property
def value(self):
fieldname = self.VT_TYPE_TO_FIELD[self.rawvt]
value = getattr(self, fieldname)
if self.rawvt == VT_UNKNOWN:
value = windows.generated_def.IUnknown(value)
return value

@classmethod
def create(cls, value, type=None):
if type is None:
return cls._create_with_guessed_type(value)
return cls._create_with_fixed_type(value, type)

@classmethod
def _create_with_guessed_type(cls, value):
# TODO : better
if value is None:
return cls(VT_NULL)
if isinstance(value, bool):
return cls(VT_BOOL, boolVal=value)
if isinstance(value, int):
return cls(VT_INT, intVal=value) # Default on llval ? int-val ?
if isinstance(value, str): # TODO: py2/py3
return cls(VT_BSTR, bstrVal=value)
raise ValueError("Could not guess VARIANT type for value {}".format(value))

@classmethod
def _create_with_fixed_type(cls, value, type):
if type in (VT_EMPTY, VT_NULL):
return cls(vt=type)
targetfield = cls.VT_TYPE_TO_FIELD[type]
return cls(vt=type, **{targetfield: value})

def __repr__(self):
return "<{0} type={1}>".format(type(self).__name__, VARENUM.mapper[self.vt])
Loading
Loading