"Universal Gateway"

Neil Hodgson nhodgson at bigpond.net.au
Tue Nov 5 06:32:08 EST 2002


Paul Moore:

> If so, is there a way to *call* an arbitrary interface?
> (Actually, I imagine that this should be possible using calldll plus a
> reasonable amount of arcane pointer arithmetic, but it sounds like it
> could be a bit messy and error prone...)

   Looks like my posts that include attachments are no longer propagating.

Bruce Dodson:

> I, too, salivated when I first heard about universal gateway.  However, on
> closer inspection, I found that it is outbound only: while it will allow
you
> to _implement_ just about any arbitrary interface in a Python server, it
> will not allow your Python code to _access_ those custom interfaces as a
> client.

   Way back in 1997, I published some very low level code for calling
arbitrary COM vtable interfaces using calldll on the Pythonwin sig mailing
list and it is visible at
http://groups.yahoo.com/group/pythonwin-sig/message/490
   Unfortunately the yahoo interface flattens the code and calldll has
changed since then, and ni has gone away :-( , so the code, resurrected
a bit, is at the end of this post and available from
http://www.scintilla.org/COMVtable.py
   You can get copies of calldll and npstruct for Python 2.2 from
http://www.activestate.com/PPMPackages/PyPPM/2.2+/packages/

   It is a very ugly way to do COM.

   Neil

import pythoncom
import calldll
import string
import npstruct

METH_QI = 0
METH_ADDREF = 1
METH_RELEASE = 2

METH_UPDATE = 13
METH_IS_UPTODATE = 14

class dwordbuf:
    def __init__(self):
        self.mb = calldll.membuf(4)
    def address(self):
        return self.mb.address()
    def value(self):
        return calldll.read_long(self.address())

class Unknown:
    def __init__(self, address, iid, fNeedAddRef = 1):
        self.address = address
        self.iid = iid
        # External use needs to addref, not needed if created by
        # internal QueryInterface
        if fNeedAddRef:
            self.AddRef()

    def __del__(self):
        self.Release()

    def Call(self, methnum, args = (), argdesc = "*", retdesc = "l"):
        if argdesc == "*": # intuit argument types
            argdesc = 'l'*len(args)
        #print 'Call ' + `methnum` + ' ' + `self.address`
        vtable = calldll.read_long(self.address)
        vtmeth = calldll.read_long(vtable+methnum * 4)
        # Prepend interface pointer to arg list
        argdesc = "l" + argdesc
        args = (self.address, ) + args
        #print args
        hres = calldll.call_foreign_function(vtmeth, argdesc, retdesc, args)
        return hres

    def QueryInterface(self, iid): # iid is a membuf holding a GUID
        bufPunk = dwordbuf()
        hres = self.Call(METH_QI, (iid.address(), bufPunk.address()))
        addrnewunk = bufPunk.value()
        return Unknown(addrnewunk, iid, 0)

    def AddRef(self):
        return self.Call(METH_ADDREF)

    def Release(self):
        return self.Call(METH_RELEASE)

def GetCOMAddress(unk):
    # Ugly way to get address of COM object represented by a
    # PyIUnknown by asking for its string representation.
    cdesc = `unk`
    cpt = string.atoi(cdesc[36:-1],16)
    return cpt

def GUIDBuf(guid):
    buf = calldll.membuf(16)
    gs = npstruct.pack('Llhhbbbbbbbb',(
        eval('0x' + guid[1:1+8]),
        string.atoi(guid[10:10+4],16),
        string.atoi(guid[15:15+4],16),
        string.atoi(guid[20:20+2],16),
        string.atoi(guid[22:22+2],16),
        string.atoi(guid[25:25+2],16),
        string.atoi(guid[27:27+2],16),
        string.atoi(guid[29:29+2],16),
        string.atoi(guid[31:31+2],16),
        string.atoi(guid[33:33+2],16),
        string.atoi(guid[35:35+2],16)))
    buf.write(gs,0,16)
    return buf

StdPict =
pythoncom.CoCreateInstance('{00000316-0000-0000-C000-000000000046}',
None, 1, pythoncom.IID_IUnknown)

StdPictOle =
StdPict.QueryInterface('{00000112-0000-0000-C000-000000000046}', 1)
iidOleObject = GUIDBuf('{00000112-0000-0000-C000-000000000046}')
pictOle = Unknown(GetCOMAddress(StdPictOle), iidOleObject)

hres = pictOle.Call(METH_IS_UPTODATE)
print 'HRESULT = ' + hex(hres)
# Destroy it now to avoid problems in automatic tear down
pictOle = None







More information about the Python-list mailing list