Using Python COM objects from C++

Alex Martelli aleaxit at yahoo.com
Fri Jan 12 07:34:15 EST 2001


"Syver Enstad" <syver at NOSPAMcyberwatcher.com> wrote in message
news:93lsgt$q72$1 at troll.powertech.no...
    [snip]
> > I know how to call the python COM object using "pure" IDispatch, but is
it
> > possible to make a type library and ensure that the Python class
> > implementing the COM complies with it?
> >
> > The problem would be the dispatch id's as far as I can see. I don't know
> > how to specify the dispatch id's that the method's in the python object
> > should have.
> >
> I found an okay solution.
>
> Make your python com object just as you use to, but:
>
> Make the class variable _public_methods_ empty, like this.
> _public_methods = []
>
> Make a class variable _dispid_to_func_ that is a dictionary mapping
dispids
> to function names. Like this:
>     _dispid_to_func_ = {0x1:'Dummy', 0x2:'Swear'}
>
> If you want to implement a dispinterface from a typelibrary/idl file
adjust
> the _dispid_to_func_ dictionary so it matches the idl.

Right!  This IS one way to achieve the results you desire.

However, you can save some boring manual work by letting makepy (and other
allied tools) generate the Python 'skeletons' for you, directly from the
information that is contained in the type library.

Let's try a simple example.  Say we have an IDL file, SAMPATL.IDL:

import "oaidl.idl";
import "ocidl.idl";

[
    uuid(EB8DED71-E882-11D4-9E4C-0060B0EB1D67),
    version(1.0),
    helpstring("A Sample Type Library")
]
library SAMPATLLib
{
    importlib("stdole32.tlb");
    importlib("stdole2.tlb");

    [
        uuid(EB8DED7F-E882-11D4-9E4C-0060B0EB1D67),
        helpstring("A Sample DispInterface")
    ]
    dispinterface MyInterface
    {
        properties:
        methods:
        [id(1), helpstring("a method")] HRESULT
            AMethod([in]LONG aLong);
        [id(2), helpstring("another method")] HRESULT
            AnotherMethod([in]BSTR aString, [out, retval]BSTR* theResult);
        [propget, id(3), helpstring("a read-only property")] HRESULT
            ReadOnly([out, retval] double *pVal);
        [propget, id(5), helpstring("a read-write property")] HRESULT
            AProperty([out, retval] DATE *pVal);
        [propput, id(5)] HRESULT
            AProperty([in] DATE newVal);
    };
};

We compile this with MIDL to get SAMPATL.TLB.  Then we run something like:

D:\experiments\sampatl>python \python20\win32com\client\makepy.py
sampatl.tlb
Generating to
d:\python20\win32com\gen_py\EB8DED71-E882-11D4-9E4C-0060B0EB1D67x0x1x0.py

Examining the python-file which we've just been told has been generated,
we'll see, among a few other things, something like:

class MyInterface(DispatchBaseClass):
    """A Sample DispInterface"""
    CLSID = pythoncom.MakeIID('{EB8DED7F-E882-11D4-9E4C-0060B0EB1D67}')

    def AMethod(self, aLong=defaultNamedNotOptArg):
        """a method"""
        return self._oleobj_.InvokeTypes(0x1, LCID, 1, (3, 0), ((3,
1),),aLong)

    def AnotherMethod(self, aString=defaultNamedNotOptArg,
theResult=defaultNamedNotOptArg):
        """another method"""
        return self._ApplyTypes_(0x2, 1, (3, 0), ((8, 1), (16392, 10)),
'AnotherMethod', None,aString, theResult)

    _prop_map_get_ = {
        "AProperty": (5, 2, (3, 0), ((16391, 10),), "AProperty", None),
        "ReadOnly": (3, 2, (3, 0), ((16389, 10),), "ReadOnly", None),
    }
    _prop_map_put_ = {
        "AProperty": ((5, LCID, 4, 0),()),
    }

Not _quite_ what you need, but, getting there.  I find it often helpful to
have the CLSID, helpstrings, argument names, etc, all present -- the
dispatch ID's you need are also here, though not evidently so (the first
arguments to InvokeTypes and _ApplyTypes_ call, first elements in the
tuples in the maps for get and put properties), so you need not consult
the IDL or other docs to double-check them.

Massaging the makepy-generated Python code, or imitating makepy and friends
into a similar tool that generates (ideally decently-named:-) python
sources that are immediately suitable to inherit from, is left as an
exercise to the student:-).  Actually, I haven't found this process to
be a frequent enough need for me, to be worth the work to automate it
fully -- because of the limitation to dispinterfaces, which IS somewhat
drastic.  Still, it IS helpful to have a type-library, anyway, if you
implement Python COM servers that you need to consume from some other
languages (it doesn't help with VBscript or JScript, but it _is_ a bit
helpful with VB, and almost-indispensable should you ever want to use
C++ as the client of such COM objects...) -- and this is one good way
to achieve that.


Alex






More information about the Python-list mailing list