eventsApartmentThreaded.py 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. # A sample originally provided by Richard Bell, and modified by Mark Hammond.
  2. # This sample demonstrates how to use COM events in an aparment-threaded
  3. # world. In this world, COM itself ensures that all calls to and events
  4. # from an object happen on the same thread that created the object, even
  5. # if they originated from different threads. For this cross-thread
  6. # marshalling to work, this main thread *must* run a "message-loop" (ie,
  7. # a loop fetching and dispatching Windows messages). Without such message
  8. # processing, dead-locks can occur.
  9. # See also eventsFreeThreaded.py for how to do this in a free-threaded
  10. # world where these marshalling considerations do not exist.
  11. # NOTE: This example uses Internet Explorer, but it should not be considerd
  12. # a "best-practices" for writing against IE events, but for working with
  13. # events in general. For example:
  14. # * The first OnDocumentComplete event is not a reliable indicator that the
  15. # URL has completed loading
  16. # * As we are demonstrating the most efficient way of handling events, when
  17. # running this sample you will see an IE Windows briefly appear, but
  18. # vanish without ever being repainted.
  19. import sys
  20. import os
  21. import win32com.client
  22. import win32api
  23. import win32event
  24. # sys.coinit_flags not set, so pythoncom initializes apartment-threaded.
  25. import pythoncom
  26. import time
  27. class ExplorerEvents:
  28. def __init__(self):
  29. self.event = win32event.CreateEvent(None, 0, 0, None)
  30. def OnDocumentComplete(self,
  31. pDisp=pythoncom.Empty,
  32. URL=pythoncom.Empty):
  33. thread = win32api.GetCurrentThreadId()
  34. print("OnDocumentComplete event processed on thread %d"%thread)
  35. # Set the event our main thread is waiting on.
  36. win32event.SetEvent(self.event)
  37. def OnQuit(self):
  38. thread = win32api.GetCurrentThreadId()
  39. print("OnQuit event processed on thread %d"%thread)
  40. win32event.SetEvent(self.event)
  41. def WaitWhileProcessingMessages(event, timeout = 2):
  42. start = time.clock()
  43. while True:
  44. # Wake 4 times a second - we can't just specify the
  45. # full timeout here, as then it would reset for every
  46. # message we process.
  47. rc = win32event.MsgWaitForMultipleObjects( (event,), 0,
  48. 250,
  49. win32event.QS_ALLEVENTS)
  50. if rc == win32event.WAIT_OBJECT_0:
  51. # event signalled - stop now!
  52. return True
  53. if (time.clock() - start) > timeout:
  54. # Timeout expired.
  55. return False
  56. # must be a message.
  57. pythoncom.PumpWaitingMessages()
  58. def TestExplorerEvents():
  59. iexplore = win32com.client.DispatchWithEvents(
  60. "InternetExplorer.Application", ExplorerEvents)
  61. thread = win32api.GetCurrentThreadId()
  62. print('TestExplorerEvents created IE object on thread %d'%thread)
  63. iexplore.Visible = 1
  64. try:
  65. iexplore.Navigate(win32api.GetFullPathName('..\\readme.htm'))
  66. except pythoncom.com_error as details:
  67. print("Warning - could not open the test HTML file", details)
  68. # Wait for the event to be signalled while pumping messages.
  69. if not WaitWhileProcessingMessages(iexplore.event):
  70. print("Document load event FAILED to fire!!!")
  71. iexplore.Quit()
  72. #
  73. # Give IE a chance to shutdown, else it can get upset on fast machines.
  74. # Note, Quit generates events. Although this test does NOT catch them
  75. # it is NECESSARY to pump messages here instead of a sleep so that the Quit
  76. # happens properly!
  77. if not WaitWhileProcessingMessages(iexplore.event):
  78. print("OnQuit event FAILED to fire!!!")
  79. iexplore = None
  80. if __name__=='__main__':
  81. TestExplorerEvents()