"""Utilities for makegw - Parse a header file to build an interface This module contains the core code for parsing a header file describing a COM interface, and building it into an "Interface" structure. Each Interface has methods, and each method has arguments. Each argument knows how to use Py_BuildValue or Py_ParseTuple to exchange itself with Python. See the @win32com.makegw@ module for information in building a COM interface """ import re import traceback class error_not_found(Exception): def __init__(self, msg="The requested item could not be found"): super(error_not_found, self).__init__(msg) class error_not_supported(Exception): def __init__(self, msg="The required functionality is not supported"): super(error_not_supported, self).__init__(msg) VERBOSE = 0 DEBUG = 0 ## NOTE : For interfaces as params to work correctly, you must ## make sure any PythonCOM extensions which expose the interface are loaded ## before generating. class ArgFormatter: """An instance for a specific type of argument. Knows how to convert itself""" def __init__(self, arg, builtinIndirection, declaredIndirection=0): # print 'init:', arg.name, builtinIndirection, declaredIndirection, arg.indirectionLevel self.arg = arg self.builtinIndirection = builtinIndirection self.declaredIndirection = declaredIndirection self.gatewayMode = 0 def _IndirectPrefix(self, indirectionFrom, indirectionTo): """Given the indirection level I was declared at (0=Normal, 1=*, 2=**) return a string prefix so I can pass to a function with the required indirection (where the default is the indirection of the method's param. eg, assuming my arg has indirection level of 2, if this function was passed 1 it would return "&", so that a variable declared with indirection of 1 can be prefixed with this to turn it into the indirection level required of 2 """ dif = indirectionFrom - indirectionTo if dif == 0: return "" elif dif == -1: return "&" elif dif == 1: return "*" else: return "?? (%d)" % (dif,) raise error_not_supported("Can't indirect this far - please fix me :-)") def GetIndirectedArgName(self, indirectFrom, indirectionTo): # print 'get:',self.arg.name, indirectFrom,self._GetDeclaredIndirection() + self.builtinIndirection, indirectionTo, self.arg.indirectionLevel if indirectFrom is None: ### ACK! this does not account for [in][out] variables. ### when this method is called, we need to know which indirectFrom = self._GetDeclaredIndirection() + self.builtinIndirection return self._IndirectPrefix(indirectFrom, indirectionTo) + self.arg.name def GetBuildValueArg(self): "Get the argument to be passes to Py_BuildValue" return self.arg.name def GetParseTupleArg(self): "Get the argument to be passed to PyArg_ParseTuple" if self.gatewayMode: # use whatever they were declared with return self.GetIndirectedArgName(None, 1) # local declarations have just their builtin indirection return self.GetIndirectedArgName(self.builtinIndirection, 1) def GetInterfaceCppObjectInfo(self): """Provide information about the C++ object used. Simple variables (such as integers) can declare their type (eg an integer) and use it as the target of both PyArg_ParseTuple and the COM function itself. More complex types require a PyObject * declared as the target of PyArg_ParseTuple, then some conversion routine to the C++ object which is actually passed to COM. This method provides the name, and optionally the type of that C++ variable. If the type if provided, the caller will likely generate a variable declaration. The name must always be returned. Result is a tuple of (variableName, [DeclareType|None|""]) """ # the first return element is the variable to be passed as # an argument to an interface method. the variable was # declared with only its builtin indirection level. when # we pass it, we'll need to pass in whatever amount of # indirection was applied (plus the builtin amount) # the second return element is the variable declaration; it # should simply be builtin indirection return self.GetIndirectedArgName( self.builtinIndirection, self.arg.indirectionLevel + self.builtinIndirection ), "%s %s" % (self.GetUnconstType(), self.arg.name) def GetInterfaceArgCleanup(self): "Return cleanup code for C++ args passed to the interface method." if DEBUG: return "/* GetInterfaceArgCleanup output goes here: %s */\n" % self.arg.name else: return "" def GetInterfaceArgCleanupGIL(self): """Return cleanup code for C++ args passed to the interface method that must be executed with the GIL held""" if DEBUG: return ( "/* GetInterfaceArgCleanup (GIL held) output goes here: %s */\n" % self.arg.name ) else: return "" def GetUnconstType(self): return self.arg.unc_type def SetGatewayMode(self): self.gatewayMode = 1 def _GetDeclaredIndirection(self): return self.arg.indirectionLevel print("declared:", self.arg.name, self.gatewayMode) if self.gatewayMode: return self.arg.indirectionLevel else: return self.declaredIndirection def DeclareParseArgTupleInputConverter(self): "Declare the variable used as the PyArg_ParseTuple param for a gateway" # Only declare it?? # if self.arg.indirectionLevel==0: # return "\t%s %s;\n" % (self.arg.type, self.arg.name) # else: if DEBUG: return ( "/* Declare ParseArgTupleInputConverter goes here: %s */\n" % self.arg.name ) else: return "" def GetParsePostCode(self): "Get a string of C++ code to be executed after (ie, to finalise) the PyArg_ParseTuple conversion" if DEBUG: return "/* GetParsePostCode code goes here: %s */\n" % self.arg.name else: return "" def GetBuildForInterfacePreCode(self): "Get a string of C++ code to be executed before (ie, to initialise) the Py_BuildValue conversion for Interfaces" if DEBUG: return "/* GetBuildForInterfacePreCode goes here: %s */\n" % self.arg.name else: return "" def GetBuildForGatewayPreCode(self): "Get a string of C++ code to be executed before (ie, to initialise) the Py_BuildValue conversion for Gateways" s = self.GetBuildForInterfacePreCode() # Usually the same if DEBUG: if s[:4] == "/* G": s = "/* GetBuildForGatewayPreCode goes here: %s */\n" % self.arg.name return s def GetBuildForInterfacePostCode(self): "Get a string of C++ code to be executed after (ie, to finalise) the Py_BuildValue conversion for Interfaces" if DEBUG: return "/* GetBuildForInterfacePostCode goes here: %s */\n" % self.arg.name return "" def GetBuildForGatewayPostCode(self): "Get a string of C++ code to be executed after (ie, to finalise) the Py_BuildValue conversion for Gateways" s = self.GetBuildForInterfacePostCode() # Usually the same if DEBUG: if s[:4] == "/* G": s = "/* GetBuildForGatewayPostCode goes here: %s */\n" % self.arg.name return s def GetAutoduckString(self): return "// @pyparm %s|%s||Description for %s" % ( self._GetPythonTypeDesc(), self.arg.name, self.arg.name, ) def _GetPythonTypeDesc(self): "Returns a string with the description of the type. Used for doco purposes" return None def NeedUSES_CONVERSION(self): "Determines if this arg forces a USES_CONVERSION macro" return 0 # Special formatter for floats since they're smaller than Python floats. class ArgFormatterFloat(ArgFormatter): def GetFormatChar(self): return "f" def DeclareParseArgTupleInputConverter(self): # Declare a double variable return "\tdouble dbl%s;\n" % self.arg.name def GetParseTupleArg(self): return "&dbl" + self.arg.name def _GetPythonTypeDesc(self): return "float" def GetBuildValueArg(self): return "&dbl" + self.arg.name def GetBuildForInterfacePreCode(self): return "\tdbl" + self.arg.name + " = " + self.arg.name + ";\n" def GetBuildForGatewayPreCode(self): return ( "\tdbl%s = " % self.arg.name + self._IndirectPrefix(self._GetDeclaredIndirection(), 0) + self.arg.name + ";\n" ) def GetParsePostCode(self): s = "\t" if self.gatewayMode: s = s + self._IndirectPrefix(self._GetDeclaredIndirection(), 0) s = s + self.arg.name s = s + " = (float)dbl%s;\n" % self.arg.name return s # Special formatter for Shorts because they're # a different size than Python ints! class ArgFormatterShort(ArgFormatter): def GetFormatChar(self): return "i" def DeclareParseArgTupleInputConverter(self): # Declare a double variable return "\tINT i%s;\n" % self.arg.name def GetParseTupleArg(self): return "&i" + self.arg.name def _GetPythonTypeDesc(self): return "int" def GetBuildValueArg(self): return "&i" + self.arg.name def GetBuildForInterfacePreCode(self): return "\ti" + self.arg.name + " = " + self.arg.name + ";\n" def GetBuildForGatewayPreCode(self): return ( "\ti%s = " % self.arg.name + self._IndirectPrefix(self._GetDeclaredIndirection(), 0) + self.arg.name + ";\n" ) def GetParsePostCode(self): s = "\t" if self.gatewayMode: s = s + self._IndirectPrefix(self._GetDeclaredIndirection(), 0) s = s + self.arg.name s = s + " = i%s;\n" % self.arg.name return s # for types which are 64bits on AMD64 - eg, HWND class ArgFormatterLONG_PTR(ArgFormatter): def GetFormatChar(self): return "O" def DeclareParseArgTupleInputConverter(self): # Declare a PyObject variable return "\tPyObject *ob%s;\n" % self.arg.name def GetParseTupleArg(self): return "&ob" + self.arg.name def _GetPythonTypeDesc(self): return "int/long" def GetBuildValueArg(self): return "ob" + self.arg.name def GetBuildForInterfacePostCode(self): return "\tPy_XDECREF(ob%s);\n" % self.arg.name def GetParsePostCode(self): return ( "\tif (bPythonIsHappy && !PyWinLong_AsULONG_PTR(ob%s, (ULONG_PTR *)%s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2)) ) def GetBuildForInterfacePreCode(self): notdirected = self.GetIndirectedArgName(None, 1) return "\tob%s = PyWinObject_FromULONG_PTR(%s);\n" % ( self.arg.name, notdirected, ) def GetBuildForGatewayPostCode(self): return "\tPy_XDECREF(ob%s);\n" % self.arg.name class ArgFormatterPythonCOM(ArgFormatter): """An arg formatter for types exposed in the PythonCOM module""" def GetFormatChar(self): return "O" # def GetInterfaceCppObjectInfo(self): # return ArgFormatter.GetInterfaceCppObjectInfo(self)[0], \ # "%s %s%s" % (self.arg.unc_type, "*" * self._GetDeclaredIndirection(), self.arg.name) def DeclareParseArgTupleInputConverter(self): # Declare a PyObject variable return "\tPyObject *ob%s;\n" % self.arg.name def GetParseTupleArg(self): return "&ob" + self.arg.name def _GetPythonTypeDesc(self): return "" % self.arg.type def GetBuildValueArg(self): return "ob" + self.arg.name def GetBuildForInterfacePostCode(self): return "\tPy_XDECREF(ob%s);\n" % self.arg.name class ArgFormatterBSTR(ArgFormatterPythonCOM): def _GetPythonTypeDesc(self): return "" def GetParsePostCode(self): return ( "\tif (bPythonIsHappy && !PyWinObject_AsBstr(ob%s, %s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2)) ) def GetBuildForInterfacePreCode(self): notdirected = self.GetIndirectedArgName(None, 1) return "\tob%s = MakeBstrToObj(%s);\n" % (self.arg.name, notdirected) def GetBuildForInterfacePostCode(self): return "\tSysFreeString(%s);\n" % ( self.arg.name, ) + ArgFormatterPythonCOM.GetBuildForInterfacePostCode(self) def GetBuildForGatewayPostCode(self): return "\tPy_XDECREF(ob%s);\n" % self.arg.name class ArgFormatterOLECHAR(ArgFormatterPythonCOM): def _GetPythonTypeDesc(self): return "" def GetUnconstType(self): if self.arg.type[:3] == "LPC": return self.arg.type[:2] + self.arg.type[3:] else: return self.arg.unc_type def GetParsePostCode(self): return ( "\tif (bPythonIsHappy && !PyWinObject_AsBstr(ob%s, %s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2)) ) def GetInterfaceArgCleanup(self): return "\tSysFreeString(%s);\n" % self.GetIndirectedArgName(None, 1) def GetBuildForInterfacePreCode(self): # the variable was declared with just its builtin indirection notdirected = self.GetIndirectedArgName(self.builtinIndirection, 1) return "\tob%s = MakeOLECHARToObj(%s);\n" % (self.arg.name, notdirected) def GetBuildForInterfacePostCode(self): # memory returned into an OLECHAR should be freed return "\tCoTaskMemFree(%s);\n" % ( self.arg.name, ) + ArgFormatterPythonCOM.GetBuildForInterfacePostCode(self) def GetBuildForGatewayPostCode(self): return "\tPy_XDECREF(ob%s);\n" % self.arg.name class ArgFormatterTCHAR(ArgFormatterPythonCOM): def _GetPythonTypeDesc(self): return "string/" def GetUnconstType(self): if self.arg.type[:3] == "LPC": return self.arg.type[:2] + self.arg.type[3:] else: return self.arg.unc_type def GetParsePostCode(self): return ( "\tif (bPythonIsHappy && !PyWinObject_AsTCHAR(ob%s, %s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 2)) ) def GetInterfaceArgCleanup(self): return "\tPyWinObject_FreeTCHAR(%s);\n" % self.GetIndirectedArgName(None, 1) def GetBuildForInterfacePreCode(self): # the variable was declared with just its builtin indirection notdirected = self.GetIndirectedArgName(self.builtinIndirection, 1) return "\tob%s = PyWinObject_FromTCHAR(%s);\n" % (self.arg.name, notdirected) def GetBuildForInterfacePostCode(self): return "// ??? - TCHAR post code\n" def GetBuildForGatewayPostCode(self): return "\tPy_XDECREF(ob%s);\n" % self.arg.name class ArgFormatterIID(ArgFormatterPythonCOM): def _GetPythonTypeDesc(self): return "" def GetParsePostCode(self): return "\tif (!PyWinObject_AsIID(ob%s, &%s)) bPythonIsHappy = FALSE;\n" % ( self.arg.name, self.arg.name, ) def GetBuildForInterfacePreCode(self): # notdirected = self.GetIndirectedArgName(self.arg.indirectionLevel, 0) notdirected = self.GetIndirectedArgName(None, 0) return "\tob%s = PyWinObject_FromIID(%s);\n" % (self.arg.name, notdirected) def GetInterfaceCppObjectInfo(self): return self.arg.name, "IID %s" % (self.arg.name) class ArgFormatterTime(ArgFormatterPythonCOM): def __init__(self, arg, builtinIndirection, declaredIndirection=0): # we don't want to declare LPSYSTEMTIME / LPFILETIME objects if arg.indirectionLevel == 0 and arg.unc_type[:2] == "LP": arg.unc_type = arg.unc_type[2:] # reduce the builtin and increment the declaration arg.indirectionLevel = arg.indirectionLevel + 1 builtinIndirection = 0 ArgFormatterPythonCOM.__init__( self, arg, builtinIndirection, declaredIndirection ) def _GetPythonTypeDesc(self): return "" def GetParsePostCode(self): # variable was declared with only the builtinIndirection ### NOTE: this is an [in] ... so use only builtin return ( '\tif (!PyTime_Check(ob%s)) {\n\t\tPyErr_SetString(PyExc_TypeError, "The argument must be a PyTime object");\n\t\tbPythonIsHappy = FALSE;\n\t}\n\tif (!((PyTime *)ob%s)->GetTime(%s)) bPythonIsHappy = FALSE;\n' % ( self.arg.name, self.arg.name, self.GetIndirectedArgName(self.builtinIndirection, 1), ) ) def GetBuildForInterfacePreCode(self): ### use just the builtinIndirection again... notdirected = self.GetIndirectedArgName(self.builtinIndirection, 0) return "\tob%s = new PyTime(%s);\n" % (self.arg.name, notdirected) def GetBuildForInterfacePostCode(self): ### hack to determine if we need to free stuff ret = "" if self.builtinIndirection + self.arg.indirectionLevel > 1: # memory returned into an OLECHAR should be freed ret = "\tCoTaskMemFree(%s);\n" % self.arg.name return ret + ArgFormatterPythonCOM.GetBuildForInterfacePostCode(self) class ArgFormatterSTATSTG(ArgFormatterPythonCOM): def _GetPythonTypeDesc(self): return "" def GetParsePostCode(self): return ( "\tif (!PyCom_PyObjectAsSTATSTG(ob%s, %s, 0/*flags*/)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 1)) ) def GetBuildForInterfacePreCode(self): notdirected = self.GetIndirectedArgName(None, 1) return ( "\tob%s = PyCom_PyObjectFromSTATSTG(%s);\n\t// STATSTG doco says our responsibility to free\n\tif ((%s).pwcsName) CoTaskMemFree((%s).pwcsName);\n" % ( self.arg.name, self.GetIndirectedArgName(None, 1), notdirected, notdirected, ) ) class ArgFormatterGeneric(ArgFormatterPythonCOM): def _GetPythonTypeDesc(self): return "" % self.arg.type def GetParsePostCode(self): return "\tif (!PyObject_As%s(ob%s, &%s) bPythonIsHappy = FALSE;\n" % ( self.arg.type, self.arg.name, self.GetIndirectedArgName(None, 1), ) def GetInterfaceArgCleanup(self): return "\tPyObject_Free%s(%s);\n" % (self.arg.type, self.arg.name) def GetBuildForInterfacePreCode(self): notdirected = self.GetIndirectedArgName(None, 1) return "\tob%s = PyObject_From%s(%s);\n" % ( self.arg.name, self.arg.type, self.GetIndirectedArgName(None, 1), ) class ArgFormatterIDLIST(ArgFormatterPythonCOM): def _GetPythonTypeDesc(self): return "" def GetParsePostCode(self): return ( "\tif (bPythonIsHappy && !PyObject_AsPIDL(ob%s, &%s)) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 1)) ) def GetInterfaceArgCleanup(self): return "\tPyObject_FreePIDL(%s);\n" % (self.arg.name,) def GetBuildForInterfacePreCode(self): notdirected = self.GetIndirectedArgName(None, 1) return "\tob%s = PyObject_FromPIDL(%s);\n" % ( self.arg.name, self.GetIndirectedArgName(None, 1), ) class ArgFormatterHANDLE(ArgFormatterPythonCOM): def _GetPythonTypeDesc(self): return "" def GetParsePostCode(self): return ( "\tif (!PyWinObject_AsHANDLE(ob%s, &%s, FALSE) bPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 1)) ) def GetBuildForInterfacePreCode(self): notdirected = self.GetIndirectedArgName(None, 1) return "\tob%s = PyWinObject_FromHANDLE(%s);\n" % ( self.arg.name, self.GetIndirectedArgName(None, 0), ) class ArgFormatterLARGE_INTEGER(ArgFormatterPythonCOM): def GetKeyName(self): return "LARGE_INTEGER" def _GetPythonTypeDesc(self): return "" % self.GetKeyName() def GetParsePostCode(self): return "\tif (!PyWinObject_As%s(ob%s, %s)) bPythonIsHappy = FALSE;\n" % ( self.GetKeyName(), self.arg.name, self.GetIndirectedArgName(None, 1), ) def GetBuildForInterfacePreCode(self): notdirected = self.GetIndirectedArgName(None, 0) return "\tob%s = PyWinObject_From%s(%s);\n" % ( self.arg.name, self.GetKeyName(), notdirected, ) class ArgFormatterULARGE_INTEGER(ArgFormatterLARGE_INTEGER): def GetKeyName(self): return "ULARGE_INTEGER" class ArgFormatterInterface(ArgFormatterPythonCOM): def GetInterfaceCppObjectInfo(self): return self.GetIndirectedArgName(1, self.arg.indirectionLevel), "%s * %s" % ( self.GetUnconstType(), self.arg.name, ) def GetParsePostCode(self): # This gets called for out params in gateway mode if self.gatewayMode: sArg = self.GetIndirectedArgName(None, 2) else: # vs. in params for interface mode. sArg = self.GetIndirectedArgName(1, 2) return ( "\tif (bPythonIsHappy && !PyCom_InterfaceFromPyInstanceOrObject(ob%s, IID_%s, (void **)%s, TRUE /* bNoneOK */))\n\t\t bPythonIsHappy = FALSE;\n" % (self.arg.name, self.arg.type, sArg) ) def GetBuildForInterfacePreCode(self): return "\tob%s = PyCom_PyObjectFromIUnknown(%s, IID_%s, FALSE);\n" % ( self.arg.name, self.arg.name, self.arg.type, ) def GetBuildForGatewayPreCode(self): sPrefix = self._IndirectPrefix(self._GetDeclaredIndirection(), 1) return "\tob%s = PyCom_PyObjectFromIUnknown(%s%s, IID_%s, TRUE);\n" % ( self.arg.name, sPrefix, self.arg.name, self.arg.type, ) def GetInterfaceArgCleanup(self): return "\tif (%s) %s->Release();\n" % (self.arg.name, self.arg.name) class ArgFormatterVARIANT(ArgFormatterPythonCOM): def GetParsePostCode(self): return ( "\tif ( !PyCom_VariantFromPyObject(ob%s, %s) )\n\t\tbPythonIsHappy = FALSE;\n" % (self.arg.name, self.GetIndirectedArgName(None, 1)) ) def GetBuildForGatewayPreCode(self): notdirected = self.GetIndirectedArgName(None, 1) return "\tob%s = PyCom_PyObjectFromVariant(%s);\n" % ( self.arg.name, notdirected, ) def GetBuildForGatewayPostCode(self): return "\tPy_XDECREF(ob%s);\n" % self.arg.name # Key : , Python Type Description, ParseTuple format char ConvertSimpleTypes = { "BOOL": ("BOOL", "int", "i"), "UINT": ("UINT", "int", "i"), "BYTE": ("BYTE", "int", "i"), "INT": ("INT", "int", "i"), "DWORD": ("DWORD", "int", "l"), "HRESULT": ("HRESULT", "int", "l"), "ULONG": ("ULONG", "int", "l"), "LONG": ("LONG", "int", "l"), "int": ("int", "int", "i"), "long": ("long", "int", "l"), "DISPID": ("DISPID", "long", "l"), "APPBREAKFLAGS": ("int", "int", "i"), "BREAKRESUMEACTION": ("int", "int", "i"), "ERRORRESUMEACTION": ("int", "int", "i"), "BREAKREASON": ("int", "int", "i"), "BREAKPOINT_STATE": ("int", "int", "i"), "BREAKRESUME_ACTION": ("int", "int", "i"), "SOURCE_TEXT_ATTR": ("int", "int", "i"), "TEXT_DOC_ATTR": ("int", "int", "i"), "QUERYOPTION": ("int", "int", "i"), "PARSEACTION": ("int", "int", "i"), } class ArgFormatterSimple(ArgFormatter): """An arg formatter for simple integer etc types""" def GetFormatChar(self): return ConvertSimpleTypes[self.arg.type][2] def _GetPythonTypeDesc(self): return ConvertSimpleTypes[self.arg.type][1] AllConverters = { "const OLECHAR": (ArgFormatterOLECHAR, 0, 1), "WCHAR": (ArgFormatterOLECHAR, 0, 1), "OLECHAR": (ArgFormatterOLECHAR, 0, 1), "LPCOLESTR": (ArgFormatterOLECHAR, 1, 1), "LPOLESTR": (ArgFormatterOLECHAR, 1, 1), "LPCWSTR": (ArgFormatterOLECHAR, 1, 1), "LPWSTR": (ArgFormatterOLECHAR, 1, 1), "LPCSTR": (ArgFormatterOLECHAR, 1, 1), "LPTSTR": (ArgFormatterTCHAR, 1, 1), "LPCTSTR": (ArgFormatterTCHAR, 1, 1), "HANDLE": (ArgFormatterHANDLE, 0), "BSTR": (ArgFormatterBSTR, 1, 0), "const IID": (ArgFormatterIID, 0), "CLSID": (ArgFormatterIID, 0), "IID": (ArgFormatterIID, 0), "GUID": (ArgFormatterIID, 0), "const GUID": (ArgFormatterIID, 0), "const IID": (ArgFormatterIID, 0), "REFCLSID": (ArgFormatterIID, 0), "REFIID": (ArgFormatterIID, 0), "REFGUID": (ArgFormatterIID, 0), "const FILETIME": (ArgFormatterTime, 0), "const SYSTEMTIME": (ArgFormatterTime, 0), "const LPSYSTEMTIME": (ArgFormatterTime, 1, 1), "LPSYSTEMTIME": (ArgFormatterTime, 1, 1), "FILETIME": (ArgFormatterTime, 0), "SYSTEMTIME": (ArgFormatterTime, 0), "STATSTG": (ArgFormatterSTATSTG, 0), "LARGE_INTEGER": (ArgFormatterLARGE_INTEGER, 0), "ULARGE_INTEGER": (ArgFormatterULARGE_INTEGER, 0), "VARIANT": (ArgFormatterVARIANT, 0), "float": (ArgFormatterFloat, 0), "single": (ArgFormatterFloat, 0), "short": (ArgFormatterShort, 0), "WORD": (ArgFormatterShort, 0), "VARIANT_BOOL": (ArgFormatterShort, 0), "HWND": (ArgFormatterLONG_PTR, 1), "HMENU": (ArgFormatterLONG_PTR, 1), "HOLEMENU": (ArgFormatterLONG_PTR, 1), "HICON": (ArgFormatterLONG_PTR, 1), "HDC": (ArgFormatterLONG_PTR, 1), "LPARAM": (ArgFormatterLONG_PTR, 1), "WPARAM": (ArgFormatterLONG_PTR, 1), "LRESULT": (ArgFormatterLONG_PTR, 1), "UINT": (ArgFormatterShort, 0), "SVSIF": (ArgFormatterShort, 0), "Control": (ArgFormatterInterface, 0, 1), "DataObject": (ArgFormatterInterface, 0, 1), "_PropertyBag": (ArgFormatterInterface, 0, 1), "AsyncProp": (ArgFormatterInterface, 0, 1), "DataSource": (ArgFormatterInterface, 0, 1), "DataFormat": (ArgFormatterInterface, 0, 1), "void **": (ArgFormatterInterface, 2, 2), "ITEMIDLIST": (ArgFormatterIDLIST, 0, 0), "LPITEMIDLIST": (ArgFormatterIDLIST, 0, 1), "LPCITEMIDLIST": (ArgFormatterIDLIST, 0, 1), "const ITEMIDLIST": (ArgFormatterIDLIST, 0, 1), } # Auto-add all the simple types for key in ConvertSimpleTypes.keys(): AllConverters[key] = ArgFormatterSimple, 0 def make_arg_converter(arg): try: clz = AllConverters[arg.type][0] bin = AllConverters[arg.type][1] decl = 0 if len(AllConverters[arg.type]) > 2: decl = AllConverters[arg.type][2] return clz(arg, bin, decl) except KeyError: if arg.type[0] == "I": return ArgFormatterInterface(arg, 0, 1) raise error_not_supported( "The type '%s' (%s) is unknown." % (arg.type, arg.name) ) ############################################################# # # The instances that represent the args, methods and interface class Argument: """A representation of an argument to a COM method This class contains information about a specific argument to a method. In addition, methods exist so that an argument knows how to convert itself to/from Python arguments. """ # in,out type name [ ] # -------------- -------- ------------ ------ regex = re.compile(r"/\* \[([^\]]*.*?)] \*/[ \t](.*[* ]+)(\w+)(\[ *])?[\),]") def __init__(self, good_interface_names): self.good_interface_names = good_interface_names self.inout = self.name = self.type = None self.const = 0 self.arrayDecl = 0 def BuildFromFile(self, file): """Parse and build my data from a file Reads the next line in the file, and matches it as an argument description. If not a valid argument line, an error_not_found exception is raised. """ line = file.readline() mo = self.regex.search(line) if not mo: raise error_not_found self.name = mo.group(3) self.inout = mo.group(1).split("][") typ = mo.group(2).strip() self.raw_type = typ self.indirectionLevel = 0 if mo.group(4): # Has "[ ]" decl self.arrayDecl = 1 try: pos = typ.rindex("__RPC_FAR") self.indirectionLevel = self.indirectionLevel + 1 typ = typ[:pos].strip() except ValueError: pass typ = typ.replace("__RPC_FAR", "") while 1: try: pos = typ.rindex("*") self.indirectionLevel = self.indirectionLevel + 1 typ = typ[:pos].strip() except ValueError: break self.type = typ if self.type[:6] == "const ": self.unc_type = self.type[6:] else: self.unc_type = self.type if VERBOSE: print( " Arg %s of type %s%s (%s)" % (self.name, self.type, "*" * self.indirectionLevel, self.inout) ) def HasAttribute(self, typ): """Determines if the argument has the specific attribute. Argument attributes are specified in the header file, such as "[in][out][retval]" etc. You can pass a specific string (eg "out") to find if this attribute was specified for the argument """ return typ in self.inout def GetRawDeclaration(self): ret = "%s %s" % (self.raw_type, self.name) if self.arrayDecl: ret = ret + "[]" return ret class Method: """A representation of a C++ method on a COM interface This class contains information about a specific method, as well as a list of all @Argument@s """ # options ret type callconv name # ----------------- -------- -------- -------- regex = re.compile(r"virtual (/\*.*?\*/ )?(.*?) (.*?) (.*?)\(\w?") def __init__(self, good_interface_names): self.good_interface_names = good_interface_names self.name = self.result = self.callconv = None self.args = [] def BuildFromFile(self, file): """Parse and build my data from a file Reads the next line in the file, and matches it as a method description. If not a valid method line, an error_not_found exception is raised. """ line = file.readline() mo = self.regex.search(line) if not mo: raise error_not_found self.name = mo.group(4) self.result = mo.group(2) if self.result != "HRESULT": if self.result == "DWORD": # DWORD is for old old stuff? print( "Warning: Old style interface detected - compilation errors likely!" ) else: print( "Method %s - Only HRESULT return types are supported." % self.name ) # raise error_not_supported, if VERBOSE: print(" Method %s %s(" % (self.result, self.name)) while 1: arg = Argument(self.good_interface_names) try: arg.BuildFromFile(file) self.args.append(arg) except error_not_found: break class Interface: """A representation of a C++ COM Interface This class contains information about a specific interface, as well as a list of all @Method@s """ # name base # -------- -------- regex = re.compile("(interface|) ([^ ]*) : public (.*)$") def __init__(self, mo): self.methods = [] self.name = mo.group(2) self.base = mo.group(3) if VERBOSE: print("Interface %s : public %s" % (self.name, self.base)) def BuildMethods(self, file): """Build all sub-methods for this interface""" # skip the next 2 lines. file.readline() file.readline() while 1: try: method = Method([self.name]) method.BuildFromFile(file) self.methods.append(method) except error_not_found: break def find_interface(interfaceName, file): """Find and return an interface in a file Given an interface name and file, search for the specified interface. Upon return, the interface itself has been built, but not the methods. """ interface = None line = file.readline() while line: mo = Interface.regex.search(line) if mo: name = mo.group(2) print(name) AllConverters[name] = (ArgFormatterInterface, 0, 1) if name == interfaceName: interface = Interface(mo) interface.BuildMethods(file) line = file.readline() if interface: return interface raise error_not_found def parse_interface_info(interfaceName, file): """Find, parse and return an interface in a file Given an interface name and file, search for the specified interface. Upon return, the interface itself is fully built, """ try: return find_interface(interfaceName, file) except re.error: traceback.print_exc() print("The interface could not be built, as the regular expression failed!") def test(): f = open("d:\\msdev\\include\\objidl.h") try: parse_interface_info("IPersistStream", f) finally: f.close() def test_regex(r, text): res = r.search(text, 0) if res == -1: print("** Not found") else: print( "%d\n%s\n%s\n%s\n%s" % (res, r.group(1), r.group(2), r.group(3), r.group(4)) )