add skeleton for variant type support
[pywinlite.git] / winlitecom.py
blobc0dd308b51292fe48963747893267410dfc9d0ba
1 #Copyright (c) 2009 Vincent Povirk
3 #Permission is hereby granted, free of charge, to any person
4 #obtaining a copy of this software and associated documentation
5 #files (the "Software"), to deal in the Software without
6 #restriction, including without limitation the rights to use,
7 #copy, modify, merge, publish, distribute, sublicense, and/or sell
8 #copies of the Software, and to permit persons to whom the
9 #Software is furnished to do so, subject to the following
10 #conditions:
12 #The above copyright notice and this permission notice shall be
13 #included in all copies or substantial portions of the Software.
15 #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 #EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 #OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 #NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 #HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 #WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 #FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 #OTHER DEALINGS IN THE SOFTWARE.
24 # basic COM utilities
26 from ctypes import POINTER, c_void_p, Structure, WINFUNCTYPE, cast, byref, addressof
27 from windef import HRESULT, GUID, REFIID, ULONG
29 class _COMMethod(object):
30 __slots__ = ['p', 'name']
31 def __init__(self, obj, name):
32 self.p = obj.p
33 self.name = name
35 def __call__(self, *args):
36 return getattr(self.p.contents.contents, self.name)(self.p, *args)
38 class _COMMethodGetter(object):
39 __slots__ = ['name']
40 def __init__(self, name):
41 self.name = name
43 def __get__(self, instance, owner):
44 return _COMMethod(instance, self.name)
46 def _translate_methods(methods, vtable):
47 # translates a com method list into a vtable
48 result = []
49 objtype = POINTER(POINTER(vtable))
50 for method in methods:
51 name = method[0]
52 functype = WINFUNCTYPE(method[1], objtype, *method[2:])
53 result.append((name, functype))
54 return result
56 class _interface_outparam(object):
57 __slots__ = ['p', '_as_parameter_']
58 def __init__(self):
59 self.p = c_void_p(0)
60 self._as_parameter_ = addressof(self.p)
62 @classmethod
63 def from_param(cls, arg):
64 if arg == None:
65 return c_void_p(0)
66 elif isinstance(arg, cls) or (isinstance(arg, _interface_outparam) and issubclass(cls.cls, arg.cls)):
67 return arg
68 else:
69 raise TypeError
71 def get(self):
72 if self.p.value:
73 result = self.cls(self.p.value)
74 self.p.value = None
75 return result
76 else:
77 raise ValueError("NULL interface pointer")
79 def __del__(self):
80 if self.p.value:
81 self.get()
83 class _InterfaceClass(type):
84 def __init__(self, name, bases, dict):
85 type.__init__(self, name, bases, dict)
87 # create the vtable type
88 class vtable(Structure):
89 pass
90 vtable.__name__ = '_%s_vtable' % name
91 self._vtable = vtable
93 if 'com_methods' in dict:
94 self.set_methods(self.com_methods)
95 #else the creator of the class must call set_methods
97 # a COM object is a pointer to a pointer to a vtable
98 self.objtype = POINTER(POINTER(vtable))
100 self.outparam = type('_%s_outparam' % name, (_interface_outparam,), {'cls': self})
102 def set_methods(self, methods):
103 # add the base class methods to our list of methods
104 for base in self.__bases__:
105 try:
106 self.com_methods = base.com_methods + methods
107 break
108 except AttributeError:
109 pass
110 else:
111 self.com_methods = methods
113 self._vtable._fields_ = _translate_methods(self.com_methods, self._vtable)
115 # add an attribute for each method
116 for method in self.com_methods:
117 setattr(self, method[0], _COMMethodGetter(method[0]))
119 class _BaseInterface(object):
120 com_methods = []
122 def __init__(self, p):
123 self.p = cast(c_void_p(p), self.objtype)
125 def _get_parameter(self):
126 return self.p
128 _as_parameter_ = property(_get_parameter)
130 @classmethod
131 def from_param(cls, arg):
132 if arg is None:
133 return c_void_p(0)
134 elif isinstance(arg, cls):
135 return arg
136 else:
137 raise TypeError
139 # Needed because 3.0 added new syntax for metaclasses and removed the old syntax.
140 _InterfaceClassInstanceBase = _InterfaceClass('_InterfaceClassInstanceBase', (_BaseInterface,), {})
142 class IUnknown(_InterfaceClassInstanceBase):
143 com_methods = [
144 ('QueryInterface', HRESULT, REFIID, POINTER(c_void_p)),
145 ('AddRef', ULONG),
146 ('Release', ULONG),
149 iid = GUID("00000000-0000-0000-C000-000000000046")
151 def __init__(self, obj):
152 try:
153 p = c_void_p()
154 obj.QueryInterface(byref(self.iid), byref(p))
155 self.p = cast(p, self.objtype)
156 except AttributeError:
157 _InterfaceClassInstanceBase.__init__(self, obj)
159 def __del__(self):
160 self.Release()