Как я могу вернуть как строку ошибки, так и код ошибки в VB6 из элемента управления activex ATL?
-
21-08-2019 - |
Вопрос
Я пытаюсь вернуть подробную ошибку в VB6, используя CComCoClass::Ошибка, но, похоже, я могу вернуть только код ошибки / или / сообщение - но не оба сразу.
return Error(_T("Not connected"), __uuidof(IMyInterface), HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID));
приводит к общему сообщению об ошибке "Запрос метода объекта IMyInterface не удался" в Err.Description на стороне VB6 (но ERROR_CONNECTION_INVALID в Err.Number), в то время как
return Error(_T("Not connected"));
выдает соответствующее сообщение об ошибке, но общий код ошибки в Err.Number.Как я могу получить лучшее из обоих миров?
Решение
Вы не можете, похоже, это сделано специально.Подробности ниже, но вкратце у вас есть три варианта:
- Возвращает отсутствие сообщения и COM-ошибку, дружественную к VB, т.е.один хорошо известный средой выполнения VB в соответствии с этим Статья в КБ;среда выполнения VB преобразует эту "COM-ошибку" в сообщение VB error plus.
- Возвращает сообщение об ошибке и DISP_E_EXCEPTION;среда выполнения VB пропустит эту "Ошибку сервера" и ваше пользовательское сообщение об ошибке.Это то, что неявно происходит в вашем втором примере, подробности см. Ниже.
- Не возвращает сообщение и любую другую COM-ошибку, т.е.один неизвестный с помощью среды выполнения VB;среда выполнения VB будет использовать необработанный HRESULT плюс общее сообщение "
Method '~' of object '~' failed
".- Пожалуйста, обратите внимание, что это поведение во время выполнения также применимо, если вы предоставляете здесь сообщение об ошибке, т.е.ваше сообщение будет просто проигнорировано!Это то, что происходит в вашем первом примере, подробности смотрите ниже.
Для поставленной задачи все сводится к двум вариантам:
- Если вы хотите предоставить контекстуально корректные "COM-ошибки" для клиентов автоматизации, таких как VB (и, вероятно, вам следует это сделать), вы должны опустить пользовательские сообщения об ошибках.
- Если вы хотите предоставить пользовательские сообщения об ошибках для "Ошибок сервера" (т.е.пользовательские условия ошибки, касающиеся функциональности внутри ваш сервер автоматизации) ваш единственный вариант - DISP_E_EXCEPTION.
Подробные сведения
Среда выполнения VB, по-видимому, предлагает только очень ограниченную обработку ошибок COM.Вероятно, это связано с историческими и / или техническими причинами, специфичными для способа реализации VB, и не представляющими особого интереса здесь (ключевыми словами будут только IDispatch против двойного интерфейса и ActiveX как "подмножества" COM).
Хотя мне не удалось найти явную спецификацию описанного выше поведения, это можно понять, покопавшись в других источниках:
Из самого Статья в КБ просто мечтатель уже указывалось:
[...] выполняется вызов метода GetErrorInfo для получения доступной информации об ошибке.Затем среда выполнения определяет, имеет ли bstrDescription значение, отличное от NULL.Если среда выполнения находит значение , отличное от NULL, [...], в этом сценарии используется исходное значение HRESULT .Если среда выполнения находит нулевое значение, [...] Затем Visual Basic использует HRESULT для поиска соответствующей ошибки Visual Basic.
Это объясняет поведение относительно вашего первого примера:вы действительно отправили сообщение об ошибке, поэтому среда выполнения просто прибегает к своему общему сообщению "Method '~' of object '~' failed
" плюс твой HRESULT
.
Поведение вашего второго примера также будет последовательным, если вы посмотрите на определение (первого в списке) конструктора для CComCoClass::Error
:он имеет значения по умолчанию для не указанных параметров, особенно 'hRes = 0'.В разделе "Примечания" далее говорится, что "Если hRes равен нулю, то первые четыре версии Error возвращают DISP_E_EXCEPTION.".Следовательно, это неявно запускает сквозное поведение "Ошибка сервера".
Наконец, для конкретного примера реализации C ++ поведения клиента автоматизации, подобного VB, смотрите, например, параграфы "Обработка ошибок" и следующее "Упражнение 5" в Автоматизация Microsoft Office 97 и Microsoft Office 2000.
Другие советы
Выведите класс, который реализует ваш COM-интерфейс, из ISupportErrorInfoImpl, вызовите SetErrorInfo, чтобы задать подробное объяснение ошибки, если таковая возникнет.Не забудьте включить ISupportErrorInfo в COM_MAP вашего класса.
Я тоже сейчас борюсь с этим.Пока что мои раскопки показывают, что код ошибки на самом деле является значением HRESULT.VB6 пытается быть умным и интерпретировать HRESULT, но, похоже, у него довольно ограниченный список результатов, которые он понимает.Для HRESULTs, с которыми VB6 не знаком, он просто помещает HRESULT в свойство Err.Number и надеется, что разработчик достаточно умен, чтобы понять, что с ним делать.
Ближе всего я подошел к возвращению номера ошибки, используя MAKE_SCODE для генерации HRESULT с полем code в HRESULT, установленным на то, что я хочу, установленным флагом серьезности и, я надеюсь, правильным средством.
Это в сочетании с CreateErrorInfo и SetErrorInfo выдает мне код ошибки и описание ошибки в VB6.И это возвращает нас к тому, что VB6 пытается быть умным с ограниченным списком ошибок.
Ознакомьтесь с этой статьей http://support.microsoft.com/kb/827994.Таким образом, ваш объект должен реализовать метод ISupportsErrorInfo::InterfaceSupportsErrorInfo(), который возвращает S_OK.и затем перед возвратом вы должны вызвать SetErrorInfo с указателем на COM-объект, который реализует IErrorInfo::getDescription().Здесь есть пример:http://msdn.microsoft.com/en-us/library/ms221409.aspx.
Если вы установите errorInfo перед возвратом, VB запросит метод getDescription указателя объекта, который вы передали SetErrorInfo.
Я не слишком разбираюсь в используемом вами атрибутивном коде - я бы предпочел протестировать его, используя более сырой COM, который, безусловно, всегда представляет собой много шаблонного кода, - но, по крайней мере, он работает, тогда вы могли бы использовать вместо него сложные оболочки.