API DLL или COM объект? Создание COM объектов без регистрации
При создании многокомпонентных проектов одним из основных вопросов, стоящих перед проектировщиком, является вопрос о том, каким, собственно, образом компоненты будут взаимодействовать друг с другом. Если мы говорим о компонентах, экспортирующих некий набор функций (по сути, библиотеках), то механизмом взаимодействия может быть использование COM интерфейсов и традиционный импорт/экспорт функций и классов. Создание COM объектов «на лету» снижает сложность использования второго метода.
Зачастую выбирается взаимодействие с использованием импорта/экспорта функций из библиотек, как по причине простоты реализации, так и из-за проблем с необходимой в случае использования COM регистрации компонентов, для которой может и не хватать прав.
А что, если регистрировать COM компонент и не придется?
Да, это возможно. Один из способов используется Microsoft, например, для создания DirectDraw объекта. Это, по сути, объединение обоих подходов. Т.е. на стадии создания объекта используется вызов экспортируемой функции, а далее работа идет с интерфейсами COM объекта.
HRESULT WINAPI DirectDrawCreate(GUID FAR* lpGUID, LPDIRECTDRAW FAR* lplpDD, IUnknown FAR* pUnkOuter);
Второй метод, на мой взгляд, красивей. Он заключается в использовании функции DllGetClassObject, которую каждый COM объект обязан экспортировать. Именно эту функцию вызывает COM подсистема Windows для зарегистрированных компонентов в тот момент, когда какая-либо программа пытается создать их копию вызовом CoCreateInstance.
HRESULT CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID * ppv); CoCreateInstance -> поиск регистрационных данных объекта -> чтение пути расположения файла из регистрационных данных -> вызов DllGetClassObject компонента для создания копии запрашиваемого объекта
Таким образом, если расположение файла известно, то копию объекта можно создать, загрузив модуль и вызывая его DllGetClassObject:
HINSTANCE hModule = CoLoadLibrary(pszModulePathName); if (hModule) { LPFNGETCLASSOBJECT pfnGetClassObject = (LPFNGETCLASSOBJECT)GetProcAddress(hModule, "DllGetClassObject"); if (pfnGetClassObject) { hr = pfnGetClassObject(rclsid, riid, ppv); ...
Причем параметры вызова pfnGetClassObject те же, что и в случае CoCreateInstance, считая, что pUnkOuter равен нулю (не использовать агрегацию) и dwClsContext равен константе CLSCTX_INPROC_SERVER.
Заключение
Ну вот, собственно, и весь метод. Все просто и эффективно. Думаю, стоит также упомянуть, что, если COM компонент входит в состав вашего программного пакета, то он может физически находиться в ресурсах вашего приложения и копироваться на диск (а, возможно, и распаковываться / расшифровываться) непосредственно перед первым его использованием. В примере, который вы можете скачать по ссылке внизу этой страницы, как раз реализован такой подход к использованию COM компонента.