Сущность технологии СОМ. Библиотека программиста
Шрифт:
Все стандартно реализованные серверы СОМ поддерживают саморегистрацию. Для внутрипроцессного сервера это означает, что DLL должен экспортировать известные функции
STDAPI DllRegisterServer(void);
STDAPI DllUnregisterServer(void);
Отметим, что STDAPI является просто макросом, индицирующим, что функция возвращает НRESULT и использует стандартное соглашение СОМ по вызову глобальных функций. Эти подпрограммы должны быть явно экспортированы с использованием или файла определения модуля, или переключателей компоновщика, или директив компилятора. Эти подпрограммы используются хранилищем классов Class Store для
Реализации внутрипроцессных серверов 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[MAXPATH];
GetModuleFileNameA(ghinstDll, szFileName, MAXPATH);
// register entries from table
// регистрируем записи из таблицы
int nEntries = sizeof(gRegTable)/sizeof(*gRegTable);
for (int i = 0; SUCCEEDED(hr) && i < nEntries; i++)
{
const char *pszKeyName = gRegTable[i][0];
const char *pszValueName = gRegTable[i][1];
const char *pszvalue = gRegTable[i][2];
// map rogue value to module file name
//
переводим нестандарное значение в имя файла модуляif (pszValue == (const char*)-1) pszValue = szFileName;
HKEY hkey;
// create the key
// создаем ключ
long err = RegCreateKeyA(HKEYCLASSESROOT, pszKeyName, &hkey);
if (err == ERRORSUCCESS)
{
// set the value
// присваиваем значение
err = RegSetValueExA(hkey, pszVvalueName, 0, REGSZ, (const BYTE*) pszValue, (strlen(pszValue) + 1));
RegCloseKey(hkey);
}
if (err != ERRORSUCCESS)
{
// if cannot add key or value, back out and fail
// если невозможно добавить ключ или значение, то откат и сбой
DllUnregisterServer;
hr = SELFREGECLASS;
}
}
return hr;
}
Соответствующая DllUnregisterServer будет выглядеть так:
STDAPI DllUnregisterServer(void)
{
HRESULT hr = SOK;
int nEntries = sizeof(gRegTable)/sizeof(*gRegTable);
for (int i = nEntries – 1; i >= 0; i-)
{
const char *pszKeyName = gRegTable[i][0];
long err = RegDeleteKeyA(HKEYCLASSESROOT, pszKeyName);
if (err != ERRORSUCCESS) hr = SFALSE; }
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, iidis(riid)] void **ppv);
// put it here!
// разместить его здесь!
Для удобства и эффективности данный сервер может содержать код для более чем одного класса. Первый параметр DllGetClassObject показывает, какой класс в данный момент запрашивается. Второй и третий параметры просто дают функции возможность возвращать типизированный указатель интерфейса для СОМ.