2016-01-31 14 views
0

У меня есть COM-объект, CProvider, реализованный с использованием ATL. Этот класс включает другой класс, CProviderInfo и поддерживает статический вектор объектов этого внутреннего типа класса.Может ли существующий простой класс C++ реализовать интерфейс IDL без перехода в класс COM?

Вот как это выглядит:

//------------- 
// CProvider.h 
//------------- 

// 
// COM object class 
// 
class ATL_NO_VTABLE CProvider : 
    public CComObjectRootEx<CComMultiThreadModel>, 
    public CComCoClass<CProvider, &CLSID_Provider>, 
    public Interface1, 
    public Interface2 
{ 
public: 
    BEGIN_COM_MAP(CProvider) 
     COM_INTERFACE_ENTRY(Interface1) 
     COM_INTERFACE_ENTRY(Interface2) 
    END_COM_MAP() 

    // 
    // The inner class 
    // 
    class CProviderInfo 
    { 
    public: 
     CProviderInfo(); 
     CComBSTR m_strName; 
     GUID m_guidRegistration; 
    }; 

private: 
    // 
    // static vector of inner class type 
    // 
    static vector<CProviderInfo> m_vProviderInfo; 
}; 

То, что я хотел бы сделать, это ввести метод на CProvider, который возвращает копию статического вектора m_vProviderInfo. Попытка играть по правилам COM, я представил новый интерфейс IDL IProviderInfoRetriever для этой цели:

//--------- 
// IDL file 
//--------- 

// 
// IProviderInfoRetriever interface 
// 
[ 
    // uuid, version ... etc. 
] 
interface IProviderInfoRetriever : IUnknown 
{ 
    HRESULT GetProviderInfo(
     [out, retval] SAFEARRAY(IProviderInfo*) *ppProviderInfo); 
} 

// 
// The interface of the class holding the info 
// 
[ 
    // uuid, version ... etc. 
] 
interface IProviderInfo : IUnknown 
{ 
    [propget] 
    HRESULT Name(
     [out, retval] BSTR *pbstrName); 

    [propget] 
    HRESULT Registration(
     [out, retval] GUID *pguidRegistration); 
} 

Мой план должен иметь CProvider реализовать IProviderInfoRetriever эффективно копирование содержимого статического вектора m_vProviderInfo в выходной SAFEARRAY от IProviderInfoRetriever :: GetProviderInfo().

Мой вопрос: можно ли иметь внутренний класс CProviderInfo реализации IProviderInfo? Будет ли это нарушать существующий код, который создает локальные переменные типа CProviderInfo?

+0

Что вы подразумеваете под словом «COM-класс», если не «класс, который реализует один или несколько COM-интерфейсов»? И нет, ничто не мешает вам создать, скажем, экземпляр «CProvider» в стеке («CProvider», по-видимому, являющийся «COM-классом» по любому определению, которое вы имеете в виду). –

+0

Еще одна альтернатива, касающаяся вас «CProviderInfo», заключалась бы в том, чтобы написать класс-оболочку, который реализует 'IProviderInfo', и содержит' CProviderInfo' как член (или даже просто указатель на элемент или индекс в оригинал вектор). Это прекрасно изолирует части, связанные с COM, и минимизирует след на остальной части кодовой базы. –

+0

@IgorTandetnik Вот что я и подумал. После некоторых исследований я обнаружил, что наследование IUnknown приведет к поломке существующего кода, потому что он превратит ** CProviderInfo ** в абстрактный класс. Невозможно создать экземпляр ** CProviderInfo **, за исключением CComObject, CComObjectStack и этого семейства классов, поскольку он обеспечивает реализацию элементов IUnknown. –

ответ

0

Я провел несколько исследований, и ответ прост: да, но это непросто. Очень маловероятно, что у вас будет обычный класс C++, наследующий/реализующий интерфейс, определенный в IDL, без нарушения существующего кода, который полагается на указанный класс C++.

Прежде всего, если вы используете возможности ATL для преобразования вашего класса C++ в COM-класс, вы вдруг обнаружите, что этот класс C++ стал абстрактным из-за всех чистых виртуальных функций, которые вводит макросы ATL. Итак, по крайней мере, у вас будут AddRef(), Release() и QueryInterface(), добавленные в ваш класс C++ макросами ATL как чистые виртуальные функции, например.

class ATL_NO_VTABLE CProviderInfo: 
    public CComObjectRootEx<CComMultiThreadModel>, 
    public IProviderInfo 
{ 
public: 
    BEGIN_COM_MAP(CProviderInfo) 
     COM_INTERFACE_ENTRY(IProviderInfo) 
    END_COM_MAP() // This line adds IUnknown's AddRef(), Release(), 
        // and QueryInterface() as pure virtual functions. 

    // ...   
}; 

Это само по себе будет нарушать существующий код, который используется для создания экземпляров вашего класса C++, будь то в стеке или в куче (с помощью нового оператора). Итак, окончательно у вас есть 2 варианта:

  1. Используйте возможности ATL, чтобы превратить ваш класс C++ в класс COM.

    Это превратит ваш класс C++ в абстрактный класс. Вы должны изменить все места в исходном коде, создавая объекты вашего класса C++, чтобы использовать классы ATL вместо, т.е. CComObject, CComObjectStack ... и т.д.

  2. Наследование/Реализовать интерфейс, определенный в IDL непосредственно и вручную.

    Это повлечет за собой предоставление ваших собственных реализаций IUnknown, IDispatch или обоих в зависимости от вашего интерфейса, а также реализаций любых методов, определенных самим интерфейсом.Преимущество этой опции состоит в том, что с меньшей вероятностью может нарушить любое существующее использование вашего класса C++ в вашей кодовой базе.

    Однако, развертывание собственных реализаций интерфейсов COM без ATL не всегда будет легким, особенно если ваш класс C++ когда-либо участвует в сложных сценариях, например. Interop.