IChapter *pc = 0;
HRESULT hr = CoGetObject(OLESTR(«Chapter:7»), О,
IID_IChapter, (void**)&pc);
if (SUCCEEDED(hr)) {
hr = pc->IncludeAllTopicsNotCoveredYet();
pc->Release(); }
Автор, 1997
В предыдущей главе были представлены основы модели программирования СОМ и архитектуры удаленного доступа. Различные интерфейсы и методики СОМ рассматриваются на протяжении всей книги. Однако осталось несколько вопросов, не связанных ни с какой определенной главой, о которых следует рассказать подробно. Вместо того чтобы просто втиснуть эти вопросы в другие главы, которые были скомпонованы рационально или даже превышали разумные размеры, я отвел данную главу под хранилище для «маленьких» тем, которые не всегда подходят к другим частям книги. За исключением вводных разделов об указателях, управлении памятью и массивах, ни одна из этих тем не является жизненно необходимой для создания эффективных распределенных систем с СОМ. Помните об этом и расслабьтесь, в то время как ваши глаза будут скользить вдоль строк этой главы.
СОМ, подобно DCE (Distributed Computing Environment – среда распределенных вычислений), ведет свое начало от языка программирования С. Хотя лишь немногие разработчики используют С для создания или использования компонентов СОМ, именно от С СОМ унаследовала синтаксис для своего языка определений интерфейсов (Interface Definition Language – IDL). Одной из наиболее сложных проблем при разработке и использовании интерфейсов является управление указателями. Рассмотрим такое простое определение метода IDL:
HRESULT f([in] const short *ps);
Если бы вызывающая программа должна была запустить этот метод так:
short s = 10;
HRESULT hr = p->f(&s);
то величину 10 следовало бы послать объекту. Если бы этому методу нужно было выйти за границы апартамента, то интерфейсный заместитель был бы обязан разыменовать указатель и передать величину 10 в сообщение ORPC-запроса.
Следующий клиентский код, хотя и написан целиком в традициях С, представляет собой более интересный случай:
HRESULT hr = p->f(0);
// pass a null pointer
// передаем нулевой указатель
Если вызывающий поток выполняется в апартаменте объекта, то заместителя нет и нулевой указатель будет передан прямо объекту. Но что если объект расположен в другом апартаменте и заместитель используется? Что в точности должен передать интерфейсный заместитель, чтобы показать, что был послан нулевой указатель? Кроме того, означает ли это, что интерфейсные заместители и заглушки должны проверять каждый указатель, не является ли он нулевым? Оказывается, бывают ситуации, в которых указатель никогда не должен быть нулевым, и другие ситуации, когда нулевые указатели, наоборот, чрезвычайно полезны как начальные значения. В последнем случае факт передачи нулевого указателя интерфейсному заместителю должен быть продублирован интерфейсной заглушкой в апартаменте объекта.
Для того чтобы удовлетворить этим столь различным требованиям, СОМ позволяет разработчикам интерфейсов указывать точную семантику каждого параметра указателя. Чтобы показать, что указатель никогда не должен принимать нулевого значения, разработчик интерфейса может применить атрибут [ref]:
HRESULT g([in, ref] short *ps);
// ps cannot be a null ptr.
// ps не может быть нулевым указателем
Указатели, использующие атрибут [ref], называются ссылочными указателями (reference pointers). При IDL-определении, приведенном выше, следующий код со стороны клиента:
HRESULT hr = p->g(0);
// danger: passing null [ref] ptr.
// опасность: передается нулевой указатель с атрибутом [ref]
является ошибочным. И если p указывает на интерфейсный заместитель, то данный интерфейсный заместитель обнаружит нулевой указатель и возвратит вызывающей программе ошибку маршалинга, даже не передав метод текущему объекту. А чтобы сделать нулевой указатель допустимым значением параметра, в IDL-определении следует использовать атрибут [unique]:
HRESULT h([in, unique] short *ps);
// ps can be a null ptr.
// ps может быть нулевым указателем
Указатели, использующие атрибут [unique], называются уникальными указателями (unique pointers). При IDL-определении, приведенном выше, следующий код со стороны клиента:
HRESULT hr = p->h(0);
// relax: passing null [unique] ptr.
// расслабьтесь: передается нулевой указатель с атрибутом [unique]
является допустимым. Это означает, что интерфейсный заместитель должен подробно исследовать указатель перед тем, как разыменовать его. И что более важно: это означает, что интерфейсному заместителю необходимо записывать в ответ на ORPC-запрос не только разыменованную величину. Кроме нее, он должен записать тег, указывающий, был или не был передан нулевой указатель. Это добавляет к размеру ORPC-сообщения четыре байта на каждый указатель. Для большинства приложений эти добавочные четыре байта и то процессорное время, которое необходимо для выявления нулевого указателя [1], пренебрежимо малы по сравнению с преимуществами использования нулевых указателей в качестве параметров.
Читать дальше