COM и исключения
От: Кодт Россия  
Дата: 26.01.05 15:11
Оценка:
Вопрос очень простой: как правильно кидать исключения из методов COM-объектов? И что при этом происходит?

Более подробно:
Пишем программу на С++ под Win32 и VxWorks, внутри вовсю практикующую COM.
Виндовский порт использует MS ATL, VxWorks-овский — WindRiver ATL (жалкая пародия на MS).
Выхухоль не содержит ни CComException, ни _com_error, ни всего остального, поэтому придётся все детальки воссоздать. Хочу сделать это правильным способом.

Пока что проект сидит на inproc-серверах, которые имеют общий рантайм. Поэтому, кидая что попало (HRESULT, std::exception и т.п.) через границу метода, я не создам себе ошибок. А вот проблемы масштабирования программы (например, переход к DCOM) — обеспечу с лёгкостью.
Перекуём баги на фичи!
Re: COM и исключения
От: SergH Россия  
Дата: 26.01.05 15:32
Оценка: +1
Здравствуйте, Кодт, Вы писали:

К>Вопрос очень простой: как правильно кидать исключения из методов COM-объектов?


Никак

К>И что при этом происходит?


Все происходит плохо COM же с потоками, а C++ исключения работают только в одном потоке.

К>Пока что проект сидит на inproc-серверах, которые имеют общий рантайм. Поэтому, кидая что попало (HRESULT, std::exception и т.п.) через границу метода, я не создам себе ошибок.


Если есть маршалинг — создашь.

К>А вот проблемы масштабирования программы (например, переход к DCOM) — обеспечу с лёгкостью.


Да, это тем болеее.

По уму, из COM-метода ничего кроме HRESULT возвращать нельзя. А _com_exception это уже код на клиенте так обрабатывает возвращаемое значение метода. Ещё есть интерфейс IErrorInfo, но он актуален, если COM используется на VB, а больше вроде никем почти не используется.
Делай что должно, и будь что будет
Re: COM и исключения
От: Vi2 Удмуртия http://www.adem.ru
Дата: 26.01.05 15:34
Оценка: +1
Здравствуйте, Кодт, Вы писали:

К>Вопрос очень простой: как правильно кидать исключения из методов COM-объектов?


Ответ не менее прост: никак, нельзя кидать исключения за пределы исполняющегося СОМ метода. Т.е. СОМ объекту запрещено кидать исключения. Сообщение об ошибке идет через HRESULT каждого метода.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[2]: COM и исключения
От: Кодт Россия  
Дата: 26.01.05 15:48
Оценка:
Здравствуйте, SergH, Вы писали:

К>>Пока что проект сидит на inproc-серверах, которые имеют общий рантайм. Поэтому, кидая что попало (HRESULT, std::exception и т.п.) через границу метода, я не создам себе ошибок.


SH>Если есть маршалинг — создашь.


Да, забыл сказать, что free-threaded. Поэтому маршалинга нет.

К>>А вот проблемы масштабирования программы (например, переход к DCOM) — обеспечу с лёгкостью.


SH>Да, это тем болеее.


SH>По уму, из COM-метода ничего кроме HRESULT возвращать нельзя. А _com_exception это уже код на клиенте так обрабатывает возвращаемое значение метода. Ещё есть интерфейс IErrorInfo, но он актуален, если COM используется на VB, а больше вроде никем почти не используется.


Я думал, есть какой-то штатный способ маршалить ошибки. В MSDN видел трёп про CreateErrorInfo и т.д., но понял, что это относится к IDispatch.

Ну если нет, то нет
(Буду втихую пользоваться исключениями, а потом избавлюсь от COM всюду где только возможно).
Перекуём баги на фичи!
Re: COM и исключения
От: ssi Россия  
Дата: 27.01.05 17:06
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Вопрос очень простой: как правильно кидать исключения из методов COM-объектов? И что при этом происходит?


К>Более подробно:

К>Пишем программу на С++ под Win32 и VxWorks, внутри вовсю практикующую COM.
К>Виндовский порт использует MS ATL, VxWorks-овский — WindRiver ATL (жалкая пародия на MS).
К>Выхухоль не содержит ни CComException, ни _com_error, ни всего остального, поэтому придётся все детальки воссоздать. Хочу сделать это правильным способом.

К>Пока что проект сидит на inproc-серверах, которые имеют общий рантайм. Поэтому, кидая что попало (HRESULT, std::exception и т.п.) через границу метода, я не создам себе ошибок. А вот проблемы масштабирования программы (например, переход к DCOM) — обеспечу с лёгкостью.


Простите за нескромный вопрос. Почему вообще возникла необходимость писать под VxWorks с использованием COM?
Старый Торнадо на котором мне приходилось работать, врядли бы потянул С++ для еще с STL/ATL/COM, как быстро время бежит... эх... Неужто он так сильно продвинулся?
Знающие не говорят, говорящие не знают. Лао Цзы
Re[3]: COM и исключения
От: Frostbitten Россия  
Дата: 27.01.05 19:37
Оценка: 70 (4)
Здравствуйте, Кодт, Вы писали:

К>Я думал, есть какой-то штатный способ маршалить ошибки. В MSDN видел трёп про CreateErrorInfo и т.д., но понял, что это относится к IDispatch.

Относится только тем, что IErrorInfo наследуется от IDispatch. Больше ничем .

На самом деле все достаточно удобно и прозрачно.

При возникновении ошибки на сервере:
* создаем системный объект, реализующий IErrorInfo и ICreateErrorInfo, вызовом CreateErrorInfo()
* устанавливаем его свойства через IErrorInfo::SetDescription() и т.п.
* получаем у этого объекта IErrorInfo
* устанавливаем его в качестве текущего объекта ошибки с помощью SetErrorInfo и возвращаем HRESULT ошибки. Указатель на текущий объекто ошибки системный и thread-local'ьный, поэтому даже не напрягаемся на счет синхронизации.

(Для проведения вышеиложенного в ATL есть грубенькая, совершенно не гибкая функция ::AtlReportError()).

А в клиенте проверяем возвращенный HRESULT и тупо-глупо получаем устанавленный объект через GetErrorInfo и читаем GetDescription и т.п.

Если объект ошибки создается через CreateErrorInfo, то о маршалинге беспокоиться не надо — все уже есть.

Есть еще опциональные мистические хитрости, связанные с ISupportErrorInfo, но это на любителя, так как даже если кокласс не реализует ISupportErrorInfo, то тот же VB6 прекрасно возвращает информацию об установленной ошибке в своем Err и удаленно информация тоже нормально передается в этом случае.

Основная задача, которую приходиться решать, если все делать грамотно — это протаскивание информации об ошибке вверх по стеку, прописывая по пути информацию о контексте (ala .net Exception.InnerException), на это есть (как минимум :) два подхода.

Либо реализовывать собственный объект ошибки, то есть не вызывать CreateErrorInfo вовсе, а в SetErrorInfo указывать IErrorInfo своего. Собственный объект по мимо IErrorInfo может реализовывать еще что-то (в этом и суть), например Chained Error Info for COM, но в этом случае, конечно, маршалинг тоже весь свой.

Либо в качестве Description передавать не просто описание ошибки, а некоторую текстовую структуру, например xml (это не слишком дружелюбно по отношению к конечному пользователю, зато дружелюбно по отношению к разработчику), где дописывать контекстную информацию (очевдно, что хотя бы какая-то часть стека должна знать и использовать формат строк), например как предлагается в Propagate Error Info: Use ATL and C++ to Implement Error-Handling COM Objects, или, если клиенты будут на VB6, то можно использовать простой формат HuntErr, он не xml, но тоже вполне удобный, в особенности для конечного пользователя (я как-то использовал этот вариант).


К>(Буду втихую пользоваться исключениями, а потом избавлюсь от COM всюду где только возможно).

Совершенно напрасно — на IErrorInfo легко построить передачу ошибок и внутри сервера, то есть обойтись без исключений, но не упираться в возвращаемые значения — зато потом из внутреннего объекта сервера можно будет сделать публичный и все клиенты сразу поймут как принимать от него информацию об ошибках.

P.S.
О скорости сказать ничего не могу. Такой проблемы когда я это делал у меня не стояло, но по ощушениям, вроде не медленнее кидания исключений (но не быстрее возвращения значений ;).
Re[2]: COM и исключения
От: Кодт Россия  
Дата: 28.01.05 09:55
Оценка:
Здравствуйте, ssi, Вы писали:

ssi>Простите за нескромный вопрос. Почему вообще возникла необходимость писать под VxWorks с использованием COM?

ssi>Старый Торнадо на котором мне приходилось работать, врядли бы потянул С++ для еще с STL/ATL/COM, как быстро время бежит... эх... Неужто он так сильно продвинулся?

Не знаю, с какого момента он продвинулся. У нас далеко не самое свежее.
Торнадо II, конечно, редкий тормоз в роли среды кросс-разработки (поэтому насколько возможно, пользуемся VC6).
А на таргете-то — какая разница, тормоз он или нет. Быстрый проц (пень-два или пень-три) — и всё летает.
Медляк возникает при "честной" передаче строк и массивов как out-параметров, но, как выяснило профилирование, это не самое узкое место. Хотя, конечно, если от COM взять только RTTI и сборку мусора — то всё ускорится...

А потребность возникла, потому что хотели делать сеть контроллеров с внешним API — DCOM/RPC (возможно, SOAP). Потом передумали, но было уже поздно
Перекуём баги на фичи!
Re[4]: COM и исключения
От: Кодт Россия  
Дата: 28.01.05 10:40
Оценка: 7 (1)
Здравствуйте, Frostbitten, Вы писали:

К>>(Буду втихую пользоваться исключениями, а потом избавлюсь от COM всюду где только возможно).

F>Совершенно напрасно — на IErrorInfo легко построить передачу ошибок и внутри сервера, то есть обойтись без исключений, но не упираться в возвращаемые значения — зато потом из внутреннего объекта сервера можно будет сделать публичный и все клиенты сразу поймут как принимать от него информацию об ошибках.

F>P.S.

F>О скорости сказать ничего не могу. Такой проблемы когда я это делал у меня не стояло, но по ощушениям, вроде не медленнее кидания исключений (но не быстрее возвращения значений ;).

Просто я думал, что можно бы сделать так.
Пусть есть некий "совместимый" класс исключения, скажем, ComEx.
Если метод бросает исключение, то, пока мы в пределах апартамента, стек раскручивается как обычно. Поймали ниже? Отлично. Не поймали?
— в функции потока есть ловушка catch(...) { много матов и abort(); }
— в прокси/стабе так же есть ловушка
// прокси
HRESULT IXxxx_Foo_Proxy(IXxxx* pXxxx, аргументы)
{
  HRESULT hr;

  ... // замаршалить, вызвать и размаршалить обратно

  if(hr == EXCEPTION_E_COMEX) throw ComEx_From_ErrorInfo();
  if(hr == EXCEPTION_E_OTHER) throw bad_exception("Some unknown exception thrown through the apartment");
  return hr;
}

// стаб
void IXxxx_Foo_Stub(/* вся ботва, которая идёт в стаб */)
{
  CComPtr<IXxx> pXxx;
  HRESULT hr;

  ... // размаршалить

  try
  {
    HRESULT hr = pXxx->Foo(.......);
  }
  catch(ComEx ex)
  {
    SetErrorInfo_From_ComEx(ex);
    hr = EXCEPTION_E_COMEX;
  }
  catch(...)
  {
    hr = EXCEPTION_E_OTHER;
  }

  ... // замаршалить обратно
}

MIDL, кстати сказать, засовывает нечто подобное в TheLibrary_ps.c, но ориентированное на SEH (и это можно понять: proxy/stub — это сишная программа, а не С++ная). VxIDL про SEH ничего не знает, поэтому даже не пытается.
Перекуём баги на фичи!
Re[4]: COM и исключения
От: Vi2 Удмуртия http://www.adem.ru
Дата: 01.02.05 11:31
Оценка:
Здравствуйте, Frostbitten, Вы писали:

К>>Я думал, есть какой-то штатный способ маршалить ошибки. В MSDN видел трёп про CreateErrorInfo и т.д., но понял, что это относится к IDispatch.

F>Относится только тем, что IErrorInfo наследуется от IDispatch. Больше ничем .

Увы, это ошибочно: IErrorInfo НЕ НАСЛЕДУЕТСЯ от IDispatch.
[object,uuid(1CF2B120-547D-101B-8E65-08002B2BD119),pointer_default(unique)] interface IErrorInfo: IUnknown

Он, IErrorInfo, обслуживает Автоматизацию (заполнение структуры EXCEPINFO) и, следовательно, связан с IDispatch-интерфейсами. Вернее, именно с dual-ными интерфейсами: только через дополнительный интерфейс/объект можно было передать информацию об ошибке из виртуального метода.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! © КВН НГУ
Re[5]: COM и исключения
От: Frostbitten Россия  
Дата: 09.02.05 12:07
Оценка:
Здравствуйте, Vi2, Вы писали:

F>>Относится только тем, что IErrorInfo наследуется от IDispatch. Больше ничем .

Vi2>Увы, это ошибочно: IErrorInfo НЕ НАСЛЕДУЕТСЯ от IDispatch.
Оу, подтверждаю, просто я смотрел в MSDNL Oct 2001 (для 6-ки), там недвусмыслено перечислены IDispatch'евые методы в секции "Methods in Vtable Order", что действительно не соответствует реальности. Сейчас посмотрел msdn.microsoft.com — там исправили.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.