Разработка клиента с использованием специальных указателей
Создайте новый пустой проект консольного приложения с именем SayTLibClient и вставьте в него новый файл SayTLibClient.cpp. Введите в файл следующий текст и проследите за тем, чтобы текст директивы #import либо не разрывался переносом ее продолжения на другую строку, либо разрывался по правилам, то есть с использованием символа переноса ' \ ', как вы видите в тексте книги. После этого запустите проект на выполнение (CTRL + F5):
#import "C:\MyProjects\MyComTLib\Debug\ MyComTLib.tlb" \ no_namespace named_guids void main() { Colnitialize(0); //====== Используем "умный" указатель ISayPtr pSay(CLSID_CoSay); pSay › Say(); pSay › SetWord(L"The client now uses smart pointers!"); pSay › Say(); pSay = 0; CoUninitialize(); }
Несмотря на то что здесь нет многих строчек кода, присутствовавшего в предыдущей версии клиентского приложения, новая версия тоже должна работать. Попробуем разобраться в том, как это происходит.
- Во-первых, здесь использована директива #import, которая читает информацию из библиотеки типов MyComTLib.tlb и на ее основании генерирует некий код C++. Этот код участвует в процессе компиляции и сборки выполняемого кода клиента. Новый код является неким эквивалентом библиотеки типов и содержит описания интерфейсов, импортированные из TLB-файла.
- Во-вторых, мы создаем и используем так называемый smart pointer ("умный" указатель pSay) на интересующий нас интерфейс. Он берет на себя большую часть работы по обслуживанию интерфейса.
Примечание
Директивой tfimport можно пользоваться для генерации кода не только на основе TLB-файлов, но также и на основе других двоичных файлов, например ЕХЕ-, DLL- или OCX-файлов. Важно, чтобы в этих файлах была информация о типах СОМ-объектов.
Вы можете увидеть результат воздействия директивы #import на плоды работы компилятора C++ в папке Debug. Там появились два новых файла заголовков: MyCoTLib.tlh (type library header) и MyComTLib.tli (type library implementations). Первый файл подключает код второго (именно в таком порядке) и они оба компилируются так, как если бы были подключены директивой #include. Этот процесс конвертации двоичной библиотеки типов в исходный код C++ дает возможность решить довольно сложную задачу обнаружения ошибок при пользовании данными о СОМ-объекте. Ошибки, присутствующие в двоичном коде, трудно диагностировать, а ошибки в исходном коде выявляет и указывает компилятор. В данный момент важно не потерять из виду цепь преобразований:
- какая-то часть исходного текста СОМ-сервера (IDL-файл) была сначала преобразована в двоичный код библиотеки типов (TLB-файл);
- затем на стороне клиента и на основании этого кода компилятор C++ сгенерировал рассматриваемый сейчас исходный код C++ (TLH- и TLB-файлы);
- после этого компилятор вновь превращает исходный код в двоичный, сплавляя его с кодом клиентского приложения.
Немного позже мы рассмотрим содержимое новых файлов, а сейчас обратите внимание на то, что директива # import сопровождается двумя атрибутами: no_namespace и named_guids, которые помогают компилятору создавать файлы заголовков. Иногда содержимое библиотеки типов определяется в отдельном пространстве имен (namespace), чтобы избежать случайного совпадения имен. Пространство имен определяется в контексте оператора library, который вы видели в IDL-файле. Но в нашем случае пространство имен не было указано, и поэтому в директиве #import задан атрибут no_namespace. Второй атрибут (named_guids) указывает компилятору, что надо определить и инициализировать переменные типа GUID в определенном (старом) стиле: ывю_муСоm, CLSiD_CoSay и iio_isay.
Новый стиль задания идентификаторов заключается в использовании операции _uuidof(expression). Microsoft-расширение языка C++ определяет ключевое слово _uuidof и связанную с ним операцию. Она позволяет добыть GUID объекта, стоящего в скобках. Для ее успешной работы необходимо прикрепить GUID к структуре или классу. Это действие выполняют строки вида:
struct declspec(uuid("9b865820-2ffa-1Id5-98b4-00e0293f01b2")) /* LIBID */ _MyCom;
Которые также используют Microsoft-расширение языка C++ (declspec).