1009 lines
34 KiB
Python
1009 lines
34 KiB
Python
"""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 "<o Py%s>" % 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 "<o unicode>"
|
|
|
|
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 "<o unicode>"
|
|
|
|
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/<o unicode>"
|
|
|
|
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 "<o PyIID>"
|
|
|
|
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 "<o PyDateTime>"
|
|
|
|
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 "<o STATSTG>"
|
|
|
|
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 "<o %s>" % 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 "<o PyIDL>"
|
|
|
|
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 "<o PyHANDLE>"
|
|
|
|
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 "<o %s>" % 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))
|
|
)
|