Не стал пробовать с VC 6.0, т.к увидел, что .NET перегенирирует прокси в соответствии с любыми более или менее вменяемыми измениями idl описаний интерфейсов вручную. И это хорошо. Умная штучка.
Но вот наткнулся на вопрос в .NET 2003 о создании дополнительных интерфейсов. Наверное, я тупой... Упростим задачу для простоты изложения.
Есть COM
ATLSomeObject объект, который имеет единственный интефейс
IATLSomeObject, имеющий единственный метод
Message( void ) (бибикает или печатает чего).
Надо добавить второй интерфейс
IATLSomeObject2, имеющий одноимённый метод
Message(void) (бибикает или печатает немного по-другому).
Как сделать... Читаем MSDN. Там две возможности. Выбираем халявную ... упс визард не хочет ничего добавлять несмотря на то, что макро BEGIN_COM_MAP я уже добавил (как не добавить, ведь MS говорит, что без этого не будет работать... а оно хоть с этим, хоть без этого не печатает...).
Это непонятность # 1. Почему визард не помогает быстренько приладить новый интерфейс.
Ладно, перехожу к первому не халявному способу, что без визарда. Руками добавляю в idl второй интерфейс
IATLSomeObject2 с заветным методом
Message. Пробую в меню "Implement interface". Печатает, собака. И, что интересно, совершенно безразлично определил я ранее BEGIN_COM_MAP, или не определил.
Это непонятность # 2. Непонятность это потому, что как сказано в MSDN, без всемогущего BEGIN_COM_MAP вроде как вообще и не добавить дополнительный интерфейс. А добавляется ведь...
Ладно, думаю, хоть сервер и собирается без ошибок и предупреждений, но клиент уж точно не будет работать, поскольку, я заветный макро BEGIN_COM_MAP закоментировал, а без него, вроде как и не будет QueryInterface пахать по заверениям MS. Наивный... Работает, ведь.
Это непонятность # 3. и самая большая непонятность. Как компилятор .NET 2003, работая с ATL проектом, умеет сообразить только на основании наследования интерфейса (чисто C++ наследование, см. ниже;c таким же успехом можно было бы отнаследоваться от чего угодно, не связанного с COM), что надо построить соответствующую правильную DllGetClassObject, если мы не определяли в классе COM_INTERFACE_ENTRY.
Для простоты, привожу очень простой код сервера и клиента. Без всяких проверок на фэйлыд. Клиент намерено сделан без smart poiners, чтобы за спиной ничего не стояло (понятное, дело что со смарт поинтерами аналогичный клиент радостно работает тоже). Сервер в одном .h файлев основном.
Сервер
Код: Выделить всё
#pragma once
#include "resource.h"
//
// IATLSomeObject interface
//
[
object,
uuid("456EDE06-67D6-4B2B-8A1A-D502850479EA"),
dual, helpstring("Interface # 1"),
pointer_default(unique)
]
__interface IATLSomeObject : IDispatch
{
[id(1), helpstring("method Message")] HRESULT Message(void);
};
//
// IATLSomeObject2 interface
//
[
object,
uuid("8F08C8B6-A53E-4d89-8265-988623204FBA"),
dual, helpstring("Interface # 2"),
pointer_default(unique)
]
__interface IATLSomeObject2 : IDispatch
{
[id(1), helpstring("method Message")] HRESULT Message(void);
};
//
// CATLSomeObject
//
[
coclass,
threading("apartment"),
vi_progid("atl_test.ATLSomeObject"),
progid("atl_test.ATLSomeObject.1"),
version(1.0),
uuid("AE3049F3-006D-40A3-8B68-045CF91DD213"),
helpstring("ATLSomeObject Class")
]
class ATL_NO_VTABLE CATLSomeObject :
public IATLSomeObject,
public IATLSomeObject2
{
public:
CATLSomeObject()
{
}
DECLARE_PROTECT_FINAL_CONSTRUCT()
/*
BEGIN_COM_MAP(CATLSomeObject)
COM_INTERFACE_ENTRY(IATLSomeObject)
COM_INTERFACE_ENTRY(IATLSomeObject2)
END_COM_MAP()
*/
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
//
// Interface
//
// IATLSomeObject Methods
public:
STDMETHOD(IATLSomeObject::Message)(void)
{
MessageBox(0, "Interface # 1 Message(void)", "ATLSomeObject", 0);
return S_OK;
}
// IATLSomeObject2 Methods
public:
STDMETHOD(IATLSomeObject2::Message)(void)
{
MessageBox(0, "Interface # 2 Message(void)", "ATLSomeObject", 0);
return S_OK;
}
};
Клиент
Код: Выделить всё
#include "stdafx.h"
#include <windows.h>
#include <initguid.h>
DEFINE_GUID(CLSID_Object,
0xAE3049F3, 0x006D, 0x40A3, 0x8b, 0x68, 0x4, 0x5c, 0xf9, 0x1d, 0xd2, 0x13);
//
// first interface
//
DEFINE_GUID(IID_Interface1,
0x456EDE06, 0x67D6, 0x4B2B, 0x8a, 0x1a, 0xd5, 0x2, 0x85, 0x4, 0x79, 0xea);
interface Interface1 : public IDispatch
{
STDMETHOD( InterfaceMessage ) () PURE;
};
//
// second interface
//
DEFINE_GUID(IID_Interface2,
0x8F08C8B6, 0xA53E, 0x4d89, 0x82, 0x65, 0x98, 0x86, 0x23, 0x20, 0x4f, 0xba);
interface Interface2 : public IDispatch
{
STDMETHOD( InterfaceMessage ) () PURE;
};
void main()
{
::CoInitialize( NULL );
Interface1* pInterface1 = NULL;
Interface2* pInterface2 = NULL;
::CoCreateInstance( CLSID_Object, NULL, CLSCTX_INPROC, IID_Interface1,(void **) &pInterface1);
pInterface1->InterfaceMessage();
pInterface1->QueryInterface( IID_Interface2, (void **) &pInterface2);
pInterface2->InterfaceMessage();
pInterface1->Release();
pInterface2->Release();
::CoUninitialize();
}
Спасибо, если кто-то прояснит мне эти ньюансы.