}
Специальные объекты исключений могут выбрать другие специфические для исключений интерфейсы в дополнение к IErrorInfo.
СОМ предусматривает по умолчанию реализацию IErrorInfo, которую можно создать с использованием API-функции СОМ CreateErrorInfo:
HRESULT CreateErrorInfo([out] ICreateErrorInfo **ppcei);
В дополнение к IErrorInfo объекты исключений по умолчанию открывают интерфейс ICreateErrorInfo, чтобы позволить пользователю инициализировать состояние нового исключения:
[ object, uuid(22F03340-547D-101B-8E65-08002B2BD119) ]
interface ICreateErrorInfo: IUnknown
{
// set IID of interface that threw exception
// устанавливаем IID интерфейс, который возбудил исключение
HRESULT SetGUID([in] REFGUID rGUID);
// set class name of object that threw exception
// устанавливаем классовое имя объекта, который возбудил исключение
HRESULT SetSource([in, string] OLECHAR* pwszSource);
// set human-readable description of exception
// устанавливаем читабельное описание исключения
HRESULT SetDescription([in, string] OLECHAR* pwszDesc);
// set WinHelp filename of documentation of error
// устанавливаем имя файла WinHelp, содержащего документацию об ошибке
HRESULT SetHelpFile([in, string] OLECHAR* pwszHelpFile);
// set WinHelp context ID for documentation of error
// устанавливаем идентификатор контекста WinHelp для документации ошибки
HRESULT SetHelpContext([in] DWORD dwHelpContext);
}
Заметим, что этот интерфейс просто позволяет пользователю заполнить объект исключения пятью основными атрибутами, доступными из интерфейса IErrorInfo.
Следующая реализация метода выбрасывает СОМ-исключение своему вызывающему объекту, используя объект исключений по умолчанию:
STDMETHODIMP PugCat::Snore(void)
{
if (this->IsAsleep())
// ok to perform operation?
// можно ли выполнять операцию?
return this->DoSnore();
//do operation and return
// выполняем операцию и возвращаемся
//otherwise create an exception object
// в противном случае создаем объект исключений
ICreateErrorInfo *рсеi = 0; HRESULT hr = CreateErrorInfo(&pcei);
assert(SUCCEEDED(hr));
// initialize the exception object
// инициализируем объект исключений
hr = pcei->SetGUID(IIDIPug);
assert(SUCCEEDED(hr));
hr = pcei->SetSource(OLESTR(«PugCat»));
assert(SUCCEEDED(hr));
hr = pcei->SetDescription(OLESTR("I am not asleep!"));
assert(SUCCEEDED(hr));
hr = pcei->SetHelpFile(OLESTR(«C:\\PugCat.hlp»));
assert(SUCCEEDED(hr));
hr = pcei->SetHelpContext(5221);
assert(SUCCEEDED(hr));
// «throw» exception
// «выбрасываем» исключение
IErrorInfo *pei = 0;
hr = pcei->QueryInterface(IIDIErrorInfo, (void**)&pei);
assert(SUCCEEDED(hr));
hr = SetErrorInfo(0, pei);
// release resources and return a SEVERITYERROR result
// высвобождаем ресурсы и возвращаем результат
// SEVERITYERROR (серьезность ошибки)
pei->Release();
pcei->Release();
return PUGEPUGNOTASLEEP;
}
Отметим, что поскольку объект исключений передается в процедуру SetErrorInfo, СОМ сохраняет ссылку на исключение до тех пор, пока оно не будет «перехвачено» вызывающим объектом, использующим GetErrorInfo.
Объекты, которые сбрасывают исключения СОМ, должны использовать интерфейс ISupportErrorInfo , чтобы показывать, какие интерфейсы поддерживают исключения. Этот интерфейс используется клиентами, чтобы установить, верный ли результат дает GetErrorInfo[2]. Этот интерфейс предельно прост:
[ object, uuid(DFOB3060-548F-101B-8E65-08002B2BD119) ]
interface ISupportErrorInfo: IUnknown
{
HRESULT InterfaceSupportsErrorInfo([in] REFIID riid);
}
Предположим, что класс PugCat, рассмотренный в этой главе, сбрасывает исключения из каждого поддерживаемого им интерфейса. Тогда его реализация будет выглядеть так:
STDMETHODIMP PugCat::InterfaceSupportsErrorInfo(REFIID riid)
{
if (riid == IIDIAnimal || riid == IIDICat || riid == IIDIDog || riid == IIDIPug) return SOK;
else return SFALSE;
}
Ниже приведен пример клиента, который надежно обрабатывает исключения, используя ISupportErrorInfo и GetErrorInfo:
void TellPugToSnore(/*[in]*/ IPug *pPug)
{
// call a method
// вызываем метод
HRESULT hr = pPug->Snore();
if (FAILED(hr))
{
// check to see if object supports СОМ exceptions
// проверяем, поддерживает ли объект исключения СОМ
ISupportErrorInfo *psei = 0;
HRESULT hr2 =pPug->QueryInterface( IIDISupportErrorInfo, (void**)&psei);
if (SUCCEEDED(hr2))
{
// check if object supports СОМ exceptions via IPug methods
// проверяем, поддерживает ли объект исключения СОМ через методы
IPug hr2 = psei->InterfaceSupportsErrorInfo(IIDIPug);
if (hr2 == SOK)
{
// read exception object for this logical thread
// читаем объект исключений для этого логического потока
IErrorInfo *реi = 0;
hr2 = GetErrorInfo(0, &pei);
if (hr2 == SOK)
{
// scrape out source and description strings
// извлекаем строки кода и описания
BSTR bstrSource = 0, bstrDesc = 0;
hr2 = pei->GetDescription(&bstrDesc);
assert(SUCCEEDED(hr2));
hr2 = pei->GetSource(&bstrSource);
assert(SUCCEEDED(hr2));
// display error information to end-user
// показываем информацию об ошибке конечному пользователю
MessageBoxW(0, bstrDesc ? bstrDesc : L"«, bstrSource ? bstrSource : L»", MBOK);
// free resources
// высвобождаем ресурсы
SysFreeString(bstrSource);
SysFreeString(bstrDesc);
pei->Release();
}
}
psei->Release();
}
}
if (hr2!= SOK)
// something went wrong with exception
// что-то неладно с исключением
MessageBox(0, «Snore Failed», «IPug», MBOK);
}
Довольно просто отобразить исключения СОМ в исключения C++, причем в любом месте. Определим следующий класс, который упаковывает объект исключений СОМ и HRESULT в один класс C++:
struct COMException
{
HRESULT mhresult;
// hresult to return
// hresult для возврата IErrorInfo *mpei;
// exception to throw
// исключение для выбрасывания
COMException(HRESULT hresult, REFIID riid, const OLECHAR *pszSource, const OLECHAR *pszDesc, const OLECHAR *pszHelpFile = 0, DWORD dwHelpContext = 0)
{
// create and init an error info object
// создаем и инициализируем объект информации об ошибке
ICreateErrorInfo *рсеi = 0;
HRESULT hr = CreateErrorInfo(&pcei);
assert(SUCCEEDED(hr));
hr = pcei->SetGUID(riid);
assert(SUCCEEDED(hr));
if (pszSource) hr=pcei->SetSource(constcast<OLECHAR*>(pszSource));
assert(SUCCEEDED(hr));
if (pszDesc) hr=pcei->SetDescription(constcast<OLECHAR*>(pszDesc));
assert(SUCCEEDED(hr));
if (pszHelpFile) hr=pcei->SetHelpFile(constcast<OLECHAR*>(pszHelpFile));
assert(SUCCEEDED(hr));
hr = pcei->SetHelpContext(dwHelpContext);
assert(SUCCEEDED(hr));
// hold the HRESULT and IErrorInfo ptr as data members
// сохраняем HRESULT и указатель IErrorInfo как элементы данных
mhresult = hresult;
hr=pcei->QueryInterface(IIDIErrorInfo, (void**)&mpei);
assert(SUCCEEDED(hr));
pcei->Release();
}
};
С использованием только что приведенного С++-класса COMException показанный выше метод Snore может быть модифицирован так, чтобы он преобразовывал любые исключения C++ в исключения СОМ:
STDMETHODIMP PugCat::Snore(void)
{
HRESULT hrex = SOK;
try
{
if (this->IsAsleep()) return this->DoSnore();
else throw COMException(PUGEPUGNOTASLEEP, IIDIPug, OLESTR(«PugCat»), OLESTR(«I am not asleep!»));
}
catch (COMException& ce)
{
// a C++ COMException was thrown
// было сброшено исключение COMException C++
HRESULT hr = SetErrorInfo(0, ce.mpei);
assert(SUCCEEDED(hr));
ce.mpei->Release();
hrex = ce.mhresult;
}
catch (…)
{
// some unidentified C++ exception was thrown
// было выброшено какое-то неидентифицированное исключение C++
COMException ex(EFAIL, IIDIPug, OLESTR(«PugCat»), OLESTR(«A C++ exception was thrown»));
HRESULT hr = SetErrorInfo(0, ex.mpei);
assert(SUCCEEDED(hr));
ex.mpei->Release();
hrex = ex.mhresult;
}
return hrex;
}
Заметим, что реализация метода заботится о том, чтобы не позволить чисто С++-исключениям переходить через границы метода. Таково безусловное требование СОМ.