ATL, .NET 2003

Все, что вы хотели знать о программизме, но боялись спросить.
vg
Маньяк
Сообщения: 2803
Зарегистрирован: 29 май 2003, 22:29
Откуда: Магадан - Миссиссага

ATL, .NET 2003

Сообщение vg »

При создании ATL проекта с именем xxx в том же солюшн генерируется проект с именем xxxPS (прокси стаб). Там лежит обычный *.def для COM и несколько *.с файлов. Не помню, но такого раньше не было в VC 6.0. Для чегой-то выделили в отдельный проджект всё это хозяйство?

Спасибо.
ura
Житель
Сообщения: 915
Зарегистрирован: 09 мар 2003, 22:46

Сообщение ura »

В опциях при создании проекта надо кажется убрать "Attributed" и включить Merge Proxy Stub, тогда все будет похоже на VC6, хотя и не совместимо в обратную сторону из-за новой версии ATL. Так что все old fashion проекты с ATL я по возможности создаю в VC6 и потом перетаскиваю на .NET 2003.
vg
Маньяк
Сообщения: 2803
Зарегистрирован: 29 май 2003, 22:29
Откуда: Магадан - Миссиссага

Сообщение vg »

ura писал(а):В опциях при создании проекта надо кажется убрать "Attributed" и включить Merge Proxy Stub, тогда все будет похоже на VC6, хотя и не совместимо в обратную сторону из-за новой версии ATL. Так что все old fashion проекты с ATL я по возможности создаю в VC6 и потом перетаскиваю на .NET 2003.
Спасибо. Попробую.
vg
Маньяк
Сообщения: 2803
Зарегистрирован: 29 май 2003, 22:29
Откуда: Магадан - Миссиссага

Сообщение vg »

Не стал пробовать с 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();
}
Спасибо, если кто-то прояснит мне эти ньюансы.
Ren
Частый Гость
Сообщения: 25
Зарегистрирован: 23 авг 2005, 00:56

Сообщение Ren »

А теперь попробуй тоже самое, только без атрибутов.
Тогда и посмотришь что получиться.
vg
Маньяк
Сообщения: 2803
Зарегистрирован: 29 май 2003, 22:29
Откуда: Магадан - Миссиссага

Сообщение vg »

Ren писал(а):А теперь попробуй тоже самое, только без атрибутов.
Тогда и посмотришь что получиться.
Спасибо за подсказку.
Попробовал. Ясно :lol: . Единственное несответствие, которое нашёл, что пока ручками в idl не пропишешь новый интерфейс - всё равно не работает визард "implement interface", а должен бы, как написано в MSDN.

Ещё вопрос. Attributed класс - очень уж удобное дело. Какое преимущество в использовании проекта с классами без атрибутов?
ura
Житель
Сообщения: 915
Зарегистрирован: 09 мар 2003, 22:46

Сообщение ura »

А теперь попробуй удалить proxy-stub DLL, и посмотри что получится. Ты же говоришь что она отдельно компонуется т.е. merge не делался при создании проекта. По идее отдельно PS DLL нужна только если будет использоваться маршаллинг с удаленного компьютера. Опять таки я смортрю что интерфесы наследуются от IDispatch т.е. как я понимаю нестандартные типы в параметрах методов использоваться не будут.
vg
Маньяк
Сообщения: 2803
Зарегистрирован: 29 май 2003, 22:29
Откуда: Магадан - Миссиссага

Сообщение vg »

ura писал(а):А теперь попробуй удалить proxy-stub DLL, и посмотри что получится.
А что должно получиться? Удаляется прокси проект. Основной проект с ком сервером также радостно и компилируется. Это прокси имеет в депенденсах основной проект (если удалить основной проект, то прокси длл не скомпилировать).
ura писал(а): Ты же говоришь что она отдельно компонуется т.е. merge не делался при создании проекта.
уточним, что я говорю.
1) Она отдельно компануется, если под компоновкой понимать одну из стадии компиляции компилятором и сборки линкером бинарника.
2) В отдельный проект можно выделить прокси длл, если АТЛ проект выбран без атрибутов. Иначе эта опция не доступна.
3) При компиляции и линковке основного проекта прокси длл не собирается. Ты можешь сам сделать билд прокси, если нужно.
ura писал(а): По идее отдельно PS DLL нужна только если будет использоваться маршаллинг с удаленного компьютера.
Совершенно точно.
ura писал(а):Опять таки я смортрю что интерфесы наследуются от IDispatch т.е. как я понимаю нестандартные типы в параметрах методов использоваться не будут.
Не очень тебя понял. Шо имеешь ввиду? Диспатч используется для возможности использования КОМ в скриптовых языках для обеспечения позднего связывания с использованием имен (скриптовые языки не умеют работать непосредственно с ВТБЛ).
А так ... если не предусматривается использование с VB (пишем в С и используем, например, в С++ ) - вполне можно обойтись одним Анноун.
ura
Житель
Сообщения: 915
Зарегистрирован: 09 мар 2003, 22:46

Сообщение ura »

Просто пытаюсь разобраться, поскольку совершенно точно при закоментированном

BEGIN_COM_MAP(CATLSomeObject)
END_COM_MAP()

QueryInterface работать не должен, просто там нет IID этих интерфейсов, откуда им взяться тогда.
vg
Маньяк
Сообщения: 2803
Зарегистрирован: 29 май 2003, 22:29
Откуда: Магадан - Миссиссага

Сообщение vg »

Наверное, я всёже не то, что-то делаю, или не понимаю, или это действительно баг у MS :(
Оба типа COM ( 1) проект ATL attributed; 2) проект ATL с объектами без атрибутов ) прекрасно работают с C++ клиентами, т.е. в обоих случаях оба интерфейса прекрасно разрешаются, так что вызываются методы в коде типа:

Код: Выделить всё

	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();
Но вот в клиенте C# как оказывается запросить оба интерфейса можно только для COM, который сделан с ATL attributed классами.
Т.е. можно повторить С++ вызовы в C# для обоих интерфейсов в коде типа

Код: Выделить всё

		CATLSomeObjectClass		coATL_AttributeCls		= new CATLSomeObjectClass();
		IATLSomeObject			InterfaceAttributed1	= ( IATLSomeObject )	coATL_AttributeCls;
		IATLSomeObject2			InterfaceAttributed2	= ( IATLSomeObject2 )	coATL_AttributeCls;

		InterfaceAttributed1.Message();
		InterfaceAttributed2.Message();
Если же объекты сделаны без атрибутов, то ничего подобного не удаётся сделать в клиенте C# (в отличии от C++). В этом случае второго интерфейса не видно напрочь. :cry: Такое ощущение, что всё же в этом моём проекте без аттрибутов где-то косяки... Хоть и работает с C++ клиентами на ура.

Если кто сталкивался подскажите, плз.
vg
Маньяк
Сообщения: 2803
Зарегистрирован: 29 май 2003, 22:29
Откуда: Магадан - Миссиссага

Сообщение vg »

ura писал(а):Просто пытаюсь разобраться, поскольку совершенно точно при закоментированном

BEGIN_COM_MAP(CATLSomeObject)
END_COM_MAP()

QueryInterface работать не должен, просто там нет IID этих интерфейсов, откуда им взяться тогда.
Ты прав. Если ATL CoКлассы без атрибутов и закоментировать макто - не то, что QueryInterface не будет работать (MS говорить тоже, и это логично, о чём и топик-то), а просто компилироваться не будет. Если же проект с атрибутами, то - там по-барабану. Не влияет это макро не на что. Там проект, как ты сам знаешь, в принципе другой, и очень вкусный для таких ленивых и малограмотных, как я. Я об этом и постил в начале топика, типа, в чём и сыр-бор.

ПС. Сейчас другая беда прицепилась. Если делать без атрибутов - в шарпе не могу получить второй интерфейс. В ClassView виден только один интерфейс, что определил первым (в С++, в ClassView, естественно видны все интерфейсы, и работает зараза, как часы).
ura
Житель
Сообщения: 915
Зарегистрирован: 09 мар 2003, 22:46

Сообщение ura »

А ты проверь TypeLibrary, скорее всего интерфесы читаются оттуда. Это в IDL секции

library YourLibrary
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");

[
uuid(0CD249D4-F18D-11D4-967B-0050BAD26351),
helpstring("Yor class")
]
coclass YouCOMObject
{
[default] interface ISomeDefaultShit;
interface ISomeExtraShit;
};
}

Генератор кода вставляет default интерфейс сам, остальное поскольку вставляется руками - надо добавлять в Type Library тоже руками.

Естественно, что интерфес будет работать и без записи в Type Library, если знаешь его IID - то проблем нет, но внешние системы об этом не ведают. Надеюсь это то что требуется.
Аватара пользователя
ajkj3em
Маньяк
Сообщения: 2063
Зарегистрирован: 12 ноя 2006, 06:53

Сообщение ajkj3em »

какая страшная муть ... рак мозгов
бедные люди, кому с этим дело иметь приходится
vg
Маньяк
Сообщения: 2803
Зарегистрирован: 29 май 2003, 22:29
Откуда: Магадан - Миссиссага

Сообщение vg »

poneyhot писал(а):какая страшная муть ... рак мозгов
бедные люди, кому с этим дело иметь приходится
Конструктивное лекарство есть? Тыж вроде как неплохой программер С++ :wink: (мне кажется только ты ник поменял опять :lol: )
vg
Маньяк
Сообщения: 2803
Зарегистрирован: 29 май 2003, 22:29
Откуда: Магадан - Миссиссага

Сообщение vg »

Вот пока на что наткнулся

Unfortunately, adding additional interfaces to an existing object is harder than you might expect—you have to do a bit of manual labor because there's no wizard to add the interface for you. (It's easy to add another object to your module because there's a wizard to help, however. And there is a wizard to help you implement interfaces for which you have a type library. But because this is a new custom interface, we have no type library to use, so we have to do it by hand.)

Ну и как это соотнести с обещанием счастья от визардов в MSDN? :lol:
Ответить