Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
makegw.py561 linesDownload Raw Back to makegw
1"""Utility functions for writing out gateway C++ files2 3  This module will generate a C++/Python binding for a specific COM4  interface.5  6  At this stage, no command line interface exists.  You must start Python, 7  import this module,  change to the directory where the generated code should8  be written, and run the public function.9  10  This module is capable of generating both 'Interfaces' (ie, Python11  client side support for the interface) and 'Gateways' (ie, Python12  server side support for the interface).  Many COM interfaces are useful13  both as Client and Server.  Other interfaces, however, really only make14  sense to implement one side or the other.  For example, it would be pointless15  for Python to implement Server side for 'IRunningObjectTable', unless we were16  implementing core COM for an operating system in Python (hey - now there's an idea!)17  18  Most COM interface code is totally boiler-plate - it consists of19  converting arguments, dispatching the call to Python, and processing20  any result values.21  22  This module automates the generation of such code.  It has the ability to23  parse a .H file generated by the MIDL tool (ie, almost all COM .h files)24  and build almost totally complete C++ code.25  26  The module understands some of the well known data types, and how to27  convert them.  There are only a couple of places where hand-editing is28  necessary, as detailed below:29 30  unsupported types -- If a type is not known, the generator will31  pretty much ignore it, but write a comment to the generated code.  You32  may want to add custom support for this type.  In some cases, C++ compile errors33  will result.  These are intentional - generating code to remove these errors would34  imply a false sense of security that the generator has done the right thing.35 36  other return policies -- By default, Python never sees the return SCODE from37  a COM function.  The interface usually returns None if OK, else a COM exception38  if "FAILED(scode)" is TRUE.  You may need to change this if:39  * EXCEPINFO is passed to the COM function.  This is not detected and handled40  * For some reason Python should always see the result SCODE, even if it41    did fail or succeed.  For example, some functions return a BOOLEAN result42    in the SCODE, meaning Python should always see it.43  * FAILED(scode) for the interface still has valid data to return (by default,44    the code generated does not process the return values, and raise an exception45    to Python/COM46  47"""48 49import re50 51from . import makegwparse52 53 54def make_framework_support(55    header_file_name, interface_name, bMakeInterface=1, bMakeGateway=156):57    """Generate C++ code for a Python Interface and Gateway58 59    header_file_name -- The full path to the .H file which defines the interface.60    interface_name -- The name of the interface to search for, and to generate.61    bMakeInterface = 1 -- Should interface (ie, client) support be generated.62    bMakeGatewayInterface = 1 -- Should gateway (ie, server) support be generated.63 64    This method will write a .cpp and .h file into the current directory,65    (using the name of the interface to build the file name.66 67    """68    fin = open(header_file_name)69    try:70        interface = makegwparse.parse_interface_info(interface_name, fin)71    finally:72        fin.close()73 74    if bMakeInterface and bMakeGateway:75        desc = "Interface and Gateway"76    elif bMakeInterface and not bMakeGateway:77        desc = "Interface"78    else:79        desc = "Gateway"80    if interface.name[:5] == "IEnum":  # IEnum - use my really simple template-based one81        import win32com.makegw.makegwenum82 83        ifc_cpp_writer = win32com.makegw.makegwenum._write_enumifc_cpp84        gw_cpp_writer = win32com.makegw.makegwenum._write_enumgw_cpp85    else:  # Use my harder working ones.86        ifc_cpp_writer = _write_ifc_cpp87        gw_cpp_writer = _write_gw_cpp88 89    fout = open("Py%s.cpp" % interface.name, "w")90    try:91        fout.write(92            """\93// This file implements the %s %s for Python.94// Generated by makegw.py95 96#include "shell_pch.h"97"""98            % (interface.name, desc)99        )100        #    if bMakeGateway:101        #      fout.write('#include "PythonCOMServer.h"\n')102        #    if interface.base not in ["IUnknown", "IDispatch"]:103        #      fout.write('#include "Py%s.h"\n' % interface.base)104        fout.write(105            '#include "Py%s.h"\n\n// @doc - This file contains autoduck documentation\n'106            % interface.name107        )108        if bMakeInterface:109            ifc_cpp_writer(fout, interface)110        if bMakeGateway:111            gw_cpp_writer(fout, interface)112    finally:113        fout.close()114    fout = open("Py%s.h" % interface.name, "w")115    try:116        fout.write(117            """\118// This file declares the %s %s for Python.119// Generated by makegw.py120"""121            % (interface.name, desc)122        )123 124        if bMakeInterface:125            _write_ifc_h(fout, interface)126        if bMakeGateway:127            _write_gw_h(fout, interface)128    finally:129        fout.close()130 131 132###########################################################################133#134# INTERNAL FUNCTIONS135#136#137 138 139def _write_ifc_h(f, interface):140    f.write(141        """\142// ---------------------------------------------------143//144// Interface Declaration145 146class Py%s : public Py%s147{148public:149	MAKE_PYCOM_CTOR(Py%s);150	static %s *GetI(PyObject *self);151	static PyComTypeObject type;152 153	// The Python methods154"""155        % (interface.name, interface.base, interface.name, interface.name)156    )157    for method in interface.methods:158        f.write(159            "\tstatic PyObject *%s(PyObject *self, PyObject *args);\n" % method.name160        )161    f.write(162        """\163 164protected:165	Py%s(IUnknown *pdisp);166	~Py%s();167};168"""169        % (interface.name, interface.name)170    )171 172 173def _write_ifc_cpp(f, interface):174    name = interface.name175    f.write(176        """\177// ---------------------------------------------------178//179// Interface Implementation180 181Py%(name)s::Py%(name)s(IUnknown *pdisp):182	Py%(base)s(pdisp)183{184	ob_type = &type;185}186 187Py%(name)s::~Py%(name)s()188{189}190 191/* static */ %(name)s *Py%(name)s::GetI(PyObject *self)192{193	return (%(name)s *)Py%(base)s::GetI(self);194}195 196"""197        % (interface.__dict__)198    )199 200    ptr = re.sub("[a-z]", "", interface.name)201    strdict = {"interfacename": interface.name, "ptr": ptr}202    for method in interface.methods:203        strdict["method"] = method.name204        f.write(205            """\206// @pymethod |Py%(interfacename)s|%(method)s|Description of %(method)s.207PyObject *Py%(interfacename)s::%(method)s(PyObject *self, PyObject *args)208{209	%(interfacename)s *p%(ptr)s = GetI(self);210	if ( p%(ptr)s == NULL )211		return NULL;212"""213            % strdict214        )215        argsParseTuple = (216            argsCOM217        ) = (218            formatChars219        ) = codePost = codePobjects = codeCobjects = cleanup = cleanup_gil = ""220        needConversion = 0221        #    if method.name=="Stat": import win32dbg;win32dbg.brk()222        for arg in method.args:223            try:224                argCvt = makegwparse.make_arg_converter(arg)225                if arg.HasAttribute("in"):226                    val = argCvt.GetFormatChar()227                    if val:228                        f.write("\t" + argCvt.GetAutoduckString() + "\n")229                        formatChars = formatChars + val230                        argsParseTuple = (231                            argsParseTuple + ", " + argCvt.GetParseTupleArg()232                        )233                        codePobjects = (234                            codePobjects + argCvt.DeclareParseArgTupleInputConverter()235                        )236                        codePost = codePost + argCvt.GetParsePostCode()237                        needConversion = needConversion or argCvt.NeedUSES_CONVERSION()238                        cleanup = cleanup + argCvt.GetInterfaceArgCleanup()239                        cleanup_gil = cleanup_gil + argCvt.GetInterfaceArgCleanupGIL()240                comArgName, comArgDeclString = argCvt.GetInterfaceCppObjectInfo()241                if comArgDeclString:  # If we should declare a variable242                    codeCobjects = codeCobjects + "\t%s;\n" % (comArgDeclString)243                argsCOM = argsCOM + ", " + comArgName244            except makegwparse.error_not_supported as why:245                f.write(246                    '// *** The input argument %s of type "%s" was not processed ***\n//     Please check the conversion function is appropriate and exists!\n'247                    % (arg.name, arg.raw_type)248                )249 250                f.write(251                    "\t%s %s;\n\tPyObject *ob%s;\n" % (arg.type, arg.name, arg.name)252                )253                f.write(254                    "\t// @pyparm <o Py%s>|%s||Description for %s\n"255                    % (arg.type, arg.name, arg.name)256                )257                codePost = (258                    codePost259                    + "\tif (bPythonIsHappy && !PyObject_As%s( ob%s, &%s )) bPythonIsHappy = FALSE;\n"260                    % (arg.type, arg.name, arg.name)261                )262 263                formatChars = formatChars + "O"264                argsParseTuple = argsParseTuple + ", &ob%s" % (arg.name)265 266                argsCOM = argsCOM + ", " + arg.name267                cleanup = cleanup + "\tPyObject_Free%s(%s);\n" % (arg.type, arg.name)268 269        if needConversion:270            f.write("\tUSES_CONVERSION;\n")271        f.write(codePobjects)272        f.write(codeCobjects)273        f.write(274            '\tif ( !PyArg_ParseTuple(args, "%s:%s"%s) )\n\t\treturn NULL;\n'275            % (formatChars, method.name, argsParseTuple)276        )277        if codePost:278            f.write("\tBOOL bPythonIsHappy = TRUE;\n")279            f.write(codePost)280            f.write("\tif (!bPythonIsHappy) return NULL;\n")281        strdict["argsCOM"] = argsCOM[1:]282        strdict["cleanup"] = cleanup283        strdict["cleanup_gil"] = cleanup_gil284        f.write(285            """	HRESULT hr;286	PY_INTERFACE_PRECALL;287	hr = p%(ptr)s->%(method)s(%(argsCOM)s );288%(cleanup)s289	PY_INTERFACE_POSTCALL;290%(cleanup_gil)s291	if ( FAILED(hr) )292		return PyCom_BuildPyException(hr, p%(ptr)s, IID_%(interfacename)s );293"""294            % strdict295        )296        codePre = codePost = formatChars = codeVarsPass = codeDecl = ""297        for arg in method.args:298            if not arg.HasAttribute("out"):299                continue300            try:301                argCvt = makegwparse.make_arg_converter(arg)302                formatChar = argCvt.GetFormatChar()303                if formatChar:304                    formatChars = formatChars + formatChar305                    codePre = codePre + argCvt.GetBuildForInterfacePreCode()306                    codePost = codePost + argCvt.GetBuildForInterfacePostCode()307                    codeVarsPass = codeVarsPass + ", " + argCvt.GetBuildValueArg()308                    codeDecl = codeDecl + argCvt.DeclareParseArgTupleInputConverter()309            except makegwparse.error_not_supported as why:310                f.write(311                    '// *** The output argument %s of type "%s" was not processed ***\n//     %s\n'312                    % (arg.name, arg.raw_type, why)313                )314                continue315        if formatChars:316            f.write(317                '%s\n%s\tPyObject *pyretval = Py_BuildValue("%s"%s);\n%s\treturn pyretval;'318                % (codeDecl, codePre, formatChars, codeVarsPass, codePost)319            )320        else:321            f.write("\tPy_INCREF(Py_None);\n\treturn Py_None;\n")322        f.write("\n}\n\n")323 324    f.write("// @object Py%s|Description of the interface\n" % (name))325    f.write("static struct PyMethodDef Py%s_methods[] =\n{\n" % name)326    for method in interface.methods:327        f.write(328            '\t{ "%s", Py%s::%s, 1 }, // @pymeth %s|Description of %s\n'329            % (method.name, interface.name, method.name, method.name, method.name)330        )331 332    interfacebase = interface.base333    f.write(334        """\335	{ NULL }336};337 338PyComTypeObject Py%(name)s::type("Py%(name)s",339		&Py%(interfacebase)s::type,340		sizeof(Py%(name)s),341		Py%(name)s_methods,342		GET_PYCOM_CTOR(Py%(name)s));343"""344        % locals()345    )346 347 348def _write_gw_h(f, interface):349    if interface.name[0] == "I":350        gname = "PyG" + interface.name[1:]351    else:352        gname = "PyG" + interface.name353    name = interface.name354    if interface.base == "IUnknown" or interface.base == "IDispatch":355        base_name = "PyGatewayBase"356    else:357        if interface.base[0] == "I":358            base_name = "PyG" + interface.base[1:]359        else:360            base_name = "PyG" + interface.base361    f.write(362        """\363// ---------------------------------------------------364//365// Gateway Declaration366 367class %s : public %s, public %s368{369protected:370	%s(PyObject *instance) : %s(instance) { ; }371	PYGATEWAY_MAKE_SUPPORT2(%s, %s, IID_%s, %s)372 373"""374        % (gname, base_name, name, gname, base_name, gname, name, name, base_name)375    )376    if interface.base != "IUnknown":377        f.write(378            "\t// %s\n\t// *** Manually add %s method decls here\n\n"379            % (interface.base, interface.base)380        )381    else:382        f.write("\n\n")383 384    f.write("\t// %s\n" % name)385 386    for method in interface.methods:387        f.write("\tSTDMETHOD(%s)(\n" % method.name)388        if method.args:389            for arg in method.args[:-1]:390                f.write("\t\t%s,\n" % (arg.GetRawDeclaration()))391            arg = method.args[-1]392            f.write("\t\t%s);\n\n" % (arg.GetRawDeclaration()))393        else:394            f.write("\t\tvoid);\n\n")395 396    f.write("};\n")397    f.close()398 399 400def _write_gw_cpp(f, interface):401    if interface.name[0] == "I":402        gname = "PyG" + interface.name[1:]403    else:404        gname = "PyG" + interface.name405    name = interface.name406    if interface.base == "IUnknown" or interface.base == "IDispatch":407        base_name = "PyGatewayBase"408    else:409        if interface.base[0] == "I":410            base_name = "PyG" + interface.base[1:]411        else:412            base_name = "PyG" + interface.base413    f.write(414        """\415// ---------------------------------------------------416//417// Gateway Implementation418"""419        % {"name": name, "gname": gname, "base_name": base_name}420    )421 422    for method in interface.methods:423        f.write(424            """\425STDMETHODIMP %s::%s(426"""427            % (gname, method.name)428        )429 430        if method.args:431            for arg in method.args[:-1]:432                inoutstr = "][".join(arg.inout)433                f.write("\t\t/* [%s] */ %s,\n" % (inoutstr, arg.GetRawDeclaration()))434 435            arg = method.args[-1]436            inoutstr = "][".join(arg.inout)437            f.write("\t\t/* [%s] */ %s)\n" % (inoutstr, arg.GetRawDeclaration()))438        else:439            f.write("\t\tvoid)\n")440 441        f.write("{\n\tPY_GATEWAY_METHOD;\n")442        cout = 0443        codePre = codePost = codeVars = ""444        argStr = ""445        needConversion = 0446        formatChars = ""447        if method.args:448            for arg in method.args:449                if arg.HasAttribute("out"):450                    cout = cout + 1451                    if arg.indirectionLevel == 2:452                        f.write("\tif (%s==NULL) return E_POINTER;\n" % arg.name)453                if arg.HasAttribute("in"):454                    try:455                        argCvt = makegwparse.make_arg_converter(arg)456                        argCvt.SetGatewayMode()457                        formatchar = argCvt.GetFormatChar()458                        needConversion = needConversion or argCvt.NeedUSES_CONVERSION()459 460                        if formatchar:461                            formatChars = formatChars + formatchar462                            codeVars = (463                                codeVars + argCvt.DeclareParseArgTupleInputConverter()464                            )465                            argStr = argStr + ", " + argCvt.GetBuildValueArg()466                        codePre = codePre + argCvt.GetBuildForGatewayPreCode()467                        codePost = codePost + argCvt.GetBuildForGatewayPostCode()468                    except makegwparse.error_not_supported as why:469                        f.write(470                            '// *** The input argument %s of type "%s" was not processed ***\n//   - Please ensure this conversion function exists, and is appropriate\n//   - %s\n'471                            % (arg.name, arg.raw_type, why)472                        )473                        f.write(474                            "\tPyObject *ob%s = PyObject_From%s(%s);\n"475                            % (arg.name, arg.type, arg.name)476                        )477                        f.write(478                            '\tif (ob%s==NULL) return MAKE_PYCOM_GATEWAY_FAILURE_CODE("%s");\n'479                            % (arg.name, method.name)480                        )481                        codePost = codePost + "\tPy_DECREF(ob%s);\n" % arg.name482                        formatChars = formatChars + "O"483                        argStr = argStr + ", ob%s" % (arg.name)484 485        if needConversion:486            f.write("\tUSES_CONVERSION;\n")487        f.write(codeVars)488        f.write(codePre)489        if cout:490            f.write("\tPyObject *result;\n")491            resStr = "&result"492        else:493            resStr = "NULL"494 495        if formatChars:496            fullArgStr = '%s, "%s"%s' % (resStr, formatChars, argStr)497        else:498            fullArgStr = resStr499 500        f.write('\tHRESULT hr=InvokeViaPolicy("%s", %s);\n' % (method.name, fullArgStr))501        f.write(codePost)502        if cout:503            f.write("\tif (FAILED(hr)) return hr;\n")504            f.write(505                "\t// Process the Python results, and convert back to the real params\n"506            )507            # process the output arguments.508            formatChars = codePobjects = codePost = argsParseTuple = ""509            needConversion = 0510            for arg in method.args:511                if not arg.HasAttribute("out"):512                    continue513                try:514                    argCvt = makegwparse.make_arg_converter(arg)515                    argCvt.SetGatewayMode()516                    val = argCvt.GetFormatChar()517                    if val:518                        formatChars = formatChars + val519                        argsParseTuple = (520                            argsParseTuple + ", " + argCvt.GetParseTupleArg()521                        )522                        codePobjects = (523                            codePobjects + argCvt.DeclareParseArgTupleInputConverter()524                        )525                        codePost = codePost + argCvt.GetParsePostCode()526                        needConversion = needConversion or argCvt.NeedUSES_CONVERSION()527                except makegwparse.error_not_supported as why:528                    f.write(529                        '// *** The output argument %s of type "%s" was not processed ***\n//     %s\n'530                        % (arg.name, arg.raw_type, why)531                    )532 533            if formatChars:  # If I have any to actually process.534                if len(formatChars) == 1:535                    parseFn = "PyArg_Parse"536                else:537                    parseFn = "PyArg_ParseTuple"538                if codePobjects:539                    f.write(codePobjects)540                f.write(541                    '\tif (!%s(result, "%s" %s))\n\t\treturn MAKE_PYCOM_GATEWAY_FAILURE_CODE("%s");\n'542                    % (parseFn, formatChars, argsParseTuple, method.name)543                )544            if codePost:545                f.write("\tBOOL bPythonIsHappy = TRUE;\n")546                f.write(codePost)547                f.write(548                    '\tif (!bPythonIsHappy) hr = MAKE_PYCOM_GATEWAY_FAILURE_CODE("%s");\n'549                    % method.name550                )551            f.write("\tPy_DECREF(result);\n")552        f.write("\treturn hr;\n}\n\n")553 554 555def test():556    # 	make_framework_support("d:\\msdev\\include\\objidl.h", "ILockBytes")557    make_framework_support("d:\\msdev\\include\\objidl.h", "IStorage")558 559 560# 	make_framework_support("d:\\msdev\\include\\objidl.h", "IEnumSTATSTG")561 
codekingpro/portable-devtools · Team Ai