来自不同线程的 Pythoncom PumpMessages
Pythoncom PumpMessages from different thread
我想做类似于要求的事情 , but using threading
like here. Using also the answer from here,我的代码可以工作,只是无法识别 ItemAdd 事件(实际上,我认为是,但在另一个线程中,它这就是没有输出的原因)。
"""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)
我已经尝试了各种方法,例如按照建议here将sys.coinit_flags=0
放在顶部),在主线程中调用PumpWaitingMessages()
,并在主线程中获取Outlook应用程序侧线程本身,而不是传递编组对象。 None 其中有效。
当我将 PumpMessages 放入主线程时(相同 HandlerClass
但没有单独的线程)它可以工作并且电子邮件在到达时被识别,但显然线程被阻塞并且甚至无法捕获 KeyboardInterrupt 异常.
那么,如何在单独的线程中让 outlook watcher 运行 将消息发送到主线程以在那里输出?
问题格式正确,附有参考文献。谢谢。
求解答。在这种情况下,我使用``threading.Thread(target= ....`。但是您也可以使用继承:
import logging
import threading
import time
def task(name):
log = logging.getLogger('task-' + name)
log.info("Starting up Outlook watcher\n"
"To terminate the program, press 'Ctrl + C'")
while True:
log.info("Doing work that takes time")
time.sleep(1)
class OtlkThread(threading.Thread):
def __init__(self, *args, **kwargs):
self.log = logging.getLogger('task-class')
super().__init__(*args, **kwargs)
def run(self):
self.log.info("Starting up Outlook watcher\n"
"To terminate the program, press 'Ctrl + C'")
while True:
self.log.info("Doing work that takes time")
time.sleep(1)
def main():
t1 = threading.Thread(target=task, args=('daemon',), daemon=True)
t1.start()
t2 = OtlkThread()
t2.start()
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG,)
main()
while True:
time.sleep(1)
再次感谢迈克尔的回答,这让我想到了 answer which also contains a link to an excellent example。答案和示例的主要内容是,不是将 Outlook 作为编组对象传递,而是将其作为客户端传递给处理程序。此外,使用 WithEvents
而不是 DispatchWithEvents
并在 导入 pythoncom
.
之前设置 sys.coinit_flags = 0
最终结果如下所示:
"""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)
我想做类似于要求的事情 threading
like here. Using also the answer from here,我的代码可以工作,只是无法识别 ItemAdd 事件(实际上,我认为是,但在另一个线程中,它这就是没有输出的原因)。
"""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)
我已经尝试了各种方法,例如按照建议here将sys.coinit_flags=0
放在顶部),在主线程中调用PumpWaitingMessages()
,并在主线程中获取Outlook应用程序侧线程本身,而不是传递编组对象。 None 其中有效。
当我将 PumpMessages 放入主线程时(相同 HandlerClass
但没有单独的线程)它可以工作并且电子邮件在到达时被识别,但显然线程被阻塞并且甚至无法捕获 KeyboardInterrupt 异常.
那么,如何在单独的线程中让 outlook watcher 运行 将消息发送到主线程以在那里输出?
问题格式正确,附有参考文献。谢谢。
求解答。在这种情况下,我使用``threading.Thread(target= ....`。但是您也可以使用继承:
import logging
import threading
import time
def task(name):
log = logging.getLogger('task-' + name)
log.info("Starting up Outlook watcher\n"
"To terminate the program, press 'Ctrl + C'")
while True:
log.info("Doing work that takes time")
time.sleep(1)
class OtlkThread(threading.Thread):
def __init__(self, *args, **kwargs):
self.log = logging.getLogger('task-class')
super().__init__(*args, **kwargs)
def run(self):
self.log.info("Starting up Outlook watcher\n"
"To terminate the program, press 'Ctrl + C'")
while True:
self.log.info("Doing work that takes time")
time.sleep(1)
def main():
t1 = threading.Thread(target=task, args=('daemon',), daemon=True)
t1.start()
t2 = OtlkThread()
t2.start()
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG,)
main()
while True:
time.sleep(1)
再次感谢迈克尔的回答,这让我想到了 WithEvents
而不是 DispatchWithEvents
并在 导入 pythoncom
.
sys.coinit_flags = 0
最终结果如下所示:
"""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)