Изменить стиль страницы

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), который позволяет получать доступ к объекту через сеть.

Большая часть этой главы была посвящена IUnknown – базовому интерфейсу, на котором построен весь СОМ. Все интерфейсы СОМ должны наследовать от IUnknown. Следовательно, все объекты СОМ должны реализовывать IUnknown. В IUnknown предусмотрено три сигнатуры метода, посредством которых клиент может безошибочно управлять иерархией типов объекта для доступа к дополнительным возможностям, предоставляемым этим объектом. С учетом этого QueryInterface можно рассматривать как оператор приведения типа в СОМ. По этой же причине IUnknown можно рассматривать как «void *» (указатель на пустой тип) среди указателей интерфейса, так как от него не слишком много пользы до тех пор, пока он не «приведен» (is «cast») к чему-нибудь более содержательному с помощью QueryInterface.

Следует заметить, что при обращении или реализации IUnknown не было сделано никаких существенных системных вызовов. В этом смысле IUnknown просто является протоколом или набором обещаний (promises), которого должны придерживаться все программы. Это позволяет объектам СОМ быть очень простыми и эффективными. Реализация IUnknown в C++ требует всего нескольких строк стандартного кода. Чтобы автоматизировать реализацию IUnknown в C++, была представлена серия макросов для препроцессора, которые реализуют QueryInterface под табличным управлением. Хотя эти макросы не были совершенно необходимыми, они удаляли большую часть общего стандартного кода из каждого определения класса, не внося при этом заметных усложнений в реализацию.

Глава 3. Классы

int cGorillas = Gorilla::GetCount();

IApe *pApe = new Gorilla();

pApe->GetYourStinkingPawsOffMeYouDamnDirtyApe();

Charleton Heston, 1968

В предыдущей главе обсуждались принципы интерфейсов СОМ вообще и интерфейс IUnknown в частности. Были показаны способы управления указателями интерфейса из C++, и детально обсуждалась фактическая техника реализации IUnknown. Однако не обсуждалось, как обычно клиенты получают начальный указатель интерфейса на объект, или как средства реализации объекта допускают, чтобы их объекты могли быть обнаружены внешними клиентами. В данной главе демонстрируется, как реализации объектов СОМ интегрируют в среду выполнения СОМ, чтобы дать клиентам возможность найти или создать объекты требуемого конкретного типа.