Дональд Бокс - Сущность технологии СОМ. Библиотека программиста
- Название:Сущность технологии СОМ. Библиотека программиста
- Автор:
- Жанр:
- Издательство:Питер
- Год:2001
- Город:СПб
- ISBN:5-318-00058-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Дональд Бокс - Сущность технологии СОМ. Библиотека программиста краткое содержание
В этой книге СОМ исследуется с точки зрения разработчика C++. Написанная ведущим специалистом по модели компонентных объектов СОМ, она раскрывает сущность СОМ, помогая разработчикам правильно понять не только методы модели программирования СОМ, но и ее основу. Понимание мотивов создания СОМ и ее аспектов, касающихся распределенных систем, чрезвычайно важно для тех разработчиков, которые желают пойти дальше простейших приложений СОМ и стать по-настоящему эффективными СОМ-программистами. Показывая, почему СОМ для распределенных систем (Distributed СОМ) работает именно так, а не иначе, Дон Бокс дает вам возможность применять эту модель творчески и эффективно для ежедневных задач программирования.
Сущность технологии СОМ. Библиотека программиста - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
[HKCR\Apes.Gorilla.1] @="Gorilla" [HKCR\Apes.Gorilla.1\CLSID]
@="\{571F1680-CC83-11d0-8C48-0080C73925BA}
Программные идентификаторы ( ProgID ) не являются обязательными, но они рекомендуются, чтобы в тех средах, которые не могут просто копировать необработанные CLSID , тоже можно было осуществлять вызовы на активацию.
Все стандартно реализованные серверы СОМ поддерживают саморегистрацию. Для внутрипроцессного сервера это означает, что DLL должен экспортировать известные функции
STDAPI DllRegisterServer(void);
STDAPI DllUnregisterServer(void);
Отметим, что STDAPI является просто макросом, индицирующим, что функция возвращает НRESULT и использует стандартное соглашение СОМ по вызову глобальных функций. Эти подпрограммы должны быть явно экспортированы с использованием или файла определения модуля, или переключателей компоновщика, или директив компилятора. Эти подпрограммы используются хранилищем классов Class Store для конфигурирования локального кэша после загрузки файла на машину клиента. Кроме Class Store эти известные подпрограммы используются различными средами (например, Microsoft Transaction Server, ActiveX Code Download, а также различными инсталляционными программами) для инсталляции или деинсталляции серверов на хост-машинах. В Win32 SDK включена утилита REGSVR32.EXE , которая может инсталлировать или деинсталлировать внутрипроцессный сервер СОМ с использованием этих известных экспортированных функций.
Реализации внутрипроцессных серверов DllRegisterServer и DllUnregisterServer должны запросить реестр на добавление или удаление соответствующих ключей, преобразующих CLSID и ProgID сервера в файловые имена сервера. Хотя существуют различные способы реализации этих подпрограмм, наиболее гибким и эффективным из них является создание строковой таблицы, содержащей соответствующие ключи, названия величин, сами величины и простое перечисление всех записей в таблице, путем вызова RegSetValueEx для инсталляции и RegDeleteKey для деинсталляции. Чтобы осуществить регистрацию, основанную на этой технологии, сервер может просто задать массив строк размером Nx3 , где каждый ряд массива содержит строки для использования в качестве ключей, имена величин и величины:
const char * gRegTable[][3] = {
// format is { key , value name, value }
{
«CLSID\\{571F1680-CC83-11d0-8C48-0080C73925BA}», 0, «Gorilla»
},
{
"CLSID\\{571F1680-CC83-11d0-8C48-0080C73925BA}
\\InprocServer32",0, (const char*)-1
// rogue value indicating file name
// нестандартное значение, указывающее имя файла
},
{
«CLSID\\{571F1680-CC83-11d0-8C48-0080C73925BA}\\ProgID», 0, «Ареs.Gorilla.1»
},
{
«Apes.Gorillа.1», 0, «Gorilla»
},
{
«Apes.Gorilla.1\\CLSID», 0, «{571F1680-CC83-11d0-8C48-0080C73925BA}»
},
};
Имея эту таблицу, весьма несложно осуществить реализацию DllRegisterServer :
STDAPI DllRegisterServer(void)
{
HRESULT hr = SOK;
// look up server's file name
// ищем имя файла сервера
char szFileName[MAX PATH];
GetModuleFileNameA( ghinstDll , szFileName, MA XPATH);
// register entries from table
// регистрируем записи из таблицы
int nEntries = sizeof(g RegTable)/sizeof(* gRegTable);
for (int i = 0; SUCCEEDED(hr) && i < nEntries; i++)
{
const char *pszKeyName = g RegTable[i][0];
const char *pszValueName = gRegTable[i][1];
const char *pszvalue = g RegTable[i][2];
// map rogue value to module file name
// переводим нестандарное значение в имя файла модуля
if (pszValue == (const char*)-1) pszValue = szFileName;
HKEY hkey;
// create the key
// создаем ключ
long err = RegCreateKeyA(HKE YCLASSES ROOT, pszKeyName, &hkey);
if (err == ERRO RSUCCESS)
{
// set the value
// присваиваем значение
err = RegSetValueExA(hkey , pszVvalueName, 0, RE GSZ , (const BYTE*) pszValue, (strlen(pszValue) + 1));
RegCloseKey(hkey);
}
if (err != ERRO RSUCCESS)
{
// if cannot add key or value , back out and fail
// если невозможно добавить ключ или значение, то откат и сбой
DllUnregisterServer();
hr = SELFRE GE CLASS;
}
}
return hr;
}
Соответствующая DllUnregisterServer будет выглядеть так:
STDAPI DllUnregisterServer(void)
{
HRESULT hr = SOK;
int nEntries = sizeof(g RegTable)/sizeof(* gRegTable);
for (int i = nEntries – 1; i >= 0; i-)
{
const char *pszKeyName = g RegTable[i][0];
long err = RegDeleteKeyA(HKE YCLASSES ROOT, pszKeyName);
if (err != ERRO RSUCCESS) hr = S FALSE; }
return hr;
}
Отметим, что реализация DllUnregisterServer просматривает таблицу с конца, начиная с последней входной точки. Делается это для преодоления ограничения RegDeleteKey , в котором разрешаются только такие ключи, у которых нет подключей, подлежащих удалению. Реализация DllUnregisterServer требует такой организации таблицы, чтобы все подключи каждого ключа появлялись в таблице после входа родительского ключа.
Так как СОМ преобразует CLSID в данный файл реализации, то для объявления в СОМ относящихся к серверу объектов класса необходимо использовать определенные стандартные методики. Для сервера, основанного на исполняемой программе, в СОМ предусмотрены явные API-функции для связывания объектов класса с их CLSID . Эти API-функции мы будем подробно обсуждать в главе 6. Для сервера, основанного на DLL, DLL должна экспортировать известную функцию, которая будет вызываться с помощью CoGetClassObject , когда потребуется объект класса. Эту функцию необходимо экспортировать с использованием файла определения модулей, причем она должна иметь следующий вид:
HRESULT DllGetClassObject(
[in] REFCLSID rclsid,
// which class object?
// какой объект класса?
[in] REFIID riid,
// which interface?
// какой интерфейс?
[out, ii dis(riid)] void **ppv);
// put it here !
// разместить его здесь!
Для удобства и эффективности данный сервер может содержать код для более чем одного класса. Первый параметр DllGetClassObject показывает, какой класс в данный момент запрашивается. Второй и третий параметры просто дают функции возможность возвращать типизированный указатель интерфейса для СОМ.
Рассмотрим сервер, реализующий три класса: Gorilla , Chimp и Orangutan . Сервер, возможно, будет содержать шесть отдельных классов C++: три из них создают экземпляры каждого класса, а другие три – объекты класса для каждого класса. В соответствии с этим сценарием, серверная реализация DllGetClassObject будет выглядеть следующим образом:
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
// define a singleton class object for each class
// определяем одноэлементный объект класса
// для каждого класса
static GorillaClass sgorillaClass;
static OrangutanClass s orangutanClass;
static ChimpClass schimpClass;
// return interface pointers to known classes
// возвращаем указатели интерфейсов известных классов
if (rclsid == CLSID Gorilla) return sgorillaClass .QueryInterface(riid, ppv);
else if (rclsid == CLSI DOrangutan)
return s orangutanClass.QueryInterface(riid, ppv);
else if (rclsid == CLSI DChimp) return s chimpClass.QueryInterface(riid, ppv);
// if we get to here, rclsid is a class we don't implement,
// so fail with well-known error code
// если мы добрались сюда, то rclsid – это класс, который
// мы не реализуем, поэтому сбой с известным кодом ошибки
*ppv = 0;
return CLAS SE CLASSNOTAVAILABLE;
}
Заметим, что приведенный код не заботится о том, какой интерфейс объявляется каждым из объектов класса. Он просто отправляет запрос QueryInterface соответствующему объекту класса.
Читать дальшеИнтервал:
Закладка: