Создать DCOM сервер
От: dmitry_npi Россия  
Дата: 07.02.08 11:22
Оценка:
Как на C# (вообще на .Net) создать COM-сервер, но не библиотеку классов, а EXE, out-of-proc. Как создать Proxy Stub?
Где можно конкретно об этом почитать?
Атмосферная музыка — www.aventuel.net
Re: Создать DCOM сервер
От: EM Великобритания  
Дата: 15.02.08 11:23
Оценка: -2
Здравствуйте, dmitry_npi, Вы писали:

_>Как на C# (вообще на .Net) создать COM-сервер, но не библиотеку классов, а EXE, out-of-proc. Как создать Proxy Stub?


имхо никак.
Опыт — это такая вещь, которая появляется сразу после того, как была нужна...
Re: Создать DCOM сервер
От: Hacker_Delphi Россия  
Дата: 15.02.08 13:04
Оценка:
Здравствуйте, dmitry_npi, Вы писали:

_>Как на C# (вообще на .Net) создать COM-сервер, но не библиотеку классов, а EXE, out-of-proc. Как создать Proxy Stub?

_>Где можно конкретно об этом почитать?
насколько я помню System.EnterpriseServices может в этом помочь... попытай Влада (VladD2) он с этим копался...
а вообще создать out of proc сервер можно... ставь в параметрах "Make Assembly Com Visible", прописывай Guid'ы и кастомизируй регистрацию... как это делать тут писалось уже несколько раз...
... << RSDN@Home 1.2.0 alpha rev. 789>>
Если при компиляции и исполнении вашей программы не происходит ни одной ошибки — это ошибка компилятора :)))
Re: Создать DCOM сервер
От: Tom Россия http://www.RSDN.ru
Дата: 15.02.08 13:23
Оценка: +1
Здравствуйте, dmitry_npi, Вы писали:

_>Как на C# (вообще на .Net) создать COM-сервер, но не библиотеку классов, а EXE, out-of-proc. Как создать Proxy Stub?

_>Где можно конкретно об этом почитать?

Любой C# класс является/может быть COM сервером. Даже наследовать этот класс не надо от специальных базовых классов. По этому, задача сводится к:
1. Правильной регистрации сервера
2. Вызове CoRegisterClassObject

ЛИБО, использовании COM+ или стандартного сурогата.

вторая задача банальна, так как для вызова CoRegisterClassObject написан банальный врапер RegisterTypeForComClients. С остальным сам справишься?
Народная мудрось
всем все никому ничего(с).
Re[2]: Создать DCOM сервер
От: Tom Россия http://www.RSDN.ru
Дата: 15.02.08 18:16
Оценка: 41 (7)
Tom>С остальным сам справишься?

Как то так:

namespace ConsoleApplication7
{
    [GuidAttribute("682E2D3C-FF7B-4833-8DDF-6CF25389E729")]
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class Foo
    {
        public string Bar()
        {
            return string.Format("Hello, world from process {0}", Process.GetCurrentProcess().Id);
        }
    }

    class Program
    {
        static private void RegisterManagedType(Type type)
        {
            String strClsId = "{" + Marshal.GenerateGuidForType(type).ToString().ToUpper(CultureInfo.InvariantCulture) + "}";
            
            // Create the HKEY_CLASS_ROOT\CLSID key.
            using (RegistryKey ClsIdRootKey = Registry.ClassesRoot.CreateSubKey("CLSID"))
            {
                // Create the HKEY_CLASS_ROOT\CLSID\<CLSID> key.
                using (RegistryKey ClsIdKey = ClsIdRootKey.CreateSubKey(strClsId))
                {
                    ClsIdKey.SetValue("", type.FullName);

                    // Create the HKEY_CLASS_ROOT\CLSID\<CLSID>\LocalServer32 key. 
                    using (RegistryKey LocalServerKey = ClsIdKey.CreateSubKey("LocalServer32"))
                    {
                        LocalServerKey.SetValue("", (new Uri(Assembly.GetExecutingAssembly().CodeBase)).LocalPath);
                    }
                }
            }
        }
        
        [MTAThread]
        static void Main(string[] args)
        {
            RegistrationServices rs = new RegistrationServices();

            if ((args.Length == 1) && (args[0] == "register"))
            {
                RegisterManagedType(typeof(Foo));

                Console.WriteLine("Server registered, press any key to exit");

                Console.ReadKey();

                return;
            }

            Guid FooGuid = typeof(Foo).GUID;

            rs.RegisterTypeForComClients(
                typeof(Foo),
                RegistrationClassContext.RemoteServer | RegistrationClassContext.LocalServer | RegistrationClassContext.InProcessServer,
                RegistrationConnectionType.MultipleUse);

            Console.WriteLine("Server started, press any key to exit");

            Console.ReadKey();
        }
    }
}


Ну и скрипт для проверки сего дела:
Dim oFoo
set oFoo = CreateObject("ConsoleApplication7.Foo")
WScript.echo oFoo.Bar
Народная мудрось
всем все никому ничего(с).
Re[3]: Создать DCOM сервер
От: __WaterFall__ Беларусь vladimir.boltuts@gmail.com
Дата: 02.05.08 13:47
Оценка:
код работает классно, только вот где почитать, как на шарпе понять, когда можно уничтожать свой out-proc server?
надо же как то поймать момент, когда последний COM клиент уйдет, подождать еще секунд 5ть и выгрузить процесс, как это было в ATL.
например у меня вместо ожидания клавиши висит форма, когда ей посылать сообщение "закрыться" или какой есть механизм?

куда копать подскажите?

то есть по сути вопрос такой, как ловить момент когда счетчик ссылок на все мои интерфейсы станет равен нулю?
а то неприятно оставлять в памяти загруженный процесс.
from __python__ import paradigma
from __future__ import generators
from __waterfall__ import power
Re[4]: Создать DCOM сервер
От: __WaterFall__ Беларусь vladimir.boltuts@gmail.com
Дата: 03.05.08 08:56
Оценка:
неужели никто не знает как считать ссылки?
наверняка кто-то сталкивался с тем, что процесс остается в памяти.
или никому не мешают пару тройку забытых EXE-ов в раме?
from __python__ import paradigma
from __future__ import generators
from __waterfall__ import power
Re[5]: Создать DCOM сервер
От: Tom Россия http://www.RSDN.ru
Дата: 04.05.08 15:41
Оценка: 1 (1)
Здравствуйте, __WaterFall__, Вы писали:

__W>неужели никто не знает как считать ссылки?

__W>наверняка кто-то сталкивался с тем, что процесс остается в памяти.
__W>или никому не мешают пару тройку забытых EXE-ов в раме?

Выгружаться можно тогда, когда на сервере нет созданных обьектов.
Соответственно тебе надо в конструкторе твоего обьекта увеличивать счётчик, а в реализации IDisposable pattern-а уменьшать. Видимо только так.
Народная мудрось
всем все никому ничего(с).
Re[6]: Создать DCOM сервер
От: __WaterFall__ Беларусь vladimir.boltuts@gmail.com
Дата: 07.05.08 19:58
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Здравствуйте, __WaterFall__, Вы писали:


__W>>неужели никто не знает как считать ссылки?

__W>>наверняка кто-то сталкивался с тем, что процесс остается в памяти.
__W>>или никому не мешают пару тройку забытых EXE-ов в раме?

Tom>Выгружаться можно тогда, когда на сервере нет созданных обьектов.

Tom>Соответственно тебе надо в конструкторе твоего обьекта увеличивать счётчик, а в реализации IDisposable pattern-а уменьшать. Видимо только так.

в теории я понимаю что так, но к чему прикрутить? конструктору какого объекта?
вот, например, у меня out-proc сервер зарегистрирован как EXE.
первый вызов из кома приходит в статическую ф-цию Main статического класса

    static class ScreenManager
    {
       static void Main()
        {
            Application.Run(new ScreenManagerForm());
        }


в которой создается форма и начинает колбасить цикл сообщений.
ком клиент вызывает ф-цию с атрибутом

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public sealed class Commands
    {
        public void ExtCommand(object arguments)
        {
            // code ...
        }
    }


я вижу в стеке, что вызов в конструктор пришел из unmanaged кода, а ниже ScreenManager::Main()

вот так примерно
my.exe!Commands.Commands()
[External code]
my.exe!ScreenManager::Main()

к какому бы классу не прикручивал IDisposable — не вызывается public void Dispose()
from __python__ import paradigma
from __future__ import generators
from __waterfall__ import power
Re[7]: Создать DCOM сервер
От: Tom Россия http://www.RSDN.ru
Дата: 08.05.08 10:16
Оценка:
__W>к какому бы классу не прикручивал IDisposable — не вызывается public void Dispose()
Dispose вызовется только при сборке мусора, данный способ для детерминированного вызова Dispose не подходит. Реализовывать IDisposable надо у вашего COM обьекта, реализованного на .NET.

В принципе, что бы иметь детерминированное освобождение обьекта, можно попробовать написать на C++ COM обьект, который будет агрегировать .NET обьект и вызывать Dispose при последнем вызове Release. Можно наверное даже сделать такой обьект универсальным, и моникер к нему написать.
Народная мудрось
всем все никому ничего(с).
Re[8]: Создать DCOM сервер
От: __WaterFall__ Беларусь vladimir.boltuts@gmail.com
Дата: 08.05.08 12:05
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Dispose вызовется только при сборке мусора, данный способ для детерминированного вызова Dispose не подходит. Реализовывать IDisposable надо у вашего COM обьекта, реализованного на .NET.


что-то неправильно с этим Dispose(), не вызывается он даже при сборке мусора.

прикрутил IDisposable к классу так


    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public sealed class Commands : IDisposable
    {
        public Commands()
        {
            MessageBox.Show("Create");
        }

        ~Commands()
        {
            MessageBox.Show("Destroy");
        }

        public void ExtCommand(object arguments)
        {
            // 
        }

        public void Dispose()
        {
            MessageBox.Show("Dispose");

            // todo: send WM_QUIT to Form
        }

    }


на форму поставил кнопку

        private void buttonGarbageCollector_Click(object sender, System.EventArgs e)
        {
            GC.Collect();
        }


запускаю одного COM клиента, дергаю ф-цию Commands::ExtCommand()
вижу создание одного объекта, конструктор Commands::Commands()

выгружаю COM клиента (EXE-ник) — ничего не происходит, правильно.
топлю кнопку сборки мусора, вижу деструктор Commands::~Commands()
значит COM клиент отпустил ссылку.

мы знаем, что был один объект, вот он и разрушился, а Dispose() так и не вызвался
from __python__ import paradigma
from __future__ import generators
from __waterfall__ import power
Re[9]: Создать DCOM сервер
От: Tom Россия http://www.RSDN.ru
Дата: 08.05.08 13:26
Оценка:
Здравствуйте, __WaterFall__, Вы писали:

__W>Здравствуйте, Tom, Вы писали:


Tom>>Dispose вызовется только при сборке мусора, данный способ для детерминированного вызова Dispose не подходит. Реализовывать IDisposable надо у вашего COM обьекта, реализованного на .NET.


__W>что-то неправильно с этим Dispose(), не вызывается он даже при сборке мусора.

Естественно нге будет, в такой реализации посмотри как стандартно реализуется IDisposable паттерн
Народная мудрось
всем все никому ничего(с).
Re[8]: Создать DCOM сервер
От: __WaterFall__ Беларусь vladimir.boltuts@gmail.com
Дата: 08.05.08 14:02
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Dispose вызовется только при сборке мусора, данный способ для детерминированного вызова Dispose не подходит. Реализовывать IDisposable надо у вашего COM обьекта, реализованного на .NET.


мне кажется что Dispose вызывается только если объект заворачивать в using () {}, это не мой случай, меня вызывают COM клиенты, объект создается при помощи CCW. Причем Dispose вызыватся не при сборке мусора, а сразу же при достижении конца области видимости. Во всяком случае, так себя ведет компилятор. Dispose вообще у меня не вызывается, если объект не заворачивать в using. Мне не удалось добиться вызова Dispose для обычных объектов.

Tom>В принципе, что бы иметь детерминированное освобождение обьекта, можно попробовать написать на C++ COM обьект, который будет агрегировать .NET обьект и вызывать Dispose при последнем вызове Release. Можно наверное даже сделать такой обьект универсальным, и моникер к нему написать.


в принципе, мне деструктора Commands::~Commands() достаточно, не хочу писать никаких агрегирований, надо было не связываться с дотнетом и писать на плюсах, но такое требование — надо на С#.

не понял про моникер, надо будет матчасть посмотреть...

пока есть такое решение.
на форму ставиться таймер, который раз в 5ть секунд колбасит GC.Collect().
может какие настройки есть у Garbage Collector-a, чтобы без таймера?

сборка мусора вызовет наш деструктор Commands::~Commands().
из деструктора вызываем Close() форме, когда ссылки сойдут на 0.

Если COM клиент придет в момент когда уже Close() вызвана, но переключена задача и стоит в очереди сообщение на закрытие, то получится что старая задача жива, тогда, теоретически, новая задача запуститься [еще один процесс MY.EXE], а старый процесс постепенно закроется, когда задача получит свой квант и проколбасит [message pump] месаги в очереди сообщений.
Вроде все на мази?

И спасибо за советы. Почему-то решение находится только когда с кем-то перетрешь тему.
from __python__ import paradigma
from __future__ import generators
from __waterfall__ import power
Re[9]: Создать DCOM сервер
От: __WaterFall__ Беларусь vladimir.boltuts@gmail.com
Дата: 12.05.08 16:22
Оценка:
прекрасно работает решение с периодическим вызовом GC.Collect()
чувствую, что есть более красивое решение, ведь всего лишь надо перехватить момент, когда последний COM client уходит...
но, как всегда, нет времени...
from __python__ import paradigma
from __future__ import generators
from __waterfall__ import power
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.