You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
9.9 KiB
292 lines
9.9 KiB
6 years ago
|
import atexit, os, unittest
|
||
|
##import comtypes
|
||
|
import comtypes.typeinfo, comtypes.client
|
||
|
|
||
|
class TypeLib(object):
|
||
|
"""This class collects IDL code fragments and eventually writes
|
||
|
them into a .IDL file. The compile() method compiles the IDL file
|
||
|
into a typelibrary and registers it. A function is also
|
||
|
registered with atexit that will unregister the typelib at program
|
||
|
exit.
|
||
|
"""
|
||
|
def __init__(self, lib):
|
||
|
self.lib = lib
|
||
|
self.interfaces = []
|
||
|
self.coclasses = []
|
||
|
|
||
|
def interface(self, header):
|
||
|
itf = Interface(header)
|
||
|
self.interfaces.append(itf)
|
||
|
return itf
|
||
|
|
||
|
def coclass(self, definition):
|
||
|
self.coclasses.append(definition)
|
||
|
|
||
|
def __str__(self):
|
||
|
header = '''import "oaidl.idl";
|
||
|
import "ocidl.idl";
|
||
|
%s {''' % self.lib
|
||
|
body = "\n".join([str(itf) for itf in self.interfaces])
|
||
|
footer = "\n".join(self.coclasses) + "}"
|
||
|
return "\n".join((header, body, footer))
|
||
|
|
||
|
def compile(self):
|
||
|
"""Compile and register the typelib"""
|
||
|
code = str(self)
|
||
|
curdir = os.path.dirname(__file__)
|
||
|
idl_path = os.path.join(curdir, "mylib.idl")
|
||
|
tlb_path = os.path.join(curdir, "mylib.tlb")
|
||
|
if not os.path.isfile(idl_path) or open(idl_path, "r").read() != code:
|
||
|
open(idl_path, "w").write(code)
|
||
|
os.system(r'call "%%VS71COMNTOOLS%%vsvars32.bat" && '
|
||
|
r'midl /nologo %s /tlb %s' % (idl_path, tlb_path))
|
||
|
# Register the typelib...
|
||
|
tlib = comtypes.typeinfo.LoadTypeLib(tlb_path)
|
||
|
# create the wrapper module...
|
||
|
comtypes.client.GetModule(tlb_path)
|
||
|
# Unregister the typelib at interpreter exit...
|
||
|
attr = tlib.GetLibAttr()
|
||
|
guid, major, minor = attr.guid, attr.wMajorVerNum, attr.wMinorVerNum
|
||
|
## atexit.register(comtypes.typeinfo.UnRegisterTypeLib,
|
||
|
## guid, major, minor)
|
||
|
return tlb_path
|
||
|
|
||
|
class Interface(object):
|
||
|
def __init__(self, header):
|
||
|
self.header = header
|
||
|
self.code = ""
|
||
|
|
||
|
def add(self, text):
|
||
|
self.code += text + "\n"
|
||
|
return self
|
||
|
|
||
|
def __str__(self):
|
||
|
return self.header + " {\n" + self.code + "}\n"
|
||
|
|
||
|
################################################################
|
||
|
import comtypes
|
||
|
from comtypes.client import wrap
|
||
|
|
||
|
tlb = TypeLib("[uuid(f4f74946-4546-44bd-a073-9ea6f9fe78cb)] library TestLib")
|
||
|
|
||
|
itf = tlb.interface("""[object,
|
||
|
oleautomation,
|
||
|
dual,
|
||
|
uuid(ed978f5f-cc45-4fcc-a7a6-751ffa8dfedd)]
|
||
|
interface IMyInterface : IDispatch""")
|
||
|
|
||
|
outgoing = tlb.interface("""[object,
|
||
|
oleautomation,
|
||
|
dual,
|
||
|
uuid(f7c48a90-64ea-4bb8-abf1-b3a3aa996848)]
|
||
|
interface IMyEventInterface : IDispatch""")
|
||
|
|
||
|
tlb.coclass("""
|
||
|
[uuid(fa9de8f4-20de-45fc-b079-648572428817)]
|
||
|
coclass MyServer {
|
||
|
[default] interface IMyInterface;
|
||
|
[default, source] interface IMyEventInterface;
|
||
|
};
|
||
|
""")
|
||
|
|
||
|
# The purpose of the MyServer class is to locate three separate code
|
||
|
# section snippets closely together:
|
||
|
#
|
||
|
# 1. The IDL method definition for a COM interface method
|
||
|
# 2. The Python implementation of the COM method
|
||
|
# 3. The unittest(s) for the COM method.
|
||
|
#
|
||
|
from comtypes.server.connectionpoints import ConnectableObjectMixin
|
||
|
class MyServer(comtypes.CoClass, ConnectableObjectMixin):
|
||
|
_reg_typelib_ = ('{f4f74946-4546-44bd-a073-9ea6f9fe78cb}', 0, 0)
|
||
|
_reg_clsid_ = comtypes.GUID('{fa9de8f4-20de-45fc-b079-648572428817}')
|
||
|
|
||
|
################
|
||
|
# definition
|
||
|
itf.add("""[id(100), propget] HRESULT Name([out, retval] BSTR *pname);
|
||
|
[id(100), propput] HRESULT Name([in] BSTR name);""")
|
||
|
# implementation
|
||
|
Name = "foo"
|
||
|
# test
|
||
|
def test_Name(self):
|
||
|
p = wrap(self.create())
|
||
|
self.assertEqual((p.Name, p.name, p.nAME), ("foo",) * 3)
|
||
|
p.NAME = "spam"
|
||
|
self.assertEqual((p.Name, p.name, p.nAME), ("spam",) * 3)
|
||
|
|
||
|
################
|
||
|
# definition
|
||
|
itf.add("[id(101)] HRESULT MixedInOut([in] int a, [out] int *b, [in] int c, [out] int *d);")
|
||
|
# implementation
|
||
|
def MixedInOut(self, a, c):
|
||
|
return a+1, c+1
|
||
|
#test
|
||
|
def test_MixedInOut(self):
|
||
|
p = wrap(self.create())
|
||
|
self.assertEqual(p.MixedInOut(1, 2), (2, 3))
|
||
|
|
||
|
################
|
||
|
# definition
|
||
|
itf.add("[id(102)] HRESULT MultiInOutArgs([in, out] int *pa, [in, out] int *pb);")
|
||
|
# implementation
|
||
|
def MultiInOutArgs(self, pa, pb):
|
||
|
return pa[0] * 3, pb[0] * 4
|
||
|
# test
|
||
|
def test_MultiInOutArgs(self):
|
||
|
p = wrap(self.create())
|
||
|
self.assertEqual(p.MultiInOutArgs(1, 2), (3, 8))
|
||
|
|
||
|
################
|
||
|
# definition
|
||
|
itf.add("HRESULT MultiInOutArgs2([in, out] int *pa, [out] int *pb);")
|
||
|
## # implementation
|
||
|
## def MultiInOutArgs2(self, pa):
|
||
|
## return pa[0] * 3, pa[0] * 4
|
||
|
## # test
|
||
|
## def test_MultiInOutArgs2(self):
|
||
|
## p = wrap(self.create())
|
||
|
## self.assertEqual(p.MultiInOutArgs2(42), (126, 168))
|
||
|
|
||
|
################
|
||
|
# definition
|
||
|
itf.add("HRESULT MultiInOutArgs3([out] int *pa, [out] int *pb);")
|
||
|
# implementation
|
||
|
def MultiInOutArgs3(self):
|
||
|
return 42, 43
|
||
|
# test
|
||
|
def test_MultiInOutArgs3(self):
|
||
|
p = wrap(self.create())
|
||
|
self.assertEqual(p.MultiInOutArgs3(), (42, 43))
|
||
|
|
||
|
################
|
||
|
# definition
|
||
|
itf.add("HRESULT MultiInOutArgs4([out] int *pa, [in, out] int *pb);")
|
||
|
# implementation
|
||
|
def MultiInOutArgs4(self, pb):
|
||
|
return pb[0] + 3, pb[0] + 4
|
||
|
# test
|
||
|
def test_MultiInOutArgs4(self):
|
||
|
p = wrap(self.create())
|
||
|
res = p.MultiInOutArgs4(pb=32)
|
||
|
## print "MultiInOutArgs4", res
|
||
|
|
||
|
itf.add("""HRESULT GetStackTrace([in] ULONG FrameOffset,
|
||
|
[in, out] INT *Frames,
|
||
|
[in] ULONG FramesSize,
|
||
|
[out, optional] ULONG *FramesFilled);""")
|
||
|
def GetStackTrace(self, this, *args):
|
||
|
## print "GetStackTrace", args
|
||
|
return 0
|
||
|
def test_GetStackTrace(self):
|
||
|
p = wrap(self.create())
|
||
|
from ctypes import c_int, POINTER, pointer
|
||
|
frames = (c_int * 5)()
|
||
|
res = p.GetStackTrace(42, frames, 5)
|
||
|
## print "RES_1", res
|
||
|
|
||
|
frames = pointer(c_int(5))
|
||
|
res = p.GetStackTrace(42, frames, 0)
|
||
|
## print "RES_2", res
|
||
|
|
||
|
# It is unlear to me if this is allowed or not. Apparently there
|
||
|
# are typelibs that define such an argument type, but it may be
|
||
|
# that these are buggy.
|
||
|
#
|
||
|
# Point is that SafeArrayCreateEx(VT_VARIANT|VT_BYREF, ..) fails.
|
||
|
# The MSDN docs for SafeArrayCreate() have a notice that neither
|
||
|
# VT_ARRAY not VT_BYREF may be set, this notice is missing however
|
||
|
# for SafeArrayCreateEx().
|
||
|
#
|
||
|
# We have this code here to make sure that comtypes can import
|
||
|
# such a typelib, although calling ths method will fail because
|
||
|
# such an array cannot be created.
|
||
|
itf.add("""HRESULT dummy([in] SAFEARRAY(VARIANT *) foo);""")
|
||
|
|
||
|
|
||
|
# Test events.
|
||
|
itf.add("""HRESULT DoSomething();""")
|
||
|
outgoing.add("""[id(103)] HRESULT OnSomething();""")
|
||
|
# implementation
|
||
|
def DoSomething(self):
|
||
|
"Implement the DoSomething method"
|
||
|
self.Fire_Event(0, "OnSomething")
|
||
|
# test
|
||
|
def test_events(self):
|
||
|
p = wrap(self.create())
|
||
|
class Handler(object):
|
||
|
called = 0
|
||
|
def OnSomething(self, this):
|
||
|
"Handles the OnSomething event"
|
||
|
self.called += 1
|
||
|
handler = Handler()
|
||
|
ev = comtypes.client.GetEvents(p, handler)
|
||
|
p.DoSomething()
|
||
|
self.assertEqual(handler.called, 1)
|
||
|
|
||
|
class Handler(object):
|
||
|
called = 0
|
||
|
def IMyEventInterface_OnSomething(self):
|
||
|
"Handles the OnSomething event"
|
||
|
self.called += 1
|
||
|
handler = Handler()
|
||
|
ev = comtypes.client.GetEvents(p, handler)
|
||
|
p.DoSomething()
|
||
|
self.assertEqual(handler.called, 1)
|
||
|
|
||
|
# events with out-parameters (these are probably very unlikely...)
|
||
|
itf.add("""HRESULT DoSomethingElse();""")
|
||
|
outgoing.add("""[id(104)] HRESULT OnSomethingElse([out, retval] int *px);""")
|
||
|
def DoSomethingElse(self):
|
||
|
"Implement the DoSomething method"
|
||
|
self.Fire_Event(0, "OnSomethingElse")
|
||
|
def test_DoSomethingElse(self):
|
||
|
p = wrap(self.create())
|
||
|
class Handler(object):
|
||
|
called = 0
|
||
|
def OnSomethingElse(self):
|
||
|
"Handles the OnSomething event"
|
||
|
self.called += 1
|
||
|
return 42
|
||
|
handler = Handler()
|
||
|
ev = comtypes.client.GetEvents(p, handler)
|
||
|
p.DoSomethingElse()
|
||
|
self.assertEqual(handler.called, 1)
|
||
|
|
||
|
class Handler(object):
|
||
|
called = 0
|
||
|
def OnSomethingElse(self, this, presult):
|
||
|
"Handles the OnSomething event"
|
||
|
self.called += 1
|
||
|
presult[0] = 42
|
||
|
handler = Handler()
|
||
|
ev = comtypes.client.GetEvents(p, handler)
|
||
|
p.DoSomethingElse()
|
||
|
self.assertEqual(handler.called, 1)
|
||
|
|
||
|
################################################################
|
||
|
|
||
|
path = tlb.compile()
|
||
|
from comtypes.gen import TestLib
|
||
|
from comtypes.typeinfo import IProvideClassInfo, IProvideClassInfo2
|
||
|
from comtypes.connectionpoints import IConnectionPointContainer
|
||
|
|
||
|
MyServer._com_interfaces_ = [TestLib.IMyInterface,
|
||
|
IProvideClassInfo2,
|
||
|
IConnectionPointContainer]
|
||
|
MyServer._outgoing_interfaces_ = [TestLib.IMyEventInterface]
|
||
|
|
||
|
################################################################
|
||
|
|
||
|
class Test(unittest.TestCase, MyServer):
|
||
|
def __init__(self, *args):
|
||
|
unittest.TestCase.__init__(self, *args)
|
||
|
MyServer.__init__(self)
|
||
|
|
||
|
def create(self):
|
||
|
obj = MyServer()
|
||
|
return obj.QueryInterface(comtypes.IUnknown)
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
unittest.main()
|