10

当我的脚本同时更新一个 excel 时,如果我要手动执行任何其他工作,另一个 excel 错误发生我正在使用调度

     from win32com.client import Dispatch

     excel    = Dispatch('Excel.Application')
excel.Visible   = True 

file_name="file_name.xls"
workbook = excel.Workbooks.Open(file_name)
workBook  = excel.ActiveWorkbook
sheet=workbook.Sheets(sheetno)

我收到这样的错误 (, com_error(-2147418111, 'Call was denied by callee.', None, None)

有什么办法可以克服它..我可以更新另一个excel而不会出错..

4

4 回答 4

11

我最近遇到了同样的问题。虽然听起来可能有多个根本原因,但我的情况正在发生,因为 Python 进行后续调用的速度太快,Excel 跟不上,尤其是在外部查询刷新时。time.sleep()我通过在我的大多数呼叫之间插入并为任何特别长的呼叫(通常在 7-15 秒之间)增加 sleep 参数,解决了这个间歇性的“呼叫被被呼叫者拒绝”错误。这使 Excel 有时间在 Python 发出其他命令之前完成每个命令。

于 2017-07-20T23:54:25.853 回答
7

发生此错误的原因是,如果您正在调用的 COM 对象已经在处理另一个操作,它将拒绝外部调用。没有对调用的异步处理,并且行为似乎是随机的。

根据操作,您将看到 pythoncom.com_error 或 pywintypes.com_error。解决此问题的一种简单(如果不优雅)的方法是使用 try-except 将调用包装到 COM 对象中,如果遇到这些访问错误之一,请重试调用。

有关背景知识,请参阅Mark Hammond 和 Andy Robinson的第 12 章摘自Python Programming on Win32的“错误处理”部分(O'Reilly 2000)。

Siew Kam Onn 的博文“Python 编程与 Excel,如何克服 makepy 生成的 python 文件中的 COM_error”中也有一些关于 Excel 的有用信息。

于 2011-03-22T23:19:27.063 回答
2

我一直在为同样的问题而苦苦挣扎,但现在我已经制定了一个迄今为止对我有用的解决方案。

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

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

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

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.

我在这里对一个较新的问题给出了相同的答案: https ://stackoverflow.com/a/55892457/2828033

于 2019-05-01T05:10:58.373 回答
0

我运行密集的 Excel 表,在计算周期运行时始终显示此(阻塞)错误。

解决方案是使用 for 循环。

我提供了有效的代码解决方案部分:

# it failed, keep trying
attempt_number = 0
reading_complete = False
while reading_complete==False:
    try:
        workbook = xw.Book(file_to_read)
        reading_complete = True
        print('file read...')
    except:
        reading_complete = False
        attempt_number += 1
        print('attempt:', attempt_number)
        if attempt_number > 5:
            print('no good: exiting the process')
            exit()

在哪里:

  • file_to_read是 Excel 工作簿的完整路径和名称。
  • attempt_number变量设置为限制尝试次数。
于 2021-11-26T15:17:02.897 回答