我想做的事情类似于,但使用 threading ,例如此处.还使用了此处的答案,我的代码可以正常工作,只是没有ItemItem事件可以识别(实际上,我认为是的,但是在另一个线程中,这就是为什么没有输出的原因.)
I want to do something similar to what is asked here, but using threading like here. Using also the answer from here, I got my code working, only that an ItemAdd event is not recognised (actually, I think it is, but in the other thread, which is why there is no output).
"""Handler class that watches for incoming mails""" import ctypes # for the WM_QUIT to stop PumpMessage() import logging import win32com.client import sys import threading import time import pythoncom # outlook config CENTRAL_MAILBOX = "My Mailbox" # get the outlook instance and inbox folders outlook = win32com.client.Dispatch("Outlook.Application") marshalled_otlk = pythoncom.CoMarshalInterThreadInterfaceInStream( pythoncom.IID_IDispatch, outlook) class HandlerClass(object): def OnItemAdd(self, item): logger.info("New item added in central mailbox") if item.Class == 43: logger.info("The item is an email!") class OtlkThread(threading.Thread): def __init__(self, marshalled_otlk, *args, **kwargs): super().__init__(*args, **kwargs) self.marshalled_otlk = marshalled_otlk self.logger = logging.getLogger("OLThread") def run(self): self.logger.info("Starting up Outlook watcher\n" "To terminate the program, press 'Ctrl + C'") pythoncom.CoInitialize() outlook = win32com.client.Dispatch( pythoncom.CoGetInterfaceAndReleaseStream( self.marshalled_otlk, pythoncom.IID_IDispatch ) ) user = outlook.Session.CreateRecipient(CENTRAL_MAILBOX) central_inbox = outlook.Session.GetSharedDefaultFolder(user, 6).Items self.logger.info(f"{central_inbox.Count} messages in central inbox") win32com.client.DispatchWithEvents(central_inbox, HandlerClass) pythoncom.PumpMessages() pythoncom.CoUninitialize() # this is prbly unnecessary as it will never be reached def main(): # pythoncom.CoInitialize() OtlkThread(marshalled_otlk, daemon=True).start() if __name__ == "__main__": status = main() while True: try: # pythoncom.PumpWaitingMessages() time.sleep(1) except KeyboardInterrupt: logger.info("Terminating program..") ctypes.windll.user32.PostQuitMessage(0) sys.exit(status)我尝试了各种方法,例如将 sys.coinit_flags = 0 放在顶部,如建议的此处),在主线程中调用 PumpWaitingMessages(),并且在侧线程本身中获取Outlook应用程序,而不是传递编组的对象.这些都不起作用.
I have tried various things, such as putting sys.coinit_flags=0 at the top, as suggested here), calling PumpWaitingMessages() in the main thread, and getting the Outlook application in the side thread itself, instead of passing the marshalled object. None of these have worked.
当我只是将PumpMessages放入主线程(相同的 HandlerClass ,但没有单独的线程)时,它可以工作,并且在到达时可以识别电子邮件,但是很明显,该线程被阻塞了,甚至无法收到KeyboardInterrupt异常抓到了.
When I just put PumpMessages in the main thread (same HandlerClass but no separate thread) it works and emails are recognised upon arrival, but obviously the thread is blocked and not even the KeyboardInterrupt exception can be caught.
那么,如何使Outlook Watcher在单独的线程中运行,以将消息发送到主线程以在那里输出?
So, how do I get the outlook watcher running in the separate thread to send the messages to the main thread for output there?
推荐答案再次非常感谢Michael的回答,这导致我这个答案,其中还包含指向优秀示例的链接.从答案和示例中得出的主要结论是,与其将Outlook作为一个编组对象传递,不如将它作为客户端传递给处理程序.另外,要使用 WithEvents 代替 DispatchWithEvents ,并在导入 pythoncom之前将 sys.coinit_flags = 0 设置为.
Again thanks a lot for your answer Michael, it led me to this answer which also contains a link to an excellent example. The main takeaway from the answer and the example is that, instead of passing Outlook as a marshalled object, just pass it as client to the handler. Also, to use WithEvents instead of DispatchWithEvents and to set sys.coinit_flags = 0 before importing pythoncom.
最终结果如下:
"""Handler class that watches for incoming mails""" import ctypes # for the WM_QUIT to stop PumpMessage() import logging import sys import time from threading import Thread sys.coinit_flags = 0 # pythoncom.COINIT_MULTITHREADED == 0 from pythoncom import (CoInitializeEx, CoUninitialize, COINIT_MULTITHREADED, PumpWaitingMessages) from win32com.client import Dispatch, WithEvents # outlook config CENTRAL_MAILBOX = "My Mailbox" # COM event handler class IncomingMailHandler: def OnItemAdd(self, item): logger.info("New item added in central mailbox") if item.Class == 43: logger.info(f"The item is an email with subject {item.Subject}") # main thread def main(): # get the outlook instance and inbox folders outlook = Dispatch("Outlook.Application") user = outlook.Session.CreateRecipient(CENTRAL_MAILBOX) central_inbox = outlook.Session.GetSharedDefaultFolder(user, 6).Items logger.info(f"{central_inbox.Count} messages in central inbox") # launch the second thread thread = Thread(target=watcher, args=(central_inbox,), daemon=True) thread.start() # other thread worker function def watcher(client): logger = logging.getLogger("watcher") CoInitializeEx(COINIT_MULTITHREADED) WithEvents(client, IncomingMailHandler) # event loop 2 _loop = 0 while True: PumpWaitingMessages() _loop += 1 if _loop % 20 == 0: logger.info("Watcher is running..") time.sleep(0.5) CoUninitialize() if __name__ == "__main__": logger.info("Starting up Outlook watcher\n" "To terminate the program, press 'Ctrl + C'") status = main() while True: try: time.sleep(0.5) except KeyboardInterrupt: logger.info("Terminating program..") ctypes.windll.user32.PostQuitMessage(0) sys.exit(status)更多推荐
来自不同线程的Pythoncom PumpMessages
发布评论