COM and VC++

dberezhnoy

Те, кто разбирается в Com и VC++, подскажите, что делать. Проблема в том, что не могу в VС++ создать экземпляр Сom класса, по известному интерфейсу. Напускаю в VC++ директиву #import на некоторую стороннюю DLL-ку, она естественно создает мне два фала file.tlh и file.tli. Вот что есть в tlh фале:
---------------------------------------------------------------------------------
//file file.tlh
...
_COM_SMARTPTR_TYPEDEF(ISmth1, __uuidof(ISmth1;
_COM_SMARTPTR_TYPEDEF(ISmth2, __uuidof(ISmth2;
struct __declspec(uuid("f96f13a2-08fb-49c4-8590-a3627b367478"
Smth1;
// [ default ] interface ISmth1
...
// End of com1.tlh
-----------------------------------------------------------------------------------
Есть два интерфейса ISmth1 и ISmth2, но для первого есть строка
struct __declspec(uuid("f96f13a2-08fb-49c4-8590-a3627b367478"
Smth1;
// [ default ] interface ISmth1
а для второго ничего подобного нет.
Пытаюсь в сpp фале присоединиться к com серверам:
----------------------------------------------------------------------------------
//file.cpp
...
ISmth1Ptr pServer1;
ISmth2Ptr pServer2;
HRESULT hr = pServer1.CreateInstance(__uuidof(Smth1;
hr = pServer2.CreateInstance(__uuidof(Smth2;
...
//Enf of file.cpp
----------------------------------------------------------------------------------
На строке "hr = pServer2.CreateInstance(__uuidof(Smth2;" компилятор ругается, что-то типа не могу найти объекта Smth2, если эту строку заменить на строку "hr = pServer2.CreateInstance(__uuidof(ISmth2;", то компилятор ругается, что не могу создать экземпляр абстактного класса ISmth2. Тем неменее следующий код в VB проходит:
Dim obj1 As Smth1
Dim obj1 As ISmth2
Как VB создает экземпляр интерфейса, я не понимаю. А вопрос в том, что как найти COM объект реализующий интерфейс ISmth2? Или по другому: как воспользоваться функциями этого интерфейса в VC++?

okunek

попробуй использовать CallCreateInstance, ей нужно передавать только имя библиотеки.

dberezhnoy

Ты наверное имел ввиду CoCreateInstance. CallCreate ни я ни MSDN не знают. А первый параметр CoCreateInstanse это CLSID COM объекта, а не библиотеки.
Цитата:
-----------------------------------------
CoCreateInstance
Creates a single uninitialized object of the class associated with a specified CLSID. Call CoCreateInstance when you want to create only one object on the local system. To create a single object on a remote system, call CoCreateInstanceEx. To create multiple objects based on a single CLSID, refer to the CoGetClassObject function.
STDAPI CoCreateInstance(
REFCLSID rclsid,
LPUNKNOWN pUnkOuter,
DWORD dwClsContext,
REFIID riid,
LPVOID * ppv
);
Parameters
rclsid
[in] CLSID associated with the data and code that will be used to create the object.
-------------------------------------------------------------------------------
А CLSID я то как раз и не знаю. Я не знаю какой объект реализует интерфейс ISmth2. Можно ли как-то по интерфейсу понять, кто его реализует?

okunek

а ну да, я тупанул. Просто я ботал com очень давно и помню, что компоненты можно загружать не через clsid

typedef IUnknown* (*CREATEFUNCPTR;
IUnknown* CallCreateInstance(char* name)
{
// Загрузить в процесс динамическую библиотеку
HINSTANCE hComponent = ::LoadLibrary(name);
if (hComponent == NULL)
{
cout << "CallCreateInstance:\tОшибка: Не могу загрузить компонент"
<< endl;
return NULL;
}
// Получить адрес функции CreateInstance
CREATEFUNCPTR CreateInstance
= (CREATEFUNCPTR)::GetProcAddress(hComponent, "CreateInstance");
if (CreateInstance == NULL)
{
cout << "CallCreateInstance:\tОшибка: "
<< "Не могу найти функцию CreateInstance"
<< endl;
return NULL;
}
return CreateInstance;
}

Dasar

Ты уверен, что второй объект должен создаваться напрямую?
По смыслу, он скорее всего получается через Smth1.
> Dim obj1 As Smth1
> Dim obj1 As ISmth2
Это не создание объектов, это простое объявление ссылок на объекты.
Аналог на C++:
ISmth1Ptr pServer1;
ISmth2Ptr pServer2;
зы
Вообщем, посмотри методы объекта smth1, один из методов скорее всего возвращает ISmth2

Aleksei66

Да видимо прав. Интерфейс и класс - разные вещи. Из того, что ты показал не следует, что есть класс Something2.

Crash770909

1) нужен конкретный пример, больше информации т.к. это могут быть аггрегированные объекты
2) если ты можешь создать объект интерфейса из VB, то объект реалиует автоматизацию (дуальные интерфейсы)
в таком случае работа идет через IDispatch::Invoke -> попробуй поискать DISP_ID для твоего интерфейса.
3) не используй _COM_SMARTPTR_TYPEDEF & директиву #import; я не понимаю начинающих - неужели так тяжело дернуть CoInitialize/CoCreateInstance зато структура программы будет понятней
Короче говоря :
тебе нужен тайплиб .tlb

Crash770909

интерфейс - это чисто абстрактный класс

dberezhnoy

Не поверишь, но все наоборот.
После этого объявления
Dim obj1 As Smth1
Dim obj2 As ISmth2
почти сразу идет что-то типа
obj1 = obj2.getSmth1;
Просто у меня есть работающий код на VB, но как он работает я не могу понять. С объявлениями все ясно, видимо надо было сразу написать эту строку "Smth1 = ISmth2.getSmth2;" На самом деле код не на этой машине, поэтому пишу на память. Но вроде ключевые моменты не вру т.е. в VB почему-то можно обращаться через интерфейс к функциям. Это меня смущает. Просто я с VB почти не работал, он для меня темный лес.

Dasar

не пугай людей.
#import - хорошая штука, особенно если заменить стандартные wrapper-ы на свои или atl-ные

Dasar

> почти сразу идет что-то типа
> obj1 = obj2.getSmth1;
не верю. Где-то у тебя в описании проблемы есть неточности.

Crash770909

>> не вру т.е. в VB почему-то можно обращаться через интерфейс к функциям
ты не поверишь, но в VC, Delphi, Java и C# тоже можно
я думаю, в твоем случае здесь имеет место аггрегация

dberezhnoy

Как вытянуть из DLL .tlb файл?
1. А если они агрегированные то, что?
2. "если ты можешь создать объект интерфейса из VB, то объект реалиует автоматизацию" - не факт. VB может и без IDispach рботать, но огда действительно нужна библиотека типов. А вот как ее вытянуть из ДЛЛ? Попробую поискать DISPID.
3. Как можно обойтись без #import? Я всгда ее использовал.
4. Если создавать с помощью CoCreateInstance то потом нужно не забыть Release вызвать Мне наоборот кажется, что лаконичнее использовать _COM_SMARTPTR_TYPEDEF и структура программы понятнее

Crash770909

отчего не веришь? аргументируй

Aleksei66

Нет. Интерфейс и то, что в COM называется CoClass, разные вещи. Один класс может иметь множество интерфейсов, но интерфейсы не могут существовать без физической сущности - класса. То что в С++ интерфейс случайно оказался абстрактным классом, ничего не значит. В данном случае проблема, видимо, в том, что имя одного интерфейса оказалось похожим на имя класса, но это ничего не меняет. Если не использовать import, который сильно облегчает работу с COM, то пришлось бы создать класс Something1 и у него потом запросить интерфейс ISomething1 или ISomething2, как нравится. По-моему дело обстоит так, или нет?

Dasar

потому что по описанию из первого поста должно быть на оборот (если, конечно, vb-шный код рабочий).

okunek

Ты бы выкинул исходнички сюда (если они не очень здоровые, и если там нет ничего секретного) , а то тут постепенно разворачиваются баталии, истоки которых происходят из-за незнания, как в действительности реализованы компоненты.

Aleksei66

Возможно тебе нужно писать не hr = pServer2.CreateInstance(__uuidof(Smth2; а hr = pServer2.CreateInstance(__uuidof(Smth1;

Dasar

все правильно, только import никакого отношения к данной проблеме не имеет. import - вообще, параллельная штука к классам, интерфейсам и т.д.
import только облегчает процесс создания "умных указателей", больше ничего сверхестественного он не делает.

dberezhnoy

Вроде пробовал. Вроде не получилось.
2: Я б конечно выложил, да только они на другой машине. У меня их сейчас нет.

Aleksei66

А что значит не получилось? hr = ENOINTERFACE или как там был равен?

Crash770909

>> Anonymous
>> но интерфейсы не могут существовать без физической сущности - класса
это, конечно, полный бред, прости за правду , видимо ты перепутал интерфейс и реализацию
а вообще
да, ты ошибся
Итак:
класс - одно из базовых понятий ООП
интерфейс - суть абстрактный класс, т.е. все методы чисто виртуальные
CoClass - суть класс (компонентный класс унаследованный от интерфейсов и обладающий реализацией метода QueryInterface для доступа к своим компонентам
Для создания объекта CoClass необходимо определить метод CreateInstance, либо определить иным образом фабрику класса
Создать экземпляр абстрактного класса нельзя (могу объяснить почему )
Имя интерфейса и имя класса - понятия одинаковые

Crash770909

>> VB может и без IDispach рботать, но огда действительно нужна библиотека типов
объясни мне как можно обращаться к _vtable из языка, не поддердивающего указатели на функции
ты неправ
а tlb файл попробуй пошукать в других местах
из длл никак

Aleksei66

Нет. КоКласс может быть чем угодно, даже не классом в понимании С++. QueryInterface не его метод, а метод интерфейса IUnknown, который должны реализовывать все объекты COM. И еще раз, то что в С++ интерфейс это абстрактный класс, это случайность. В C#, например, интерфейс и абстрактный класс разные вещи. Поэтому не нужно путать имя КоКласса и имя интерфейса. Разница между ними видна даже в tlb.

buba741

Итак:
класс - одно из базовых понятий ООП
интерфейс - суть абстрактный класс, т.е. все методы чисто виртуальные
CoClass - суть класс (компонентный класс унаследованный от интерфейсов и обладающий реализацией метода QueryInterface для доступа к своим компонентам
Для создания объекта CoClass необходимо определить метод CreateInstance, либо определить иным образом фабрику класса
Создать экземпляр абстрактного класса нельзя (могу объяснить почему )
Имя интерфейса и имя класса - понятия одинаковые
Это лажа.
Ты напрасно отождествляешь COM и сложившуюся практику создания COM-компонент на С++.
Вообще говоря, COM-классы не имеют никакого отношения к С++ классам.
Например (при большом желании вполне можно писать комовщину на чистом С, безо всяких классов вообще.
Попробуй на досуге. Тебе, как любителю низкоуровневых API, должно понравиться.

Aleksei66

Из VB6 можно вызывать функции не заморачиваясь с IDispatch. Точнее это он не заморачивается с ним. Это не противоречит ни чему, поскольку делается самим VB за спиной программиста, также как например это происходит при использовании import. Там тоже не нужно гемороиться с IDispatch поскольку все само генерируется.

dberezhnoy

VB может обращаться к методам интерфейса напрямую, без диспетчерского механизма. Для этого нужно, чтобы была скомпилирована библиотека типов, как раз файл c расширением tlb. Только зачастую никто tlb файлы не поставляет, они компануются вместе с DLL или там EXE сервером COM-компонента. Только есть одно необходимое условие - нужно, чтобы все типы данных были типами, поддерживаемые Automaion. Как и что VB делает с этим tlb, я не знаю. Я вообще пугаюсь VB - это страшный с необъяснимиым поведением язык.

Crash770909

>> КоКласс может быть чем угодно, даже не классом в понимании С++
согласен
>> QueryInterface не его метод, а метод интерфейса IUnknown, который должны реализовывать все объекты COM
согласен, большой респект за напоминание
Если еще раз перечитаешь мои посты, то имя КоКласса и имя интерфейса я не путаю.
Переубеждать и объяснять концепцию КОМ я здесь не буду, мне за это не платят (лучше помогу советом)
Приведу напоследок две цитаты из MSDN
coclass (component class)
Component object class. A top-level object in the object hierarchy.
CoClass:
The Microsoft® Component Object Model defines a class as an implementation that allows QueryInterface between a set of interfaces .

Crash770909

я исходил из контекста беседы

Crash770909

Automation - это поддержка IDispatch, т.н. дуальный интерфейс (dual)

Aleksei66

Я не силен в теории. Я хочу лишь сказать, что в COM мы работаем только с интерфейсами, но получаем к ним доступ через создание специального объекта. И если вышло так, что этот объект называется A, а один из его интерфейсов IA, то это просто совпадение и не влечет никаких особых последствий.

Crash770909

>> вполне можно писать комовщину на чистом С
я писал контроль для "HelloWorld" на С

dberezhnoy

Тем неменее, если предположить, что это работающий код
Dim obj1 As Smth1
Dim obj2 As ISmth2
obj1 = obj2.getSmth1;
А этот код работающий. Как это можно обяснить? Ты чего-то про агрегацию говорил.

Crash770909

В общем случае да, согласен
мне показалось, что ты работал с КОМом в основном на языках высокого уровня

dberezhnoy

Ты все правильно сказал. Только ты не внимательно прочитал меня, я сказал, что необходимое условие это то, что типы переменных и возвращаемых значений функций твоего интерфейcа должны быть типа данных VARIANT. Ты не обязан реализовывать IDispatch, чтобы к твоему серверу мог обратиться клиент из VB. Именно из VB. Для всех остальных языков, необходим реализация маршалера Automation с помощью интерфейса IDIspatch.

Crash770909

аггрегация - это объединение двух объектов в агрегат
оба естественно реализуют IUnknown, но один из них перенаправляет вызовы в т.ч IU к аггрегированному объекту
это примерно как если бы ты написал dll которая заменяла бы напр. kernel32.dll и перенаправляла все ее вызовы через себя
т.е. есть аггрегат и есть аггрегированный
в моем примере аггрегат - твоя длл, аггрегируемый объект - kernel32.dll
ты можешь получить аггрегированный объект, если у тебя имеется GUID аггрегата
все IMHO

dberezhnoy

А что значит не получилось? hr = ENOINTERFACE или как там был равен?
-----------------------------------------------------------------------------------------------
Я не проверял значение. Просто на проверке if(FAILED(hr вылетало.

dberezhnoy

Теорию я знаю. Нового ты к сожалению ничего не сказал, поэтому проблема остается.

Crash770909

до тех пор пока ты не выложишь конкретный пример
Оставить комментарий
Имя или ник:
Комментарий: