pythoncom.pumpmessages()_来自不同线程的Pythoncom PumpMessages

这篇博客探讨了如何在Python中使用`pythoncom.PumpMessages()`函数来处理来自不同线程的`Outlook`事件,特别是`ItemAdd`事件。作者创建了一个`HandlerClass`来监听中央邮箱的新邮件,并通过`OtlkThread`线程运行`Outlook`监视器。尽管代码能正常运行,但在多线程环境下,`ItemAdd`事件未被正确识别。博客内容讨论了尝试的不同解决方案,包括初始化`pythoncom`,使用`PumpWaitingMessages`,以及在线程间传递`Outlook`实例,但都没有成功解决事件触发问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我想执行类似于要求here的操作,但是使用诸如here的线程.也使用来自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)

我尝试了各种方法,例如将sys.coinit_flags = 0放在顶部,如建议的here),在主线程中调用PumpWaitingMessages(),并在侧线程本身中获取Outlook应用程序,而不是传递编组的对象.这些都不起作用.

当我仅将PumpMessages放入主线程(相同的HandlerClass,但没有单独的线程)时,它就可以工作并且可以在到达时识别电子邮件,但是很明显,该线程已被阻塞,甚至无法捕获KeyboardInterrupt异常.

因此,如何使Outlook Watcher在单独的线程中运行,以将消息发送到主线程以在那里输出?

import tkinter as tk from tkinter import Menu import threading import win32api import pythoncom from win32gui import NIF_TIP, NIF_MESSAGE, NIF_ICON, LoadImage, Shell_NotifyIcon, NIM_ADD, NIM_DELETE,GetCursorPos from win32con import LR_LOADFROMFILE, WM_USER, IMAGE_ICON, WM_LBUTTONUP, LR_DEFAULTSIZE,WM_RBUTTONUP import os WM_TRAYICON = WM_USER + 1 class SysTrayIcon: def __init__(self, hwnd, icon_path: str = 'app.ico', title="Tkinter App"): self.hwnd =hwnd # 主窗口引用 self.icon_path = os.path.abspath(icon_path) self.title = title self.message_map = {WM_LBUTTONUP: self.on_left_click} # 左键单击事件 self.message_map.update({WM_RBUTTONUP: self.show_menu}) self.create_tray_icon() def create_tray_icon(self): # 创建托盘图标 self.icon_flags = NIF_ICON | NIF_MESSAGE | NIF_TIP self.hIcon = LoadImage(None, self.icon_path, IMAGE_ICON, 0, 0, LR_LOADFROMFILE) self.nid = ( self.hwnd, 0, self.icon_flags, WM_TRAYICON, self.hIcon, "双击显示窗口" ) # print(self.nid) Shell_NotifyIcon(NIM_ADD, self.nid) def on_left_click(self, hwnd, msg, wparam, lparam): self.hwnd.deiconify() # 恢复窗口显示 self.remove_icon() # 移除托盘图标 # 添加托盘菜单功能(扩展SysTrayIcon类) def show_menu(self, hwnd, msg, wparam, lparam): if msg == WM_RBUTTONUP: # 右键单击 pos = GetCursorPos() self.tray_menu.tk_popup(pos[0], pos[1]) # 在鼠标位置弹出菜单 def remove_icon(self): Shell_NotifyIcon(NIM_DELETE, self.nid) class MainWindow(tk.Tk): def __init__(self): super().__init__() self.title("系统托盘示例") self.geometry("300x200") self.protocol("WM_DELETE_WINDOW", self.on_close) # 覆盖关闭按钮行为 self.bind("<Unmap>", self.minimize_to_tray) # 绑定最小化事件 # 创建托盘菜单 self.tray_menu = Menu(self, tearoff=0) self.tray_menu.add_command(label="恢复窗口", command=self.restore_window) self.tray_menu.add_command(label="退出", command=self.destroy) def minimize_to_tray(self, event): if event.widget == self: # 确保是主窗口最小化 self.withdraw() # 隐藏窗口 SysTrayIcon(self.winfo_id(), "app.ico") # 创建托盘图标 def on_close(self): self.destroy() # 实际关闭程序 def restore_window(self): self.deiconify() # 恢复窗口显示 if __name__ == "__main__": app = MainWindow() app.mainloop()
06-18
import openpyxl import os import pyHook import pythoncom # 监听到鼠标事件调用 def onMouseEvent(event): if (event.MessageName != "mouse move"): # 因为鼠标一动就会有很多mouse move,所以把这个过滤下 print(event.MessageName) return True # 为True才会正常调用,如果为False的话,此次事件被拦截 # 监听到键盘事件调用 def onKeyboardEvent(event): print(event.Key) # 返回按下的键 return True def main(): # 创建管理器 hm = pyHook.HookManager() # 监听键盘 hm.KeyDown = onKeyboardEvent hm.HookKeyboard() # 监听鼠标 # hm.MouseAll = onMouseEvent # hm.HookMouse() # 循环阻塞监听 pythoncom.PumpMessages() # pythoncom.PumpWaitingMessages() #单次监听,配合while循环,可以方便控制停止 # 检查是否需要终止线程 # if not keyboard_monitor_running: # break if __name__ == "__main__": main() 代码报错C:\Users\2307105076\PythonEnv\Scripts\python.exe E:/SVN/Project_BMS/03_LiXiang/C20250417_LIA_X04C/Trunk/05_Tools/6_CANOE_CAPLTEST/Fast_Charg/182049/Other/2015Plus/temp.py Traceback (most recent call last): File "E:/SVN/Project_BMS/03_LiXiang/C20250417_LIA_X04C/Trunk/05_Tools/6_CANOE_CAPLTEST/Fast_Charg/182049/Other/2015Plus/temp.py", line 4, in <module> import pythoncom File "C:\Users\2307105076\PythonEnv\lib\site-packages\pythoncom.py", line 2, in <module> import pywintypes File "C:\Users\2307105076\PythonEnv\lib\site-packages\win32\lib\pywintypes.py", line 126, in <module> __import_pywin32_system_module__("pywintypes", globals()) File "C:\Users\2307105076\PythonEnv\lib\site-packages\win32\lib\pywintypes.py", line 52, in __import_pywin32_system_module__ import _win32sysloader ImportError: DLL load failed while importing _win32sysloader: 找不到指定的模块。 进程已结束,退出代码为 1
06-28
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值