【问题标题】:Passing additional arguments to python callback object (win32com.client.dispatchWithEvents)将附加参数传递给 python 回调对象 (win32com.client.dispatchWithEvents)
【发布时间】:2014-06-14 00:49:10
【问题描述】:

我正在使用 win32com 包与 windows 应用程序交互(应用程序并不重要)。

简而言之,我想要实现的是订阅更新的表。

我已经成功实现了一个回调,它在更新表时接收返回的数据,但我现在需要对收到的数据采取行动。

如果我可以使用附加参数实例化回调对象,这个问题将很容易解决(见下面的代码)但我不知道如何做到这一点。


回调类:

class callBackEvents(object):
    """ Callback Object for win32com
    """

    def OnNewData(self, XMLData):
        logging.info("Subscription returned information")
        print "HERE : {}".format(XMLData))
        
        # Would like to use some argument to access logic
        # For how to use the new data  

    def OnActionResult(self, job, msg):
        return True

    def OnServerDisconnect(self):
        logging.debug("Server Disconnected")

    def OnServerConnect(self):
        logging.debug("Trader Connected To Server")

实例化回调对象:

# Instantiate API com object
self.app = win32com.client.DispatchWithEvents("WindowsApplication" callBackEvents)
# I would like to give the callback object extra arguments e.g. callBackEvents(params)

编辑 实例化回调对象:

# Instatiate two com objects
self.com1 = win32com.client.DispatchWithEvents("WindowsApplication" callBackEvents)
self.com2 = win32com.client.DispatchWithEvents("WindowsApplication" callBackEvents)

# Create multiple subscriptions (Note these are asynchronous)
# Pushing the subscribed info is not a problem and done elsewhere
self.com1.Subscribe(<subscription info>)
self.com2.Subscribe(<subscription info>)

现在,当订阅信息遇到回调对象时,我不知道是哪个 com 对象设置了订阅(我可以根据返回的信息猜测,但是在设置相同的订阅时这会导致问题)

【问题讨论】:

标签: python callback arguments win32com


【解决方案1】:

我遇到了同样的问题,最终增加了 DispatchWithEvents。请看下面我的解决方案(我认为它更优雅):

from win32com.client import Dispatch
from win32com.client import gencache
from win32com.client import getevents
from win32com.client import EventsProxy
import pythoncom

def _event_setattr_(self, attr, val):
    try:
        # Does the COM object have an attribute of this name?
        self.__class__.__bases__[0].__setattr__(self, attr, val)
    except AttributeError:
        # Otherwise just stash it away in the instance.
        self.__dict__[attr] = val

def DispatchWithEvents(clsid, user_event_class, arguments):
    # Create/Get the object.
    disp = Dispatch(clsid)
    if not disp.__class__.__dict__.get("CLSID"): # Eeek - no makepy support - try and build it.
        try:
            ti = disp._oleobj_.GetTypeInfo()
            disp_clsid = ti.GetTypeAttr()[0]
            tlb, index = ti.GetContainingTypeLib()
            tla = tlb.GetLibAttr()
            gencache.EnsureModule(tla[0], tla[1], tla[3], tla[4], bValidateFile=0)
            # Get the class from the module.
            disp_class = gencache.GetClassForProgID(str(disp_clsid))
        except pythoncom.com_error:
            raise TypeError("This COM object can not automate the makepy process - please run makepy manually for this object")
    else:
        disp_class = disp.__class__
    # If the clsid was an object, get the clsid
    clsid = disp_class.CLSID
    # Create a new class that derives from 3 classes - the dispatch class, the event sink class and the user class.
    # XXX - we are still "classic style" classes in py2x, so we need can't yet
    # use 'type()' everywhere - revisit soon, as py2x will move to new-style too...
    try:
        from types import ClassType as new_type
    except ImportError:
        new_type = type # py3k
    events_class = getevents(clsid)
    if events_class is None:
        raise ValueError("This COM object does not support events.")
    result_class = new_type("COMEventClass", (disp_class, events_class, user_event_class), {"__setattr__" : _event_setattr_})
    instance = result_class(disp._oleobj_) # This only calls the first base class __init__.
    events_class.__init__(instance, instance)
    args = [instance] + arguments
    if hasattr(user_event_class, "__init__"):
        user_event_class.__init__(*args)
    return EventsProxy(instance)

您的处理程序类必须有一个__init__ 函数并准备好按顺序接受参数:

class Handler_Class():
            def __init__(self, cls):
                self.cls = cls
            def OnItemAdd(self, mail):
                #Check if the item is of the MailItem type
                if mail.Class==43:
                    print("##########",inbox, "##########")
                    print(mail.Subject, " - ", mail.Parent.FolderPath)
                    label = cls.label_email(datetime.now(),mail)
                    print("=======>",label)

你可以这样初始化它:

clsGED = classifier.PersonClassifier()
items = win32com.client.DispatchEx("Outlook.Application").GetNamespace("MAPI").Folders[<emailaddress>].Folders["Inbox"].Items
utilities.DispatchWithEvents(items, Handler_Class, [cls])

正如您可能已经猜到的那样,这里的应用程序用于数据科学项目,其中传入的电子邮件被自动分类,但新的 DispatchWithEvents 方法非常通用,并且接受动态数量的参数。

【讨论】:

    【解决方案2】:

    由于您可能只有一个应用实例,因此只有一个 DispatchWithEvents,您可以简单地将参数设置为该类的成员:

    class callBackEvents(object):
        """ Callback Object for win32com
        """
    
        params = None
    
        def OnNewData(...
    
        ...
    
    # populate the params field
    callBackEvents.params = yourParams
    
    self.app = win32com.client.DispatchWithEvents("WindowsApplication", callBackEvents)
    

    您当然可以将 params 设为全局变量,但您应该仅将全局变量用作最后的手段或用于常量。

    【讨论】:

    • 感谢您的回答@Schollii,对不起,我应该更具描述性(我的第一个 SO 问题):我正在创建两个(或更多)DispatchWithEvents 实例,我有一个创建实例并处理 Windows 应用程序输入的类。我苦苦挣扎的地方是如何使用有关应用程序实例实例化它的信息来实例化 CallBack 对象? -- 非常感谢
    • @JosephHughes 你不能。扩展您的问题以显示您实际尝试做的事情(DispatchWithEvents 的两个实例),我相信我们可以找到替代方案。
    • 我被告知多次订阅会导致我正在测试的应用出现奇怪的行为。因此,我已将您的答案标记为已接受。我还将扩展我的问题,尝试提供更多关于我试图实现的目标的信息,以防将来有人在同样的事情上苦苦挣扎。
    猜你喜欢
    • 1970-01-01
    • 2012-03-23
    • 2021-09-20
    • 2017-11-10
    • 2011-06-29
    • 1970-01-01
    • 2019-08-09
    • 2016-09-19
    • 1970-01-01
    相关资源
    最近更新 更多