如果打开了对话框或excel正在等待用户,则win32com中的被呼叫者拒绝了呼叫 [英] Call was rejected by callee in win32com if a dialog box is open or excel is otherwise waiting for the user

查看:415
本文介绍了如果打开了对话框或excel正在等待用户,则win32com中的被呼叫者拒绝了呼叫的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要确定Excel是否准备好在Python中接受来自win32com的COM对象.例如,如果在excel中打开一个对话框,则对win32com函数的任何调用都将导致呼叫被被呼叫者拒绝"错误.

I need to determine if Excel is ready to accept a COM object from win32com in Python. For instance if a dialog box is open in excel, any call to a win32com function will cause a 'Call was rejected by callee' error.

通过反复试验,我发现如果Excel(实际上我认为任何Office产品都已打开)都打开了对话框,则对win32com的任何调用都将导致错误.

Through trial and error I have found that if Excel ( actaully I assume any Office product ) has a dialog box open, any call to win32com will result in an error.

经过大量的搜索之后,我发现了很多有关打开自我设置对话框的问题.即执行Excel.SaveAs()将在工作表上打开一个对话框,您将被卡住,直到用户关闭它为止.

After quite a bit of googling I have found many questions about what amounts to self inflicted dialog boxes being open. i.e. doing a Excel.SaveAs() will open a dialog box on the sheet and you are stuck until the user closes it.

就我而言,我有一个用户打开了一个对话框,或者以其他方式与Excel进行了交互,并使其等待输入.简单的开始在公式栏上输入公式会导致win32com函数返回错误.

In my case I have a user that has opened a dialog box or in some otherway has interacted with Excel and left it waiting for input. Something as simple a starting to enter a formula on the formula bar will cause the win32com function to return with an error.

几个问题: 有没有一种方法可以确定Excel是否已准备好执行命令? 有没有办法告诉打开的盒子是什么(excel还等什么?) 有没有一种方法可以通过win32com关闭盒子...请记住,据我所知,使用win32com进行的任何操作都会在此状态下返回错误

So couple of questions: Is there a way to determine if Excel is ready for a command? Is there a way to tell what box is open (what is excel waiting for?) Is there a way to close the box through win32com ... remember that as far as I can tell anything I do with win32com will return an error when it is in this state

我知道我可以尝试:catch:但是我需要在每个win32com函数周围(此时有很多函数).我认为这种方法会使代码不必要地冗长和复杂.

I know I could do a try: catch: but I would need that around every win32com function ( at this point there are a lot of them). I think that that approach would make the code unnecessarily long and complex.

推荐答案

我一直在努力解决相同的问题,但现在我已经提出了一种对我有效的解决方案.

I have been struggling with the same problem, but now I have made a solution that works for me so far.

我创建了一个类ComWrapper,用于包装Excel COM对象.它将自动包装每个嵌套对象并在ComWrapper中进行调用,并在将它们用作函数调用或对包装对象的赋值时对其进行拆包.包装器通过捕获呼叫被被呼叫者拒绝"例外并重试该呼叫直到达到顶部定义的超时来工作.如果达到了超时,则最终将异常抛出到包装对象之外.

I created a class, ComWrapper, that I wrap the Excel COM object in. It automatically wraps every nested object and call in ComWrapper, and unwraps them when they are used as arguments to function calls or assignments to wrapped objects. The wrapper works by catching the "Call was rejected by callee"-exceptions and retrying the call until the timeout defined at the top is reached. If the timeout is reached, the exception is finally thrown outside the wrapper object.

对包装对象的函数调用由_com_call_wrapper函数自动包装,这就是魔术发生的地方.

Function calls to wrapped objects are automatically wrapped by a function _com_call_wrapper, which is where the magic happens.

要使其工作,只需使用ComWrapper包装来自Dispatch的com对象,然后像在代码底部一样照常使用它.如有问题请发表评论.

To make it work, just wrap the com object from Dispatch using ComWrapper and then use it as usual, like at the bottom of the code. Comment if there are problems.

import win32com.client
from pywintypes import com_error
import time
import logging

_DELAY = 0.05  # seconds
_TIMEOUT = 60.0  # seconds


def _com_call_wrapper(f, *args, **kwargs):
    """
    COMWrapper support function. 
    Repeats calls when 'Call was rejected by callee.' exception occurs.
    """
    # Unwrap inputs
    args = [arg._wrapped_object if isinstance(arg, ComWrapper) else arg for arg in args]
    kwargs = dict([(key, value._wrapped_object)
                   if isinstance(value, ComWrapper)
                   else (key, value)
                   for key, value in dict(kwargs).items()])

    start_time = None
    while True:
        try:
            result = f(*args, **kwargs)
        except com_error as e:
            if e.strerror == 'Call was rejected by callee.':
                if start_time is None:
                    start_time = time.time()
                    logging.warning('Call was rejected by callee.')

                elif time.time() - start_time >= _TIMEOUT:
                    raise

                time.sleep(_DELAY)
                continue

            raise

        break

    if isinstance(result, win32com.client.CDispatch) or callable(result):
        return ComWrapper(result)
    return result


class ComWrapper(object):
    """
    Class to wrap COM objects to repeat calls when 'Call was rejected by callee.' exception occurs.
    """

    def __init__(self, wrapped_object):
        assert isinstance(wrapped_object, win32com.client.CDispatch) or callable(wrapped_object)
        self.__dict__['_wrapped_object'] = wrapped_object

    def __getattr__(self, item):
        return _com_call_wrapper(self._wrapped_object.__getattr__, item)

    def __getitem__(self, item):
        return _com_call_wrapper(self._wrapped_object.__getitem__, item)

    def __setattr__(self, key, value):
        _com_call_wrapper(self._wrapped_object.__setattr__, key, value)

    def __setitem__(self, key, value):
        _com_call_wrapper(self._wrapped_object.__setitem__, key, value)

    def __call__(self, *args, **kwargs):
        return _com_call_wrapper(self._wrapped_object.__call__, *args, **kwargs)

    def __repr__(self):
        return 'ComWrapper<{}>'.format(repr(self._wrapped_object))


_xl = win32com.client.dynamic.Dispatch('Excel.Application')
xl = ComWrapper(_xl)

# Do stuff with xl instead of _xl, and calls will be attempted until the timeout is
# reached if "Call was rejected by callee."-exceptions are thrown.

这篇关于如果打开了对话框或excel正在等待用户,则win32com中的被呼叫者拒绝了呼叫的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆