Re: Connection Points Implementation
"Shane Holloway (IEEE)" <[email protected]> Mon, 23 May 2005 12:01:12 -0600
| Newsgroups | gmane.comp.python.ctypes.devel |
|---|---|
| Message-ID | <[email protected]> |
Thomas Heller wrote:
> Shane, thanks for the patch. I have my own version of this patch, but
> yours is probably better. My own one only supports a single outgoing
> interface, but yours looks like it supports more of them. As a bonus,
> my version actually supports to 'fire' the events, and even from
> different threads. I'll try to merge both and check them into CVS. The
> approach to fire the events is quite simple, here it is (I hope
> thunderbird doesn't destroy the formatting):
<snip>
Ok, I tried to implement the fireEvent in what I have, but I keep
getting an exception that says::
TypeError: function takes at most 3 arguments (9 given)
For sink.Invoke(). It obviously takes 9 parameters, but I can't seem to
convince the system of that. ;) Any thoughts? ::
def fireEvent(*args):
for sink in self._iterConnectionPointSinks():
params = DISPPARAMS()
params.cArgs = len(args)
rgvarg = params.rgvarg = (VARIANT * len(args))()
for i, a in enumerate(args):
rgvarg[i].value = a
Invoke = dict(sink._methods_)['Invoke']
Invoke(
sink, dispid,
byref(GUID()), 0, # lcid
DISPATCH_METHOD, # wFlags
byref(params),
None, # pVarResult
None, # pExcepInfo
None) # puArgError
The patch of my attempt is attached.
Thanks,
-Shane
ctypes-ServerConnectionPoints-2005.05.23.patch
(text/plain, 12.9 KB)
Index: win32/com/connectionpoints.py
===================================================================
RCS file: /cvsroot/ctypes/ctypes/win32/com/connectionpoints.py,v
retrieving revision 1.10
diff -u -r1.10 connectionpoints.py
--- win32/com/connectionpoints.py 21 Apr 2004 18:07:34 -0000 1.10
+++ win32/com/connectionpoints.py 23 May 2005 17:51:54 -0000
@@ -1,6 +1,7 @@
# connect.py - ConnectionPoint support
from ctypes import *
-from ctypes.com import IUnknown, GUID, REFIID, STDMETHOD, HRESULT, COMObject
+from ctypes.com import IUnknown, GUID, REFIID, STDMETHOD, HRESULT, COMObject, E_NOTIMPL, hresult
+from ctypes.com.automation import VARIANT, DISPPARAMS, DISPATCH_METHOD
from ctypes.wintypes import DWORD
################
@@ -114,3 +115,153 @@
def disconnect(self, (cp, cookie)):
# disconnect. Call this with the data returned by connect()
cp.Unadvise(cookie)
+
+################################################################
+# A Base class for implementing ConnectionPoints
+#
+
+class ConnectableEventBinding(object):
+ _connectionPointSinks = None
+ _eventInterface = None
+
+ def __init__(self, eventInterface):
+ self._eventInterface = eventInterface
+ self._bindDispMethods(eventInterface)
+
+ def _getConnectionPointSinks(self):
+ if self._connectionPointSinks is None:
+ self._connectionPointSinks = {}
+ return self._connectionPointSinks
+
+ def _addConnectionSink(self, cookie, sink):
+ self._getConnectionPointSinks()[cookie] = sink
+ def _removeConnectionSink(self, cookie):
+ return self._getConnectionPointSinks().pop(cookie, None) is not None
+
+ def _iterConnectionPointSinks(self):
+ return self._getConnectionPointSinks().itervalues()
+
+ def _bindDispMethods(self, eventInterface):
+ for dispid, name, stdmethod in eventInterface._dispmethods_:
+ method = self._bindMethod(dispid, name)
+ if method is not None:
+ setattr(self, name, method)
+
+ def _bindMethod(self, dispid, methodName):
+ def fireEvent(*args):
+ for sink in self._iterConnectionPointSinks():
+ params = DISPPARAMS()
+ params.cArgs = len(args)
+ rgvarg = params.rgvarg = (VARIANT * len(args))()
+ for i, a in enumerate(args):
+ rgvarg[i].value = a
+
+ Invoke = dict(sink._methods_)['Invoke']
+ print
+ print '*************'
+ print 'method:', methodName
+ print 'sink:', sink
+ print 'dispid:', dispid
+ print 'args:', args
+ print
+ print 'invoke:', Invoke, type(Invoke)
+ print ' flags:', Invoke._flags_
+ print ' res:', Invoke.restype, Invoke._restype_
+ print ' args:', Invoke.argtypes, Invoke._argtypes_
+ print '*************'
+ print
+ Invoke(
+ sink, dispid,
+ byref(GUID()), 0, # lcid
+ DISPATCH_METHOD, # wFlags
+ byref(params),
+ None, # pVarResult
+ None, # pExcepInfo
+ None) # puArgError
+
+ fireEvent.__name__ = methodName
+ return fireEvent
+
+
+#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class ConnectableMixin(object):
+ EventBindingFactory = ConnectableEventBinding
+ _com_interfaces_ = [IConnectionPoint, IConnectionPointContainer]
+ _outgoing_interfaces_ = []
+ _connectableBindings = None
+ _connectionPointCookie = 0
+
+ def defaultEvent(self):
+ return self.getEventBindingFor(self._outgoing_interfaces_[0])
+
+ def getEventBindings(self):
+ if self._connectableBindings is None:
+ self._connectableBindings = {}
+ return self._connectableBindings
+
+ def iterEventBindings(self):
+ return self.getEventBindings().itervalues()
+
+ def getEventBindingFor(self, eventInterface):
+ bindings = self.getEventBindings()
+ try:
+ return bindings[eventInterface]
+ except LookupError:
+ eventBinding = self.EventBindingFactory(eventInterface)
+ bindings[eventInterface] = eventBinding
+ return eventBinding
+
+ def addConnectionPointSink(self, interface, connectionSink, cookie=None):
+ if cookie is None:
+ self._connectionPointCookie += 1
+ cookie = self._connectionPointCookie
+ self.getEventBindingFor(interface)._addConnectionSink(cookie, connectionSink)
+ return cookie
+
+ def removeConnectionPointSink(self, cookie):
+ result = False
+ for binding in self.iterEventBindings():
+ if binding._removeConnectionSink(cookie):
+ result = True
+ return result
+
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ def IConnectionPoint_GetConnectionInterface(self, this, guid):
+ return hresult.E_NOTIMPL
+ def IConnectionPoint_GetConnectionPointContainer(self, this, retIConnectionPointContainer):
+ return hresult.E_NOTIMPL
+ def IConnectionPoint_EnumConnections(self, piEnumConnections):
+ return hresult.E_NOTIMPL
+
+ def IConnectionPoint_Advise(self, this, piUnknown, pCookie):
+ cookie = None
+ for interface in self._outgoing_interfaces_:
+ connectionSink = POINTER(interface)()
+ piUnknown.QueryInterface(byref(interface._iid_), byref(connectionSink))
+
+ if connectionSink[0]:
+ cookie = self.addConnectionPointSink(interface, connectionSink[0], cookie)
+
+ if cookie is not None:
+ pCookie[0] = cookie
+ return hresult.S_OK
+ else:
+ return hresult.E_NOINTERFACE
+
+ def IConnectionPoint_Unadvise(self, this, cookie):
+ if self.removeConnectionPointSink(cookie):
+ return hresult.S_OK
+ else:
+ return hresult.E_UNEXPECTED
+
+ def IConnectionPointContainer_EnumConnectionPoints(self, this, piEnumConnectionPoints):
+ raise NotImplementedError() #return hresult.E_NOTIMPL
+ def IConnectionPointContainer_FindConnectionPoint(self, this, iidConnectionPoint, retIConnectionPoint):
+ cp = iidConnectionPoint[0]
+ if iidConnectionPoint[0] not in [x._iid_ for x in self._outgoing_interfaces_]:
+ return hresult.E_NOINTERFACE
+
+ return self.QueryInterface(this, [IConnectionPoint._iid_], retIConnectionPoint)
+
Index: win32/com/server.py
===================================================================
RCS file: /cvsroot/ctypes/ctypes/win32/com/server.py,v
retrieving revision 1.20
diff -u -r1.20 server.py
--- win32/com/server.py 20 Aug 2004 14:10:03 -0000 1.20
+++ win32/com/server.py 17 May 2005 19:25:28 -0000
@@ -49,6 +49,7 @@
def install(cls):
import sys
+ return # TODO: Remove SWH
sys.stdout = sys.stderr = cls()
install = classmethod(install)
Index: win32/com/samples/server/sum.idl
===================================================================
RCS file: /cvsroot/ctypes/ctypes/win32/com/samples/server/sum.idl,v
retrieving revision 1.3
diff -u -r1.3 sum.idl
--- win32/com/samples/server/sum.idl 15 Jan 2004 18:49:32 -0000 1.3
+++ win32/com/samples/server/sum.idl 23 May 2005 17:49:20 -0000
@@ -9,6 +9,18 @@
{
importlib("stdole2.tlb");
coclass CSum;
+
+ [
+ uuid(8312BA64-DC22-4C1A-BC07-007B89C092A8),
+ helpstring("_IDualSumEvents Interface")
+ ]
+ dispinterface _IDualSumEvents {
+ properties:
+ methods:
+ [id(0x00002005), helpstring("method OnAdd")]
+ HRESULT OnAdd([in] double a, double b, double result);
+ };
+
/* a dual interface, derived from IDispatch */
[
object,
@@ -29,5 +41,6 @@
coclass CSum
{
[default] interface IDualSum;
+ [default, source] dispinterface _IDualSumEvents;
}
};
Index: win32/com/samples/server/sum.py
===================================================================
RCS file: /cvsroot/ctypes/ctypes/win32/com/samples/server/sum.py,v
retrieving revision 1.8
diff -u -r1.8 sum.py
--- win32/com/samples/server/sum.py 9 Jul 2003 19:08:19 -0000 1.8
+++ win32/com/samples/server/sum.py 20 May 2005 20:28:20 -0000
@@ -11,7 +11,7 @@
# Then, we can import what we need from the sum_gen module:
# interface, coclass, typelib
-from sum_gen import IDualSum, CSum, SumLib
+from sum_gen import IDualSum, CSum, SumLib, _IDualSumEvents
# special code for this sample, to make sure sum_gen contains
# a valid path to the type library:
@@ -26,10 +26,12 @@
# and ctypes.com provides a handy bas class we can use:
from ctypes.com.automation import DualObjImpl
from ctypes.com.server import CLSCTX_LOCAL_SERVER, CLSCTX_INPROC_SERVER
+from ctypes.com.connectionpoints import ConnectableMixin
-class SumObject(DualObjImpl):
+class SumObject(DualObjImpl, ConnectableMixin):
# A sequence of COM interfaces this object implements
- _com_interfaces_ = [IDualSum]
+ _com_interfaces_ = [IDualSum] + ConnectableMixin._com_interfaces_
+ _outgoing_interfaces_ = [_IDualSumEvents]
# The type library we need, SumLib has the correct attributes
_typelib_ = SumLib
# The progid for the registry
@@ -62,6 +64,10 @@
# floats, and presult is the result pointer which also points
# to a c_double.
presult[0] = a + b
+
+ # fire the callbacks
+ self.defaultEvent().OnAdd(a, b, presult[0])
+
# The method must return a HRESULT, 0 is the same as S_OK
return 0
Index: win32/com/samples/server/sum.tlb
===================================================================
RCS file: /cvsroot/ctypes/ctypes/win32/com/samples/server/sum.tlb,v
retrieving revision 1.3
diff -u -r1.3 sum.tlb
Binary files /tmp/cvslU5URW and sum.tlb differ
Index: win32/com/samples/server/sum_gen.py
===================================================================
RCS file: /cvsroot/ctypes/ctypes/win32/com/samples/server/sum_gen.py,v
retrieving revision 1.4
diff -u -r1.4 sum_gen.py
--- win32/com/samples/server/sum_gen.py 3 Jul 2003 20:06:37 -0000 1.4
+++ win32/com/samples/server/sum_gen.py 23 May 2005 17:49:56 -0000
@@ -1,5 +1,5 @@
# -*- python -*-
-# Generated from C:\sf\ctypes_head\win32\com\samples\server\sum.tlb
+# Generated from c:\3rdParty\pyKeepers.src\tools and libs\ctypes\win32\com\samples\server\sum.tlb
###############################################################
# NOTE: This is a GENERATED file. Please do not make changes, #
@@ -20,7 +20,7 @@
guid = GUID('{90810CB9-D427-48B6-81FF-92D4A2098B45}')
version = (1, 0)
flags = 0x8
- path = 'C:\\sf\\ctypes_head\\win32\\com\\samples\\server\\sum.tlb'
+ path = 'c:\\3rdParty\\pyKeepers.src\\tools and libs\\ctypes\\win32\\com\\samples\\server\\sum.tlb'
##############################################################################
@@ -29,14 +29,24 @@
_iid_ = GUID('{6EDC65BF-0CB7-4B0D-9E43-11C655E51AE9}')
+class _IDualSumEvents(dispinterface):
+ """_IDualSumEvents Interface"""
+ _iid_ = GUID('{8312BA64-DC22-4C1A-BC07-007B89C092A8}')
+
+
IDualSum._methods_ = IDispatch._methods_ + [
STDMETHOD(HRESULT, "Add", c_double, c_double, POINTER(c_double)),
]
+_IDualSumEvents._dispmethods_ = [
+ DISPMETHOD(0x2005L, HRESULT, "OnAdd", c_double, c_double, c_double),
+]
+
##############################################################################
class CSum:
"""CSum Class"""
_reg_clsid_ = '{2E0504A1-1A23-443F-939D-869A6C731521}'
_com_interfaces_ = [IDualSum]
+ _outgoing_interfaces_ = [_IDualSumEvents]
Index: win32/com/samples/server/sum_user.py
===================================================================
RCS file: /cvsroot/ctypes/ctypes/win32/com/samples/server/sum_user.py,v
retrieving revision 1.3
diff -u -r1.3 sum_user.py
--- win32/com/samples/server/sum_user.py 28 May 2003 19:53:43 -0000 1.3
+++ win32/com/samples/server/sum_user.py 17 May 2005 19:23:00 -0000
@@ -4,7 +4,8 @@
from ctypes import *
from ctypes.com import CreateInstance
from ctypes.com.automation import IDispatch
-from sum_gen import CSum
+from ctypes.com.connectionpoints import dispinterface_EventReceiver
+from sum_gen import CSum, _IDualSumEvents
CLSCTX_INPROC_SERVER = 0x1
CLSCTX_LOCAL_SERVER = 0x4
@@ -17,11 +18,23 @@
print "Using CLSCTX_LOCAL_SERVER"
clsctx = CLSCTX_LOCAL_SERVER
+class SumEvents(dispinterface_EventReceiver):
+ _com_interfaces_ = [_IDualSumEvents]
+
+ def _IDualSumEvents_AnAddEvent(self, this, a, b, result):
+ print "OnAdd", a, b, result
+
sum = CreateInstance(CSum, clsctx=clsctx)
result = c_double()
sum.Add(3.14, 3.14, byref(result))
print "Added 3.14 and 3.14, result", result.value
+events = SumEvents()
+cookie = events.connect(sum)
+sum.Add(2.718, 2.718, byref(result))
+print "Added 2.718 and 2.718, result", result.value
+events.disconnect(cookie)
+
idisp = pointer(IDispatch())
sum.QueryInterface(byref(IDispatch._iid_), byref(idisp))