0001 """Utility functions for writing out gateway C++ files 0002 0003 This module will generate a C++/Python binding for a specific COM 0004 interface. 0005 0006 At this stage, no command line interface exists. You must start Python, 0007 import this module, change to the directory where the generated code should 0008 be written, and run the public function. 0009 0010 This module is capable of generating both 'Interfaces' (ie, Python 0011 client side support for the interface) and 'Gateways' (ie, Python 0012 server side support for the interface). Many COM interfaces are useful 0013 both as Client and Server. Other interfaces, however, really only make 0014 sense to implement one side or the other. For example, it would be pointless 0015 for Python to implement Server side for 'IRunningObjectTable', unless we were 0016 implementing core COM for an operating system in Python (hey - now there's an idea!) 0017 0018 Most COM interface code is totally boiler-plate - it consists of 0019 converting arguments, dispatching the call to Python, and processing 0020 any result values. 0021 0022 This module automates the generation of such code. It has the ability to 0023 parse a .H file generated by the MIDL tool (ie, almost all COM .h files) 0024 and build almost totally complete C++ code. 0025 0026 The module understands some of the well known data types, and how to 0027 convert them. There are only a couple of places where hand-editing is 0028 necessary, as detailed below: 0029 0030 unsupported types -- If a type is not known, the generator will 0031 pretty much ignore it, but write a comment to the generated code. You 0032 may want to add custom support for this type. In some cases, C++ compile errors 0033 will result. These are intentional - generating code to remove these errors would 0034 imply a false sense of security that the generator has done the right thing. 0035 0036 other return policies -- By default, Python never sees the return SCODE from 0037 a COM function. The interface usually returns None if OK, else a COM exception 0038 if "FAILED(scode)" is TRUE. You may need to change this if: 0039 * EXCEPINFO is passed to the COM function. This is not detected and handled 0040 * For some reason Python should always see the result SCODE, even if it 0041 did fail or succeed. For example, some functions return a BOOLEAN result 0042 in the SCODE, meaning Python should always see it. 0043 * FAILED(scode) for the interface still has valid data to return (by default, 0044 the code generated does not process the return values, and raise an exception 0045 to Python/COM 0046 0047 """ 0048 0049 import regsub 0050 import string 0051 import makegwparse 0052 0053 def make_framework_support(header_file_name, interface_name, bMakeInterface = 1, bMakeGateway = 1): 0054 """Generate C++ code for a Python Interface and Gateway 0055 0056 header_file_name -- The full path to the .H file which defines the interface. 0057 interface_name -- The name of the interface to search for, and to generate. 0058 bMakeInterface = 1 -- Should interface (ie, client) support be generated. 0059 bMakeGatewayInterface = 1 -- Should gateway (ie, server) support be generated. 0060 0061 This method will write a .cpp and .h file into the current directory, 0062 (using the name of the interface to build the file name. 0063 0064 """ 0065 fin=open(header_file_name) 0066 try: 0067 interface = makegwparse.parse_interface_info(interface_name, fin) 0068 finally: 0069 fin.close() 0070 0071 if bMakeInterface and bMakeGateway: 0072 desc = "Interface and Gateway" 0073 elif bMakeInterface and not bMakeGateway: 0074 desc = "Interface" 0075 else: 0076 desc = "Gateway" 0077 if interface.name[:5]=="IEnum": # IEnum - use my really simple template-based one 0078 import win32com.makegw.makegwenum 0079 ifc_cpp_writer = win32com.makegw.makegwenum._write_enumifc_cpp 0080 gw_cpp_writer = win32com.makegw.makegwenum._write_enumgw_cpp 0081 else: # Use my harder working ones. 0082 ifc_cpp_writer = _write_ifc_cpp 0083 gw_cpp_writer = _write_gw_cpp 0084 0085 fout=open("Py%s.cpp" % interface.name, "w") 0086 try: 0087 fout.write(\ 0088 '''\ 0089 // This file implements the %s %s for Python. 0090 // Generated by makegw.py 0091 0092 #include "shell_pch.h" 0093 ''' % (interface.name, desc)) 0094 # if bMakeGateway: 0095 # fout.write('#include "PythonCOMServer.h"\n') 0096 # if interface.base not in ["IUnknown", "IDispatch"]: 0097 # fout.write('#include "Py%s.h"\n' % interface.base) 0098 fout.write('#include "Py%s.h"\n\n// @doc - This file contains autoduck documentation\n' % interface.name) 0099 if bMakeInterface: ifc_cpp_writer(fout, interface) 0100 if bMakeGateway: gw_cpp_writer(fout, interface) 0101 finally: 0102 fout.close() 0103 fout=open("Py%s.h" % interface.name, "w") 0104 try: 0105 fout.write(\ 0106 '''\ 0107 // This file declares the %s %s for Python. 0108 // Generated by makegw.py 0109 ''' % (interface.name, desc)) 0110 0111 if bMakeInterface: _write_ifc_h(fout, interface) 0112 if bMakeGateway: _write_gw_h(fout, interface) 0113 finally: 0114 fout.close() 0115 0116 ########################################################################### 0117 # 0118 # INTERNAL FUNCTIONS 0119 # 0120 # 0121 0122 def _write_ifc_h(f, interface): 0123 f.write(\ 0124 '''\ 0125 // --------------------------------------------------- 0126 // 0127 // Interface Declaration 0128 0129 class Py%s : public Py%s 0130 { 0131 public: 0132 MAKE_PYCOM_CTOR(Py%s); 0133 static %s *GetI(PyObject *self); 0134 static PyComTypeObject type; 0135 0136 // The Python methods 0137 ''' % (interface.name, interface.base, interface.name, interface.name)) 0138 for method in interface.methods: 0139 f.write('\tstatic PyObject *%s(PyObject *self, PyObject *args);\n' % method.name) 0140 f.write(\ 0141 '''\ 0142 0143 protected: 0144 Py%s(IUnknown *pdisp); 0145 ~Py%s(); 0146 }; 0147 ''' % (interface.name, interface.name)) 0148 0149 def _write_ifc_cpp(f, interface): 0150 name = interface.name 0151 f.write(\ 0152 '''\ 0153 // --------------------------------------------------- 0154 // 0155 // Interface Implementation 0156 0157 Py%(name)s::Py%(name)s(IUnknown *pdisp): 0158 Py%(base)s(pdisp) 0159 { 0160 ob_type = &type; 0161 } 0162 0163 Py%(name)s::~Py%(name)s() 0164 { 0165 } 0166 0167 /* static */ %(name)s *Py%(name)s::GetI(PyObject *self) 0168 { 0169 return (%(name)s *)Py%(base)s::GetI(self); 0170 } 0171 0172 ''' % (interface.__dict__)) 0173 0174 ptr = regsub.gsub('[a-z]', '', interface.name) 0175 strdict = {'interfacename':interface.name, 'ptr': ptr} 0176 for method in interface.methods: 0177 strdict['method'] = method.name 0178 f.write(\ 0179 '''\ 0180 // @pymethod |Py%(interfacename)s|%(method)s|Description of %(method)s. 0181 PyObject *Py%(interfacename)s::%(method)s(PyObject *self, PyObject *args) 0182 { 0183 %(interfacename)s *p%(ptr)s = GetI(self); 0184 if ( p%(ptr)s == NULL ) 0185 return NULL; 0186 ''' % strdict) 0187 argsParseTuple = argsCOM = formatChars = codePost = \ 0188 codePobjects = codeCobjects = cleanup = cleanup_gil = "" 0189 needConversion = 0 0190 # if method.name=="Stat": import win32dbg;win32dbg.brk() 0191 for arg in method.args: 0192 try: 0193 argCvt = makegwparse.make_arg_converter(arg) 0194 if arg.HasAttribute("in"): 0195 val = argCvt.GetFormatChar() 0196 if val: 0197 f.write ('\t' + argCvt.GetAutoduckString() + "\n") 0198 formatChars = formatChars + val 0199 argsParseTuple = argsParseTuple + ", " + argCvt.GetParseTupleArg() 0200 codePobjects = codePobjects + argCvt.DeclareParseArgTupleInputConverter() 0201 codePost = codePost + argCvt.GetParsePostCode() 0202 needConversion = needConversion or argCvt.NeedUSES_CONVERSION() 0203 cleanup = cleanup + argCvt.GetInterfaceArgCleanup() 0204 cleanup_gil = cleanup_gil + argCvt.GetInterfaceArgCleanupGIL() 0205 comArgName, comArgDeclString = argCvt.GetInterfaceCppObjectInfo() 0206 if comArgDeclString: # If we should declare a variable 0207 codeCobjects = codeCobjects + "\t%s;\n" % (comArgDeclString) 0208 argsCOM = argsCOM + ", " + comArgName 0209 except makegwparse.error_not_supported, why: 0210 f.write('// *** The input argument %s of type "%s" was not processed ***\n// Please check the conversion function is appropriate and exists!\n' % (arg.name, arg.raw_type)) 0211 0212 f.write('\t%s %s;\n\tPyObject *ob%s;\n' % (arg.type, arg.name, arg.name)) 0213 f.write('\t// @pyparm <o Py%s>|%s||Description for %s\n' % (arg.type, arg.name, arg.name)) 0214 codePost = codePost + '\tif (bPythonIsHappy && !PyObject_As%s( ob%s, &%s )) bPythonIsHappy = FALSE;\n' % (arg.type, arg.name, arg.name) 0215 0216 formatChars = formatChars + "O" 0217 argsParseTuple = argsParseTuple + ", &ob%s" % (arg.name) 0218 0219 argsCOM = argsCOM + ", " + arg.name 0220 cleanup = cleanup + "\tPyObject_Free%s(%s);\n" % (arg.type, arg.name) 0221 0222 if needConversion: f.write("\tUSES_CONVERSION;\n") 0223 f.write(codePobjects); 0224 f.write(codeCobjects); 0225 f.write('\tif ( !PyArg_ParseTuple(args, "%s:%s"%s) )\n\t\treturn NULL;\n' % (formatChars, method.name, argsParseTuple)) 0226 if codePost: 0227 f.write('\tBOOL bPythonIsHappy = TRUE;\n') 0228 f.write(codePost); 0229 f.write('\tif (!bPythonIsHappy) return NULL;\n') 0230 strdict['argsCOM'] = argsCOM[1:] 0231 strdict['cleanup'] = cleanup 0232 strdict['cleanup_gil'] = cleanup_gil 0233 f.write(\ 0234 ''' HRESULT hr; 0235 PY_INTERFACE_PRECALL; 0236 hr = p%(ptr)s->%(method)s(%(argsCOM)s ); 0237 %(cleanup)s 0238 PY_INTERFACE_POSTCALL; 0239 %(cleanup_gil)s 0240 if ( FAILED(hr) ) 0241 return PyCom_BuildPyException(hr, p%(ptr)s, IID_%(interfacename)s ); 0242 ''' % strdict) 0243 codePre = codePost = formatChars = codeVarsPass = codeDecl = "" 0244 for arg in method.args: 0245 if not arg.HasAttribute("out"): 0246 continue 0247 try: 0248 argCvt = makegwparse.make_arg_converter(arg) 0249 formatChar = argCvt.GetFormatChar() 0250 if formatChar: 0251 formatChars = formatChars + formatChar 0252 codePre = codePre + argCvt.GetBuildForInterfacePreCode() 0253 codePost = codePost + argCvt.GetBuildForInterfacePostCode() 0254 codeVarsPass = codeVarsPass + ", " + argCvt.GetBuildValueArg() 0255 codeDecl = codeDecl + argCvt.DeclareParseArgTupleInputConverter() 0256 except makegwparse.error_not_supported, why: 0257 f.write('// *** The output argument %s of type "%s" was not processed ***\n// %s\n' % (arg.name, arg.raw_type, why)) 0258 continue 0259 if formatChars: 0260 f.write('%s\n%s\tPyObject *pyretval = Py_BuildValue("%s"%s);\n%s\treturn pyretval;' % (codeDecl, codePre, formatChars, codeVarsPass, codePost)) 0261 else: 0262 f.write('\tPy_INCREF(Py_None);\n\treturn Py_None;\n') 0263 f.write('\n}\n\n') 0264 0265 f.write ('// @object Py%s|Description of the interface\n' % (name)) 0266 f.write('static struct PyMethodDef Py%s_methods[] =\n{\n' % name) 0267 for method in interface.methods: 0268 f.write('\t{ "%s", Py%s::%s, 1 }, // @pymeth %s|Description of %s\n' % (method.name, interface.name, method.name, method.name, method.name)) 0269 0270 interfacebase = interface.base 0271 f.write('''\ 0272 { NULL } 0273 }; 0274 0275 PyComTypeObject Py%(name)s::type("Py%(name)s", 0276 &Py%(interfacebase)s::type, 0277 sizeof(Py%(name)s), 0278 Py%(name)s_methods, 0279 GET_PYCOM_CTOR(Py%(name)s)); 0280 ''' % locals()) 0281 0282 def _write_gw_h(f, interface): 0283 if interface.name[0] == "I": 0284 gname = 'PyG' + interface.name[1:] 0285 else: 0286 gname = 'PyG' + interface.name 0287 name = interface.name 0288 if interface.base == "IUnknown" or interface.base == "IDispatch": 0289 base_name = "PyGatewayBase" 0290 else: 0291 if interface.base[0] == "I": 0292 base_name = 'PyG' + interface.base[1:] 0293 else: 0294 base_name = 'PyG' + interface.base 0295 f.write(\ 0296 '''\ 0297 // --------------------------------------------------- 0298 // 0299 // Gateway Declaration 0300 0301 class %s : public %s, public %s 0302 { 0303 protected: 0304 %s(PyObject *instance) : %s(instance) { ; } 0305 PYGATEWAY_MAKE_SUPPORT2(%s, %s, IID_%s, %s) 0306 0307 ''' % (gname, base_name, name, gname, base_name, gname, name, name, base_name)) 0308 if interface.base != "IUnknown": 0309 f.write("\t// %s\n\t// *** Manually add %s method decls here\n\n" % (interface.base, interface.base)) 0310 else: 0311 f.write('\n\n') 0312 0313 f.write("\t// %s\n" % name) 0314 0315 for method in interface.methods: 0316 f.write('\tSTDMETHOD(%s)(\n' % method.name) 0317 if method.args: 0318 for arg in method.args[:-1]: 0319 f.write("\t\t%s,\n" % (arg.GetRawDeclaration())) 0320 arg = method.args[-1] 0321 f.write("\t\t%s);\n\n" % (arg.GetRawDeclaration())) 0322 else: 0323 f.write('\t\tvoid);\n\n') 0324 0325 f.write('};\n') 0326 f.close() 0327 0328 def _write_gw_cpp(f, interface): 0329 if interface.name[0] == "I": 0330 gname = 'PyG' + interface.name[1:] 0331 else: 0332 gname = 'PyG' + interface.name 0333 name = interface.name 0334 if interface.base == "IUnknown" or interface.base == "IDispatch": 0335 base_name = "PyGatewayBase" 0336 else: 0337 if interface.base[0] == "I": 0338 base_name = 'PyG' + interface.base[1:] 0339 else: 0340 base_name = 'PyG' + interface.base 0341 f.write('''\ 0342 // --------------------------------------------------- 0343 // 0344 // Gateway Implementation 0345 ''' % {'name':name, 'gname':gname, 'base_name':base_name}) 0346 0347 for method in interface.methods: 0348 f.write(\ 0349 '''\ 0350 STDMETHODIMP %s::%s( 0351 ''' % (gname, method.name)) 0352 0353 if method.args: 0354 for arg in method.args[:-1]: 0355 inoutstr = string.join(arg.inout, '][') 0356 f.write("\t\t/* [%s] */ %s,\n" % (inoutstr, arg.GetRawDeclaration())) 0357 0358 arg = method.args[-1] 0359 inoutstr = string.join(arg.inout, '][') 0360 f.write("\t\t/* [%s] */ %s)\n" % (inoutstr, arg.GetRawDeclaration())) 0361 else: 0362 f.write('\t\tvoid)\n') 0363 0364 f.write("{\n\tPY_GATEWAY_METHOD;\n") 0365 cout = 0 0366 codePre = codePost = codeVars = "" 0367 argStr = "" 0368 needConversion = 0 0369 formatChars = "" 0370 if method.args: 0371 for arg in method.args: 0372 if arg.HasAttribute("out"): 0373 cout = cout + 1 0374 if arg.indirectionLevel ==2 : 0375 f.write("\tif (%s==NULL) return E_POINTER;\n" % arg.name) 0376 if arg.HasAttribute("in"): 0377 try: 0378 argCvt = makegwparse.make_arg_converter(arg) 0379 argCvt.SetGatewayMode() 0380 formatchar = argCvt.GetFormatChar(); 0381 needConversion = needConversion or argCvt.NeedUSES_CONVERSION() 0382 0383 if formatchar: 0384 formatChars = formatChars + formatchar 0385 codeVars = codeVars + argCvt.DeclareParseArgTupleInputConverter() 0386 argStr = argStr + ", " + argCvt.GetBuildValueArg() 0387 codePre = codePre + argCvt.GetBuildForGatewayPreCode() 0388 codePost = codePost + argCvt.GetBuildForGatewayPostCode() 0389 except makegwparse.error_not_supported, why: 0390 f.write('// *** The input argument %s of type "%s" was not processed ***\n// - Please ensure this conversion function exists, and is appropriate\n// - %s\n' % (arg.name, arg.raw_type, why)) 0391 f.write('\tPyObject *ob%s = PyObject_From%s(%s);\n' % (arg.name, arg.type, arg.name)) 0392 f.write('\tif (ob%s==NULL) return PyCom_HandlePythonFailureToCOM();\n' % arg.name) 0393 codePost = codePost + "\tPy_DECREF(ob%s);\n" % arg.name 0394 formatChars = formatChars + "O" 0395 argStr = argStr + ", ob%s" % (arg.name) 0396 0397 if needConversion: f.write('\tUSES_CONVERSION;\n') 0398 f.write(codeVars) 0399 f.write(codePre) 0400 if cout: 0401 f.write("\tPyObject *result;\n") 0402 resStr = "&result" 0403 else: 0404 resStr = "NULL" 0405 0406 if formatChars: 0407 fullArgStr = '%s, "%s"%s' % (resStr, formatChars, argStr) 0408 else: 0409 fullArgStr = resStr 0410 0411 f.write('\tHRESULT hr=InvokeViaPolicy("%s", %s);\n' % (method.name, fullArgStr)) 0412 f.write(codePost) 0413 if cout: 0414 f.write("\tif (FAILED(hr)) return hr;\n") 0415 f.write("\t// Process the Python results, and convert back to the real params\n") 0416 # process the output arguments. 0417 formatChars = codePobjects = codePost = argsParseTuple = "" 0418 needConversion = 0 0419 for arg in method.args: 0420 if not arg.HasAttribute("out"): 0421 continue 0422 try: 0423 argCvt = makegwparse.make_arg_converter(arg) 0424 argCvt.SetGatewayMode() 0425 val = argCvt.GetFormatChar() 0426 if val: 0427 formatChars = formatChars + val 0428 argsParseTuple = argsParseTuple + ", " + argCvt.GetParseTupleArg() 0429 codePobjects = codePobjects + argCvt.DeclareParseArgTupleInputConverter() 0430 codePost = codePost + argCvt.GetParsePostCode() 0431 needConversion = needConversion or argCvt.NeedUSES_CONVERSION() 0432 except makegwparse.error_not_supported, why: 0433 f.write('// *** The output argument %s of type "%s" was not processed ***\n// %s\n' % (arg.name, arg.raw_type, why)) 0434 0435 if formatChars: # If I have any to actually process. 0436 if len(formatChars)==1: 0437 parseFn = "PyArg_Parse" 0438 else: 0439 parseFn = "PyArg_ParseTuple" 0440 if codePobjects: f.write(codePobjects) 0441 f.write('\tif (!%s(result, "%s" %s)) return PyCom_HandlePythonFailureToCOM(/*pexcepinfo*/);\n' % (parseFn, formatChars, argsParseTuple)) 0442 if codePost: 0443 f.write('\tBOOL bPythonIsHappy = TRUE;\n') 0444 f.write(codePost) 0445 f.write('\tif (!bPythonIsHappy) hr = PyCom_HandlePythonFailureToCOM(/*pexcepinfo*/);\n') 0446 f.write('\tPy_DECREF(result);\n'); 0447 f.write('\treturn hr;\n}\n\n') 0448 0449 def test(): 0450 # make_framework_support("d:\\msdev\\include\\objidl.h", "ILockBytes") 0451 make_framework_support("d:\\msdev\\include\\objidl.h", "IStorage") 0452 # make_framework_support("d:\\msdev\\include\\objidl.h", "IEnumSTATSTG") 0453
Generated by PyXR 0.9.4