Итак почему же столько проблем с COM+-ом если компоненты, и особенно клиента создавать средствами .NET Framework?
По большому счету по двум причинам:
1. Большинство людей пытающихся создавать COM+-компоненты на .NET не указывают (явно) гуидов для всех составляющих будущей библиотеки типв.
2. Люди забывают отключить автоматическое приращение версий сборок в проектах созданных VS.NET.
Итак для того чтобы создать полноценное COM+-приложение нужно:
1. Обявить интерфейс (который в последствии будет использоваться как главный (default) интерфейс ком-объекта) отдельно от описания класса.
2. Задать гуиды для:
* Инерфейса
* Ко-класса (т.е. для класса реализующего ком-объект)
* Сборки (он будет являться LibID)
* AppID
Ниже привдены описания самых важных атрибутов и пример COM+-приложения на Шарпе:
Версия сборки и по совместительству библиотки типов. В качестве версии библиотеки типов используется первые две цифры. Если клиент нэйтив, используется именно [version(x.x)]. Для .NET-клиентов используется полная версия сборки (!) которая помещается в custom-атрибут с ID == 90883F05-3D28-11D2-8F17-00A0C9A6186D. Например:
А это пример приложения созданного на Шарепе. В нем можно увидеть применение всех атрибутов и технику описания интерфейса.
[assembly: Guid("49E484AE-02C4-4c77-A36E-7B4ED0BCE11F")]
[assembly: ApplicationActivation(ActivationOption.Server)]
[assembly: ApplicationID("49E484AE-02C4-4c77-FFFF-7B4ED0BCE11F")]
[assembly: AssemblyDescription("Test AssemblyDescription")]
[assembly: ApplicationName("TestNetComPlusServer")]
// Обратите внимание на то, что версия сборки задана явно!
// Т.е. без использования знака *. Это позволяет отучить VS
// излишне беспокоиться о "правильности" версии сборки.
// И тем самым снять проблему постоянной перерегистрации
// COM+-приложения, COM+-прокси и перекомпиляции клиента.
[assembly: AssemblyVersion("1.1.100.200")]
[assembly: AssemblyDelaySign(false)]
// Строгое имя (а именно так лучше переводить "strong name")
// нужно создать утилитой Sn.exe
[assembly: AssemblyKeyFile("..\\..\\TestNetComPlusServer.snk")]
[assembly: AssemblyKeyName("")]
using System;
using System.EnterpriseServices;
using System.Runtime.InteropServices;namespace TestNetComPlusServer
{
[GuidAttribute("FBEAA7C9-06D2-40a3-A3B7-ABC769DDA589")]// Ип интерфейса: Dual, IDispatch или IUnknown[InterfaceType(ComInterfaceType.InterfaceIsDual)]public interface IServer
{
void Method1();
};
// Переключает галочку "Component supports e&vents and statistics",
// тем самым позволяя следить за активностью компонента в Component Services
[EventTrackingEnabledAttribute(true)]
// CLSID. Сногие думают что так задается IID дефолтного интрфейса, но это не так.
[Guid("676C8C4F-7178-4023-863F-BC13658B8688")]
[ClassInterface(ClassInterfaceType.None)]
// Тут же можно задать кучу дургих атрибутов.
//[Transaction(TransactionOption.Disabled)]public class Server: System.EnterpriseServices.ServicedComponent, IServer
{
public Server(){}
// Лдя методов тоже можно задавать атрибуты.
//[AutoComplete]voidIServer.Method1()
{
// Метод ком объекта.int i = 0;
i = i;
}
}
}
Ниже приведен скрипт регистрирующий приложение в GAC и COM+-е:
cd bin\Debug
gacutil /i TestNetComPlusServer.dll
regsvcs /tlb:TestNetComPlusServer.tlb TestNetComPlusServer.dll
В принципе того же эффекта можно добиться изменением опций проекта.
Вот библиотека типов (реинженирнутая с помощью OLEView) получившаяся после компиляции этого проекта:
MessageBox.Show("OK TestNetComPlusServer.Server new");
srv.Method1(); // вызываем метод...
MessageBox.Show("OK Method1");
// Если нужно освободить ссвлку вызываем:
ServicedComponent.DisposeObject((ServicedComponent)srv);
// Иначе экземпляр объекта будет болтаться до сборки мусора.
Да! Совсем забыл. Вот код нетного клиента. Это тоже не маловажно...
// Получаем тип ком-объекта из его CLSID...
Type type = Type.GetTypeFromCLSID(
new Guid("676C8C4F-7178-4023-863F-BC13658B8688"),
"STAS");
// Создаем экземпляр...
IServer srv = (IServer)Activator.CreateInstance(type);
MessageBox.Show("OK TestNetComPlusServer.Server new");
srv.Method1(); // вызываем метод...
MessageBox.Show("OK Method1");
// Если нужно освободить ссвлку вызываем:
ServicedComponent.DisposeObject((ServicedComponent)srv);
// Иначе экземпляр объекта будет болтаться до сборки мусора.
VD>// Обратите внимание на то, что версия сборки задана явно!
VD>// Т.е. без использования знака *. Это позволяет отучить VS
VD>// излишне беспокоиться о "правильности" версии сборки.
VD>// И тем самым снять проблему постоянной перерегистрации
VD>// COM+-приложения, COM+-прокси и перекомпиляции клиента.
Вот за это мы молодец. Только скажи, где ты это нашёл?
Если нам не помогут, то мы тоже никого не пощадим.
Re[2]: Как создавать COM+-компоненты на .NET-е (это ответ, а
VD>>// Обратите внимание на то, что версия сборки задана явно!
VD>>// Т.е. без использования знака *. Это позволяет отучить VS
VD>>// излишне беспокоиться о "правильности" версии сборки.
VD>>// И тем самым снять проблему постоянной перерегистрации
VD>>// COM+-приложения, COM+-прокси и перекомпиляции клиента.
IT>
IT>Вот за это мы молодец. Только скажи, где ты это нашёл?
AVK так затрахался с моим TreeView что что-то там нахимичил и VS стала плевать на версии сборки. Ну я полез MSDN и в VS.NET и нарыл. То или нет не знаю, но работает.
А что за [assembly: Guid("49E484AE-02C4-4c77-A36E-7B4ED0BCE11F")] я не моледец?
Здравствуйте VladD2, Вы писали:
IT>>Вот за это мы молодец. Только скажи, где ты это нашёл?
VD>AVK так затрахался с моим TreeView что что-то там нахимичил и VS стала плевать на версии сборки. Ну я полез MSDN и в VS.NET и нарыл. То или нет не знаю, но работает.
Я не нашёл, хотя перерыл всё. Правда дело было ещё до релиза и может этого тогда ещё не было в документации (а может и в природе),
VD>А что за [assembly: Guid("49E484AE-02C4-4c77-A36E-7B4ED0BCE11F")] я не моледец?
То же молодец, но это я возможно пробовал (опять же, если оно было).
Короче, всё это здорово, но ком всё равно уже пора хоронить
Если нам не помогут, то мы тоже никого не пощадим.
Re[4]: Как создавать COM+-компоненты на .NET-е (это ответ, а
Здравствуйте VladD2, Вы писали:
IT>>Короче, всё это здорово, но ком всё равно уже пора хоронить
VD>Тады пиши в MS писмо с просьбой сделать сервер приложений под ремоутинг. А то самопальные экзешники супратив КОМ+ — это не серьезно.
Со мной работает чел у которого brother-in-law колбасит CLR в Редмонде. Я попросил его по секрету выведать как с этим в MS обстоит дело Скоро узнаем.
Если нам не помогут, то мы тоже никого не пощадим.
Re[6]: Как создавать COM+-компоненты на .NET-е (это ответ, а
Здравствуйте IT, Вы писали: IT>Со мной работает чел у которого brother-in-law колбасит CLR в Редмонде. Я попросил его по секрету выведать как с этим в MS обстоит дело Скоро узнаем.
По секрету? Это хорошо. А на форуме расскажешь?
Успехов,
Виталий.
Re[2]: Как создавать COM+-компоненты на .NET-е (это ответ, а
Здравствуйте VladD2, Вы писали:
VD>Здравствуйте VladD2, Вы писали:
VD>Да! Совсем забыл. Вот код нетного клиента. Это тоже не маловажно... VD>
VD>// Получаем тип ком-объекта из его CLSID...
VD>Type type = Type.GetTypeFromCLSID(
VD> new Guid("676C8C4F-7178-4023-863F-BC13658B8688"),
VD> "STAS");
VD>// Создаем экземпляр...
VD>IServer srv = (IServer)Activator.CreateInstance(type);
VD>MessageBox.Show("OK TestNetComPlusServer.Server new");
VD>srv.Method1(); // вызываем метод...
VD>MessageBox.Show("OK Method1");
VD>// Если нужно освободить ссвлку вызываем:
VD>ServicedComponent.DisposeObject((ServicedComponent)srv);
VD>// Иначе экземпляр объекта будет болтаться до сборки мусора.
VD>
это всё хорошо и свойства и методы для кокласса работают
но как только вводим косвенность(метод или свойство кокласса возвращает другой объект или интерфейс), то работать с полученным объектом не получается:
то ему сериализация(которая для СОМ-объектов не нужна), то ему RemotingException(СОМ не работает через ремотинг)...
VladD2, если у тебя всё же получается, то не мог бы написать примерчик или подредактировать этот здесь
Здравствуйте IT, Вы писали:
IT>Я не нашёл, хотя перерыл всё. Правда дело было ещё до релиза и может этого тогда ещё не было в документации (а может и в природе),
Я про это не в документации нашел а сам догадался. Там правда никакого COM+ не было, но эта коза цеплялась к конкретной версии библиотеки, а так как при перекомпиляции она менялась то студия продолжала цеплять старый вариант.
Здравствуйте VladD2, Вы писали:
VD>1. Обявить интерфейс (который в последствии будет использоваться как главный (default) интерфейс ком-объекта) отдельно от описания класса.
Интерфейс отдельно можно и не объявлять, пусть c# сам поработает и если вы не завели новые public методы, свойства и т.п., т.е. не расширили интерфейс, то будет работать без перерегистрации.
Например,так
[ClassInterface(ClassInterfaceType.AutoDual)]
VD>// Обратите внимание на то, что версия сборки задана явно! VD>// Т.е. без использования знака *. Это позволяет отучить VS VD>// излишне беспокоиться о "правильности" версии сборки. VD>// И тем самым снять проблему постоянной перерегистрации VD>// COM+-приложения, COM+-прокси и перекомпиляции клиента. VD>[assembly: AssemblyVersion("1.1.100.200")]
Так вот с этой строчкой работает ничуть не хуже
[assembly: AssemblyVersion("1.1.*")]
и не требуется ни какой перерегистрации приложения или перекомпиляции клиента
Re[2]: Как создавать COM+-компоненты на .NET-е (это ответ, а
Здравствуйте dragon23, Вы писали:
D>Интерфейс отдельно можно и не объявлять, пусть c# сам поработает и если вы не завели новые public методы, свойства и т.п., т.е. не расширили интерфейс, то будет работать без перерегистрации.
D>Например,так
D>
Нельзя. Есле не ребилд, то покрайней мере банальное добавление метода приведет к тому, что для интерфейса будет сгенерирован новый гуид. Это приведет к неработоспособности половины клиентов. Остальных придется перекомпилировать. Единственный разумный выход — это отдельное описание интерфейса и задание ему гуида вручную.
Более того. Это еще и более коректно с точки зрения качества описания интерфейса между клиентом и сервером. Всегда лучше чтобы паблик-интерфейс передающийся на сервер был описан отдельно и его методы не пересикались с методами реализации. Вдруг придется вызывать этот объект не как COM-сервер, т.е. через его паблик-методы или через другой интерфейс?
VD>>// Обратите внимание на то, что версия сборки задана явно! VD>>// Т.е. без использования знака *. Это позволяет отучить VS VD>>// излишне беспокоиться о "правильности" версии сборки. VD>>// И тем самым снять проблему постоянной перерегистрации VD>>// COM+-приложения, COM+-прокси и перекомпиляции клиента. VD>>[assembly: AssemblyVersion("1.1.100.200")]
D>Так вот с этой строчкой работает ничуть не хуже
D>
D>[assembly: AssemblyVersion("1.1.*")]
D>
D>и не требуется ни какой перерегистрации приложения или перекомпиляции клиента
Ну ты попробуй. Создай клиента на .NET... зарегистрируй его на удаленном компьютере... перекомпилируй сервер... далее ты все поймешь, ты все увидишь сам.
Здравствуйте zelyony, Вы писали:
Z>это всё хорошо и свойства и методы для кокласса работают Z>но как только вводим косвенность(метод или свойство кокласса возвращает другой объект или интерфейс), то работать с полученным объектом не получается: Z>то ему сериализация(которая для СОМ-объектов не нужна), то ему RemotingException(СОМ не работает через ремотинг)... Z>VladD2, если у тебя всё же получается, то не мог бы написать примерчик или подредактировать этот здесь
Здравствуйте VladD2, Вы писали:
VD>Нельзя. Есле не ребилд, то покрайней мере банальное добавление метода приведет к тому, что для интерфейса будет сгенерирован новый гуид. Это приведет к неработоспособности половины клиентов. Остальных придется перекомпилировать. Единственный разумный выход — это отдельное описание интерфейса и задание ему гуида вручную.
Никто не мешает задать тот же самый guid и для класса и тогда изменение ничего за собой не повлечет, а чтобы интерфейс не менялся не стоит методы, свойства и т.п. делать public.
VD>Ну ты попробуй. Создай клиента на .NET... зарегистрируй его на удаленном компьютере... перекомпилируй сервер... далее ты все поймешь, ты все увидишь сам.
Насчет .Net клиента не знаю не пробовал, а клиенты на Cpp и Delphi работают и не замечают какая версия.
Re[4]: Как создавать COM+-компоненты на .NET-е (это ответ, а
Здравствуйте dragon23, Вы писали:
D>Никто не мешает задать тот же самый guid и для класса
Что значит тот же самый? Ты задаеш гуид исключительно для класа, т.е. ты задаешь исключительно CLSID. Гуид интерфейса при этом задать невозможно!
D>и тогда изменение ничего за собой не повлечет,
Первый же клиент пытающийся без перекомпиляции сделать QI будет послан к ... Причем тебе придется перерегистрировать приложение в COM+-е, переделывать прокси и перекомпилировать клиента. Как показывает практика если эти действия не автоматизированы, получается форменное шаманство с непредсказуемым результатом.
D>а чтобы интерфейс не менялся не стоит методы, свойства и т.п. делать public.
Это диктуется логикой работы приложения. Иногда без этог не обойтись. Более того интерфейс в процессе разработки принципиально изменяется. Обычно при этом всего лишь добавляются методы. И явное прописывание гуидов является самым просты способом избежать шаманства.
VD>>Ну ты попробуй. Создай клиента на .NET... зарегистрируй его на удаленном компьютере... перекомпилируй сервер... далее ты все поймешь, ты все увидишь сам.
D>Насчет .Net клиента не знаю не пробовал, ...
Не завешь, а говоришь. Мы не просто так извращались. Была реальная проблема... Даже обычная перекомпиляция приводила к неработоспособности клиента. Я не знаю точно кто тому виной, но так она решается.
Здравствуйте AndrewVK, Вы писали:
IT>>Я не нашёл, хотя перерыл всё. Правда дело было ещё до релиза и может этого тогда ещё не было в документации (а может и в природе),
AVK>Я про это не в документации нашел а сам догадался. Там правда никакого COM+ не было, но эта коза цеплялась к конкретной версии библиотеки, а так как при перекомпиляции она менялась то студия продолжала цеплять старый вариант.
Значит это всё не документировано, т.е. почти хак. И после этого этот злобный Влад обвиняет меня в невнимательности при чтении документации и предвзятому отношению к веб-сервисам. как жить дальше.
Если нам не помогут, то мы тоже никого не пощадим.
Re[6]: Как создавать COM+-компоненты на .NET-е (это ответ, а
Здравствуйте IT, Вы писали:
AVK>>Я про это не в документации нашел а сам догадался. Там правда никакого COM+ не было, но эта коза цеплялась к конкретной версии библиотеки, а так как при перекомпиляции она менялась то студия продолжала цеплять старый вариант.
IT>Значит это всё не документировано, т.е. почти хак. И после этого этот злобный Влад обвиняет меня в невнимательности при чтении документации и предвзятому отношению к веб-сервисам. как жить дальше.
Вообще-то если вызвать хелп на AssemblyVersion, то там все более менее объясняется. Догатдаться нужно только о том, что в референсе запоминается полный номер сборки. Но это вроде не сложно.
Здравствуйте AndrewVK, Вы писали:
AVK>Здравствуйте IT, Вы писали:
IT>>Я не нашёл, хотя перерыл всё. Правда дело было ещё до релиза и может этого тогда ещё не было в документации (а может и в природе),
AVK>Я про это не в документации нашел а сам догадался. Там правда никакого COM+ не было, но эта коза цеплялась к конкретной версии библиотеки, а так как при перекомпиляции она менялась то студия продолжала цеплять старый вариант.
Странное поведение. Не разу с подобным не встречался. Если все проекты в рамках одного solution, то студия нормально перестраивает все ссылки.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.