Подтвердить что ты не робот

Есть ли способ декодировать числовые коды ошибок COM в pywin32

Вот часть трассировки стека из недавнего запуска ненадежного приложения, написанного на Python, которое управляет другим приложением, написанным в Excel:

pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)

Очевидно, что-то пошло не так... но что? [1] Эти коды ошибок COM кажутся чрезмерно загадочными.

Как я могу декодировать это сообщение об ошибке? Есть ли где-нибудь таблица, которая позволяет мне преобразовать этот цифровой код ошибки в нечто более значимое?

[1] Я действительно знаю, что пошло не так в этом случае, он пытался получить доступ к имени prperty в объекте Range, у которого не было свойства Name... не все ошибки легко найти!

4b9b3361

Ответ 1

Ты не делаешь ничего плохого. Первый элемент в вашей трассировке стека (номер) - это код ошибки, возвращаемый COM-объектом. Второй элемент - это описание, связанное с кодом ошибки, который в данном случае является "Исключением". pywintypes.com_error уже назвал эквивалент win32api.FormatMessage(errCode) для вас. Мы посмотрим на второе число через минуту.

Кстати, вы можете использовать утилиту "Поиск ошибок", которая поставляется в Visual Studio (C:\Program Files\Microsoft Visual Studio 9.0\Common7\Tools\ErrLook.exe) в качестве быстрой стартовой панели для проверки COM-ошибки коды. Эта утилита также вызывает FormatMessage для вас и отображает результат. Не все коды ошибок будут работать с этим механизмом, но многие будут. Это обычно моя первая остановка.

Обработка ошибок и отчетность в COM немного беспорядочны. Я постараюсь дать вам несколько примеров.

Все вызовы COM-метода возвращают числовой код под названием HRESULT, который может указывать на успех или неудачу. Все формы отчетов об ошибках в COM строятся поверх этого.

Коды обычно выражаются в шестнадцатеричном формате, хотя иногда вы увидите их как большие 32-битные числа, например, в вашей трассировке стека. Существуют все типы предопределенных кодов возврата для общих результатов и проблем, или объект может возвращать пользовательские числовые коды для особых ситуаций. Например, значение 0 (называемое S_OK) универсально означает "Нет ошибки", а 0x80000002 - E_OUTOFMEMORY. Иногда коды HRESULT возвращаются объектом, иногда с помощью инфраструктуры COM.

COM-объект также может предоставить гораздо более богатую информацию об ошибках, реализуя интерфейс IErrorInfo. Когда объект реализует IErrorInfo, он может предоставить все виды подробностей о том, что произошло, например подробное пользовательское сообщение об ошибке и даже имя файла справки, описывающего проблему. В VB6 и VBA. объект Err позволяет получить доступ ко всей этой информации (Err.Description и т.д.).

Чтобы усложнить проблему, связанные с COM-объекты, которые используют механизм COM Automation или IDispatch, добавляют некоторые слои, которые необходимо очистить, чтобы получить информацию. Обычно Excel обрабатывается посредством позднего связывания.

Теперь посмотрим на свою ситуацию снова. То, что вы получаете как первое число, является довольно общим кодом ошибки: DISP_E_EXCEPTION. Примечание. Обычно вы можете узнать официальное имя HRESULT, указав номер, хотя иногда вам нужно использовать шестнадцатеричную версию, чтобы найти что-нибудь полезное.

Ошибки, начинающиеся с DISP_, являются кодами ошибок IDISPATCH. Ошибка означает, что "было создано исключение COM, созданное объектом", с дополнительной информацией, упакованной в другое место (хотя я не совсем знаю, где я должен искать).

Из того, что я понимаю в pywintypes.com_error, последнее число в вашем сообщении - это фактический код ошибки, который был возвращен объектом во время исключения. Это фактический цифровой код, который вы выберете из VBA Err.Number.

К сожалению, этот второй код -2146788248 (0x800A9C68) находится в диапазоне, зарезервированном для пользовательских сообщений об ошибках приложения (в VBA: VbObjectError + someCustomErrorNumber), поэтому нет централизованного значения. Такое же число может означать совершенно разные вещи для разных программ.

В этом случае мы достигли тупика:

Код ошибки является "обычным", и приложение должно документировать, что это такое, за исключением того, что Excel этого не делает. Кроме того, Excel (или фактический источник ошибки), похоже, не предоставляет никакой дополнительной информации через IErrorInfo.

Excel печально известен (по крайней мере, мне) для загадочных кодов ошибок из автоматизации и неясных ситуаций, которые их вызывают. Это особенно важно для ошибок, которые можно было бы учитывать "ошибки времени разработки" ( "вы должны были знать лучше, чем вызов метода, который не существует в объекте" ). Вместо хорошего "Не удалось прочитать свойство Name" вы получите " Ошибка времени выполнения" 1004 ": определение приложения или объектная ошибка" (которую я только что получил, пытаясь получить доступ к имени свойство на Range, от VBA в Excel). Это НЕ очень полезно.

Проблема не маршрутизируется на Python или это интерфейс к Excel. Сам Excel не объясняет, что произошло, даже для VBA.

Однако общая процедура выше остается в силе. Если вы получите сообщение об ошибке в Excel в будущем, вы можете получить сообщение об ошибке, которое вы можете отслеживать таким же образом.

Удачи!

Ответ 2

Сделайте это так:

try:
    [whatever code]
except pythoncom.com_error as error:
    print(win32api.FormatMessage(error.excepinfo[5]))

Дополнительная информация о переваривании объекта pythoncom.com_error здесь: http://docs.activestate.com/activepython/3.2/pywin32/com_error.html

Ответ 3

Да, попробуйте модуль win32api:

import win32api
e_msg = win32api.FormatMessage(-2147352567)

Вы можете получить любые коды, возвращенные из исключения, и передать их в FormatMessage. В вашем примере было 2 кода ошибок.

Ответ 4

В частности, для pythoncom коды ошибок, которые являются результатом, более чем загадочны. Это связано с тем, что pythoncom представляет их внутренне как 32-битное целое число со знаком, когда правильное представление представляет собой 32-битное целое число без знака. В результате преобразование, которое вы видите в трассировке стека, неверно.

В частности, ваше исключение, в соответствии с pythoncom, равно -2147352567, а ваше (из-за отсутствия лучшего слова) Err.Number - -2146788248.

Однако это вызывает некоторые проблемы при просмотре определенных ошибок, например ниже:

DISP_E_EXCEPTION = 0x80020009
#...
#except pywintypes.com_error as e:
#    print repr(e)
#    #pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)
#    hr = e.hresult

hr = -2147352567
if hr == DISP_E_EXCEPTION:
    pass #This never occurs
else:
    raise

Чтобы узнать, почему у этого есть проблемы, рассмотрим эти коды ошибок:

>>> DISP_E_EXCEPTION = 0x80020009
>>> DISP_E_EXCEPTION
2147614729L
>>> my_hr = -2147352567
>>> my_hr == DISP_E_EXCEPTION
False

Опять же, это связано с тем, что python видит константу, объявленную как положительную, а неправильное объявление pythoncom интерпретирует ее как отрицательное. Конечно, наиболее очевидное решение не выполняется:

>>> hex(my_hr)
'-0x7ffdfff7'

Решение должно правильно интерпретировать число. К счастью, представление pythoncom обратимо. Нам нужно интерпретировать отрицательное число как 32-битное целое число со знаком, а затем интерпретировать его как целое без знака:

def fix_com_hresult(hr):
    import struct
    return struct.unpack("L", struct.pack("l", hr))[0]

>>> DISP_E_EXCEPTION = 0x80020009
>>> my_hr = -2147352567
>>> my_hr == DISP_E_EXCEPTION
False
>>> fixed_hr = fix_com_hresult(my_hr)
>>> fixed_hr
2147614729L
>>> fixed_hr == DISP_E_EXCEPTION
True

Итак, все вместе, вам нужно запустить fix_com_hresult() для этого результата из pythoncom, по существу все время.

Так как обычно вам нужно это делать, проверяя исключения, я создал следующие функции:

def fix_com_exception(e):
    e.hresult = fix_com_hresult(e.hresult)
    e.args = [e.hresult] + list(e.args[1:])
    return e

def fix_com_hresult(hr):
    import struct
    return struct.unpack("L", struct.pack("l", hr))[0]

который затем можно использовать, как вы ожидаете:

DISP_E_EXCEPTION = 0x80020009
try:
    #failing call
except pywintypes.com_error as e:
    print repr(e)
    #pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)
    fix_com_exception(e)
    print repr(e)
    #pywintypes.com_error: (2147614729L, 'Exception occurred.', (0, None, None, None, 0, -2146788248), None)
    if e.hresult == DISP_E_EXCEPTION:
        print "Got expected failure"
    else:
        raise

Мне не удалось найти документ MSDN, в котором перечислены все HRESULT, но я нашел это: http://www.megos.ch/support/doserrors_e.txt

Кроме того, поскольку у вас есть это, fix_com_hresult() также должен запускаться в расширенном коде ошибки (-2146788248), но, как сказал Euro Micelli, это не поможет вам в этом конкретном случае:)

Ответ 5

Никто еще не упомянул атрибут strerror pywintypes.com_error Exception. Это возвращает результат FormatMessage для кода ошибки. Поэтому вместо того, чтобы делать это самостоятельно,

try:
    [whatever code]
except pythoncom.com_error as error:
    print(win32api.FormatMessage(error.excepinfo[5]))

Вы можете просто сделать это:

try:
    [whatever code]
except pythoncom.com_error as error:
    print(error.strerror)

Обратите внимание, что он вернет None, если у вас есть нестандартный HRESULT: (