text: # Implements _both_ a connectable client, and a connectable server.
  #
  # Note that we cheat just a little - the Server in this demo is not created
  # via Normal COM - this means we can avoid registering the server.
  # However, the server _is_ accessed as a COM object - just the creation
  # is cheated on - so this is still working as a fully-fledged server.
  
  import pythoncom
  import win32com.server.util
  import win32com.server.connect
  from win32com.server.exception import Exception
  from pywin32_testutil import str2bytes
  
  # This is the IID of the Events interface both Client and Server support.
  IID_IConnectDemoEvents = pythoncom.MakeIID("{A4988850-49C3-11d0-AE5D-52342E000000}")
  
  # The server which implements
  # Create a connectable class, that has a single public method
  # 'DoIt', which echos to a single sink 'DoneIt'
  
  class ConnectableServer(win32com.server.connect.ConnectableServer):
  	_public_methods_ = ["DoIt"] + win32com.server.connect.ConnectableServer._public_methods_
  	_connect_interfaces_ = [IID_IConnectDemoEvents]
  	# The single public method that the client can call on us
  	# (ie, as a normal COM server, this exposes just this single method.
  	def DoIt(self,arg):
  		# Simply broadcast a notification.
  		self._BroadcastNotify(self.NotifyDoneIt, (arg,))
  
  	def NotifyDoneIt(self, interface, arg):
  		interface.Invoke(1000, 0, pythoncom.DISPATCH_METHOD, 1, arg)
  
  # Here is the client side of the connection world.
  # Define a COM object which implements the methods defined by the
  # IConnectDemoEvents interface.								
  class ConnectableClient:
  	# This is another cheat - I _know_ the server defines the "DoneIt" event
  	# as DISPID==1000 - I also know from the implementation details of COM
  	# that the first method in _public_methods_ gets 1000.
  	# Normally some explicit DISPID->Method mapping is required.
  	_public_methods_ = ["OnDoneIt"]
  	def __init__(self):
  		self.last_event_arg = None
  	# A client must implement QI, and respond to a query for the Event interface.
  	# In addition, it must provide a COM object (which server.util.wrap) does.
  	def _query_interface_(self, iid):
  		import win32com.server.util
  		# Note that this seems like a necessary hack.  I am responding to IID_IConnectDemoEvents
  		# but only creating an IDispatch gateway object.
  		if iid==IID_IConnectDemoEvents: return win32com.server.util.wrap(self)
  	# And here is our event method which gets called.
  	def OnDoneIt(self, arg):
  		self.last_event_arg = arg
  
  def CheckEvent(server, client, val, verbose):
  	client.last_event_arg = None
  	server.DoIt(val)
  	if client.last_event_arg != val:
  		raise RuntimeError("Sent %r, but got back %r" % (val, client.last_event_arg))
  	if verbose:
  		print("Sent and received %r" % val)
  
  # A simple test script for all this.
  # In the real world, it is likely that the code controlling the server
  # will be in the same class as that getting the notifications.
  def test(verbose=0):
  	import win32com.client.dynamic, win32com.client.connect
  	import win32com.server.policy
  	server = win32com.client.dynamic.Dispatch(win32com.server.util.wrap(ConnectableServer()))
  	connection = win32com.client.connect.SimpleConnection()
  	client = ConnectableClient()
  	connection.Connect(server, client, IID_IConnectDemoEvents)
  	CheckEvent(server, client, "Hello", verbose)
  	CheckEvent(server, client, str2bytes("Here is a null>\x00<"), verbose)
  	CheckEvent(server, client, "Here is a null>\x00<", verbose)
  	val = "test-\xe0\xf2" # 2 extended characters.
  	CheckEvent(server, client, val, verbose)
  	if verbose:
  		print("Everything seemed to work!")
  	# Aggressive memory leak checking (ie, do nothing!) :-)  All should cleanup OK???
  
  if __name__=='__main__':
  	test(1)
  