From 0aa6a65f508f79fc8bcf69a732d922e1e2ee9e56 Mon Sep 17 00:00:00 2001 From: CristiFati Date: Fri, 16 Feb 2024 20:01:58 +0200 Subject: [PATCH 1/4] Improve returning None --- com/win32com/makegw/makegw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com/win32com/makegw/makegw.py b/com/win32com/makegw/makegw.py index 92e3354628..ca5bc63c9f 100644 --- a/com/win32com/makegw/makegw.py +++ b/com/win32com/makegw/makegw.py @@ -319,7 +319,7 @@ def _write_ifc_cpp(f, interface): ) ) else: - f.write("\tPy_INCREF(Py_None);\n\treturn Py_None;\n") + f.write("\tPy_RETURN_NONE;\n") f.write("\n}\n\n") f.write("// @object Py%s|Description of the interface\n" % (name)) From ca2c82de3e0879c2a81fe3797364bf4733c86343 Mon Sep 17 00:00:00 2001 From: CristiFati Date: Fri, 16 Feb 2024 20:57:21 +0200 Subject: [PATCH 2/4] Command line functionality --- com/win32com/makegw/makegw.py | 86 ++++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/com/win32com/makegw/makegw.py b/com/win32com/makegw/makegw.py index ca5bc63c9f..d99fa540a2 100644 --- a/com/win32com/makegw/makegw.py +++ b/com/win32com/makegw/makegw.py @@ -3,9 +3,9 @@ This module will generate a C++/Python binding for a specific COM interface. - At this stage, no command line interface exists. You must start Python, - import this module, change to the directory where the generated code should - be written, and run the public function. + Can be run from command line (passing required arguments) or the old way + (start Python, import this module, change to the directory where the generated code + should be written, and run the public function). This module is capable of generating both 'Interfaces' (ie, Python client side support for the interface) and 'Gateways' (ie, Python @@ -46,13 +46,19 @@ """ +import argparse +import os import re from . import makegwparse def make_framework_support( - header_file_name, interface_name, bMakeInterface=1, bMakeGateway=1 + header_file_name, + interface_name, + bMakeInterface=1, + bMakeGateway=1, + output_directory=None, ): """Generate C++ code for a Python Interface and Gateway @@ -86,7 +92,12 @@ def make_framework_support( ifc_cpp_writer = _write_ifc_cpp gw_cpp_writer = _write_gw_cpp - fout = open("Py%s.cpp" % interface.name, "w") + fout = open( + os.path.join( + directory if directory else os.getcwd(), f"Py{interface.name}.cpp" + ), + "w", + ) try: fout.write( f"""\ @@ -110,7 +121,10 @@ def make_framework_support( gw_cpp_writer(fout, interface) finally: fout.close() - fout = open("Py%s.h" % interface.name, "w") + fout = open( + os.path.join(directory if directory else os.getcwd(), f"Py{interface.name}.h"), + "w", + ) try: fout.write( f"""\ @@ -564,3 +578,63 @@ def test(): # make_framework_support("d:\\msdev\\include\\objidl.h", "IEnumSTATSTG") +# python -m com.win32com.makegw.makegw -f "C:\Windows Kits\10\Include\10.0.19041.0\um\ShObjIdl_core.h" -n IFolderView1 -o com\win32comext\shell\src + + +def parse_arguments(): + parser = argparse.ArgumentParser(description="COM interface wrapper generator") + parser.add_argument( + "--header_file", "-f", required=True, help="header file (system) to parse" + ) + parser.add_argument( + "--interface_name", "-n", required=True, help="interface name to search for" + ) + parser.add_argument( + "--no_create_interface", + "-i", + action="store_false", + dest="create_interface", + help="do not generate interface code", + ) + parser.add_argument( + "--no_create_gateway", + "-g", + action="store_false", + dest="create_gateway", + help="do not generate gateway code", + ) + parser.add_argument( + "--output_directory", "-o", help="directory where to generate files" + ) + + args, unk = parser.parse_known_args() + if unk: + print(f"Warning: Ignoring unknown arguments: {unk}") + + if not args.header_file or not os.path.isfile(args.header_file): + parser.exit(status=-1, message="Invalid header file\n") + if not args.interface_name: + parser.exit(status=-1, message="Invalid interface name\n") + + return ( + args.header_file, + args.interface_name, + args.create_interface, + args.create_gateway, + args.output_directory or None, + ) + + +if __name__ == "__main__": + header_file, interface_name, create_interface, create_gateway, directory = ( + parse_arguments() + ) + if directory: + os.makedirs(directory, exist_ok=True) + make_framework_support( + header_file_name=header_file, + interface_name=interface_name, + bMakeInterface=create_interface, + bMakeGateway=create_gateway, + output_directory=directory, + ) From 8cac0681aca9c39ea1f293092d551499ae4a04cb Mon Sep 17 00:00:00 2001 From: CristiFati Date: Sun, 18 Feb 2024 20:18:23 +0200 Subject: [PATCH 3/4] Add new aliases --- com/win32com/makegw/makegwparse.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/com/win32com/makegw/makegwparse.py b/com/win32com/makegw/makegwparse.py index 63b4692b8a..c699b43ce0 100644 --- a/com/win32com/makegw/makegwparse.py +++ b/com/win32com/makegw/makegwparse.py @@ -759,6 +759,14 @@ def _GetPythonTypeDesc(self): "LPITEMIDLIST": (ArgFormatterIDLIST, 0, 1), "LPCITEMIDLIST": (ArgFormatterIDLIST, 0, 1), "const ITEMIDLIST": (ArgFormatterIDLIST, 0, 1), + "PITEMID_CHILD": (ArgFormatterIDLIST, 1), + "const PITEMID_CHILD": (ArgFormatterIDLIST, 0), + "PCITEMID_CHILD": (ArgFormatterIDLIST, 0), + "PUITEMID_CHILD": (ArgFormatterIDLIST, 1), + "PCUITEMID_CHILD": (ArgFormatterIDLIST, 0), + "const PUITEMID_CHILD": (ArgFormatterIDLIST, 0), + "PCUITEMID_CHILD_ARRAY": (ArgFormatterIDLIST, 2), + "const PCUITEMID_CHILD_ARRAY": (ArgFormatterIDLIST, 2), } # Auto-add all the simple types From 4e8e185c6581ab0521f6088e7fa00c10c8ee670e Mon Sep 17 00:00:00 2001 From: CristiFati Date: Sun, 18 Feb 2024 20:23:56 +0200 Subject: [PATCH 4/4] Improve argument parsing (still doesn't work properly) --- com/win32com/makegw/makegwparse.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/com/win32com/makegw/makegwparse.py b/com/win32com/makegw/makegwparse.py index c699b43ce0..a1dff12ab1 100644 --- a/com/win32com/makegw/makegwparse.py +++ b/com/win32com/makegw/makegwparse.py @@ -804,11 +804,20 @@ class Argument: # -------------- -------- ------------ ------ regex = re.compile(r"/\* \[([^\]]*.*?)] \*/[ \t](.*[* ]+)(\w+)(\[ *])?[\),]") + @classmethod + def drop_rpc_metadata(cls, type_): + return " ".join(e for e in type_.split(" ") if not e.startswith("__RPC_")) + def __init__(self, good_interface_names): self.good_interface_names = good_interface_names - self.inout = self.name = self.type = None + self.inout = None + self.name = None + self.type = None + self.raw_type = None + self.unc_type = None self.const = 0 self.arrayDecl = 0 + self.indirectionLevel = 0 def BuildFromFile(self, file): """Parse and build my data from a file @@ -825,21 +834,17 @@ def BuildFromFile(self, file): 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_no_rpc = self.drop_rpc_metadata(typ) + if typ != typ_no_rpc: + self.indirectionLevel += 1 - typ = typ.replace("__RPC_FAR", "") + typ = self.drop_rpc_metadata(typ) while 1: try: pos = typ.rindex("*") - self.indirectionLevel = self.indirectionLevel + 1 + self.indirectionLevel += 1 typ = typ[:pos].strip() except ValueError: break