assert(SUCCEEDED(hr));
hr = SetErrorInfo(0, pei);
// release resources and return a SEVERIT YERROR result
// высвобождаем ресурсы и возвращаем результат
// SEVERITY ERROR (серьезность ошибки)
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 == II DIAnimal || riid == IID ICat || riid == II DIDog || riid == IID IPug) return SOK;
else return S FALSE;
}
Ниже приведен пример клиента, который надежно обрабатывает исключения, используя 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(pszSource));
assert(SUCCEEDED(hr));
if (pszDesc) hr=pcei->SetDescription(constcast(pszDesc));
assert(SUCCEEDED(hr));
if (pszHelpFile) hr=pcei->SetHelpFile(constcast(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;
}
Заметим, что реализация метода заботится о том, чтобы не позволить чисто С++-исключениям переходить через границы метода. Таково безусловное требование СОМ.
В этой главе была представлена концепция интерфейса СОМ. Интерфейсы СОМ обладают простыми двоичными сигнатурами, которые позволяют любому клиенту обращаться к объекту независимо от языка программирования, использованного клиентом или конструктором объекта. Чтобы облегчить поддержку различных языков, интерфейсы СОМ определяются на языке IDL (Interface Definition Language). Эти IDL-определения интерфейса могут быть также использованы для генерирования кода передачи данных (communications code), который позволяет получать доступ к объекту через сеть.
Читать дальше