Требования C# к COM-серверу
От: Eugene Sh Россия  
Дата: 17.02.05 15:52
Оценка:
Надо написать клиета на C#, который взаимодействовал бы с уже написанным COM-сервером. Клиент должен получать COM-овские сообщения от сервера.

Про сервер точно известно, что для него можно написать C++ клиент, который получал бы от него COM-сообщения (через ConnectionPoint). Есть работающий пример.

Вопрос: могло ли так получится, что сервер не поддерживает возможность такого взаимодействия с C#-клиентом? Какие требования предъявляет C# к COM-клиенту, чтобы они могли взаимодействовать таким образом? Может, какое-то из этих требований не выполняется.

Потому что про предыдущую версию сервера сами авторы утверждали, что возможность взаимодействия с .NET отсутствует.

Вобщем, ключевой вопрос — про требования, предъявляемые C#-клиентом к COM-серверу.
Re: Требования C# к COM-серверу
От: Eugene Sh Россия  
Дата: 17.02.05 15:56
Оценка:
Забыл написать.

Вопрос возник потому, что я написал клиента на C#, который, по идее, должен работать. Так вот, он нормально вызывает методы Сервера, но никаких сообщений от него не получает.
Re[2]: Требования C# к COM-серверу
От: bo Россия  
Дата: 17.02.05 18:20
Оценка:
Здравствуйте, Eugene Sh, Вы писали:

ES>Вопрос возник потому, что я написал клиента на C#, который, по идее, должен работать. Так вот, он нормально вызывает методы Сервера, но никаких сообщений от него не получает.


Если на C++ работает, то и на шарпе должен работать. Специфичных требований нет.
Думаю, ты что-то не так делаешь. Покажи код клиента.

PS: а ты вообще на события-то подписываешься?
В человечишке все должно быть прекрасненьким: и одёжка, и душенка, и мордочка, и мыслишки.
Re[3]: Требования C# к COM-серверу
От: Eugene Sh Россия  
Дата: 18.02.05 08:15
Оценка:
Здравствуйте, bo, Вы писали:

bo>Покажи код клиента.



using System;
using CTIOSCLIENTLib;

namespace LicenseManagerConsole
{
    /// <summary>
    /// Summary description for Class1.
    /// </summary>
    class Class1
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            CTIOSCLIENTLib.Session session = new CTIOSCLIENTLib.Session();
            session.OnConnection += new _IAllEvents_OnConnectionEventHandler(session_OnConnection);
            session.OnConnectionClosed += new _IAllEvents_OnConnectionClosedEventHandler(session_OnConnectionClosed);
            session.OnConnectionFailure += new _IAllEvents_OnConnectionFailureEventHandler(session_OnConnectionFailure);
            session.OnConnectionRejected += new _IAllEvents_OnConnectionRejectedEventHandler(session_OnConnectionRejected);

            CTIOSCLIENTLib.Arguments rArgs = new CTIOSCLIENTLib.ArgumentsClass();
/*
тут была настройка rArgs...
*/

            int returnCode = session.Connect(rArgs);

            Console.Read();

            CTIOSCLIENTLib.Arguments rDiscArgs = new CTIOSCLIENTLib.ArgumentsClass();
            session.Disconnect(rDiscArgs);
        }

        public static void session_OnConnection(CTIOSCLIENTLib.Arguments rArgs)
        {
            Console.WriteLine("session_OnConnection!!!");
        }

        private static void session_OnConnectionClosed(Arguments pIArguments)
        {
            Console.WriteLine("session_OnConnectionClosed!!!");
        }

        private static void session_OnConnectionFailure(Arguments pIArguments)
        {
            Console.WriteLine("session_OnConnectionFailure!!!");
        }

        private static void session_OnConnectionRejected(Arguments pIArguments)
        {
            Console.WriteLine("session_OnConnectionRejected!!!");
        }
    }
}


Что я сделал:
1) добавил в проект reference на COM-dll`ки, в которых реализованы нужные мне классы. Для них были созданы .NET-dll`ки
2) строчки такого типа:

session.OnConnection += new _IAllEvents_OnConnectionEventHandler(session_OnConnection);


были созданы автоматически Visual Studio .NET. Сам я только написал "session.OnConnection += new", а дальше VS сам предложил мне подставить остальное. Так что ошибок там быть не должно.

Судя по документации, после вызова session.Connect() должно прийти сообщение об успехе или неудаче коннекта. Вот оно то и не приходит.
Re[4]: Требования C# к COM-серверу
От: bo Россия  
Дата: 18.02.05 08:38
Оценка:
Здравствуйте, Eugene Sh, Вы писали:

Попробуй так:

using System;
using CTIOSCLIENTLib;

namespace LicenseManagerConsole
{
    class Class1 : MarshalByRefObject,
        IDisposable
    {
        CTIOSCLIENTLib.Session session = new CTIOSCLIENTLib.Session();

        public Class1()
        {
            session.OnConnection += new _IAllEvents_OnConnectionEventHandler(session_OnConnection);
            session.OnConnectionClosed += new _IAllEvents_OnConnectionClosedEventHandler(session_OnConnectionClosed);
            session.OnConnectionFailure += new _IAllEvents_OnConnectionFailureEventHandler(session_OnConnectionFailure);
            session.OnConnectionRejected += new _IAllEvents_OnConnectionRejectedEventHandler(session_OnConnectionRejected);
        }

        public void Dispose()
        {
            session.OnConnection -= new _IAllEvents_OnConnectionEventHandler(session_OnConnection);
            session.OnConnectionClosed -= new _IAllEvents_OnConnectionClosedEventHandler(session_OnConnectionClosed);
            session.OnConnectionFailure -= new _IAllEvents_OnConnectionFailureEventHandler(session_OnConnectionFailure);
            session.OnConnectionRejected -= new _IAllEvents_OnConnectionRejectedEventHandler(session_OnConnectionRejected);
        }

        void Connect()
        {
            CTIOSCLIENTLib.Arguments rArgs = new CTIOSCLIENTLib.ArgumentsClass();
            /*
            тут была настройка rArgs...
            */

            int returnCode = session.Connect(rArgs);
        }

        void Disconnect()
        {
            CTIOSCLIENTLib.Arguments rDiscArgs = new CTIOSCLIENTLib.ArgumentsClass();
            session.Disconnect(rDiscArgs);
        }

        [STAThread]
        static void Main(string[] args)
        {
            using(Class1 clientSession=new Class1())
            {
                clientSession.Connect();

                Console.Read();

                clientSession.Disconnect();
            }
        }

        public void session_OnConnection(Arguments rArgs)
        {
            Console.WriteLine("session_OnConnection!!!");
        }

        public void session_OnConnectionClosed(Arguments pIArguments)
        {
            Console.WriteLine("session_OnConnectionClosed!!!");
        }

        public void session_OnConnectionFailure(Arguments pIArguments)
        {
            Console.WriteLine("session_OnConnectionFailure!!!");
        }

        public void session_OnConnectionRejected(Arguments pIArguments)
        {
            Console.WriteLine("session_OnConnectionRejected!!!");
        }
    }
}
В человечишке все должно быть прекрасненьким: и одёжка, и душенка, и мордочка, и мыслишки.
Re[5]: Требования C# к COM-серверу
От: Eugene Sh Россия  
Дата: 18.02.05 09:04
Оценка:
Не помогло... Результат тот же самый.

Может поможет кусок лога?

02/18/05 11:56:50.749 268 LicenseManagerConsole Thd(00001316) ** Attempting to connect to server [111.11.11.11 (111.11.11.11) port:42028] **
02/18/05 11:56:50.765 268 LicenseManagerConsole Thd(00002316) NotifyConnectionRestored:: Adding system event eSysCTIOSServerOnline
02/18/05 11:57:05.765 268 LicenseManagerConsole Thd(00002316) CilEventSink::OnEvent, MsgWaitForMultipleObjectsEx returned 102 while waiting for event


Последнее сообщение (про MsgWaitForMultipleObjectsEx) начинает повторяться с интервалом 5 секунд. 102 — код WAIT_TIMEOUT
Требования C# к COM-серверу
От: Аноним  
Дата: 17.02.05 17:03
Оценка:
> Надо написать клиета на C#, который взаимодействовал бы с уже написанным COM-сервером. Клиент должен получать COM-овские сообщения от сервера.

Пример подключения к исходящим событиям COM-объекта, например, WebBrowser'а:

SinkHelper sh = new SinkHelper(this.WebBrowser, new Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"), new Event_SinkHelper());
sh.Advise();
...

[GuidAttribute("34A715A0-6587-11D0-924A-0020AFC7AC4D")]
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
[ComImportAttribute]
interface DWebBrowserEvents2
{
[DispId(0x000000fc)] void NavigateComplete2(object pDisp, ref object URL);
}

sealed class Event_SinkHelper : DWebBrowserEvents2
{
public void NavigateComplete2(object pDisp, ref object url)
{
MessageBox.Show("OK !!!!!!!");
}
}

public class SinkHelper
{
private UCOMIConnectionPoint ppcp = null;
private int pdwCookie = -1;
private object Source = null;
private Guid iid_Events;
private object sink = null;

/// <summary>
///
/// </summary>
/// <param name="obj">__ComObject</param>
/// <param name="sink">приемник событий</param>
/// <param name="interfaceType">Com-интерфейс, от которого наследует sink</param>
public SinkHelper(object obj, object sink, Type interfaceType)
{
this.Source = obj;
this.sink = sink;
Attribute attr = Attribute.GetCustomAttribute(interfaceType, typeof(GuidAttribute));
if(attr != null)
this.iid_Events = new Guid(((GuidAttribute)attr).Value);
else
throw(new ArgumentException("GuidAttribute not find."));
}

public bool autoUnadvise = false;

public bool Advise()
{
IntPtr ppv;
Guid iid_IConnectionPointContainer = new Guid("B196B284-BAB4-101A-B69C-00AA00341D07");
if(0 == Marshal.QueryInterface(Marshal.GetIUnknownForObject(this.Source), ref iid_IConnectionPointContainer, out ppv))
{
try
{
///получим ссылку на интерфрейс:
UCOMIConnectionPointContainer cpc = (UCOMIConnectionPointContainer)Marshal.GetObjectForIUnknown(ppv);
if(cpc != null)
{
ppcp = null;
cpc.FindConnectionPoint(ref iid_Events, out ppcp);
if(ppcp != null)
ppcp.Advise(this.sink, out pdwCookie);
}
}
catch(Exception exc)
{
throw new Exception("SinkHelper::FindConnectionPoint Failed.", exc);
}
finally
{
Marshal.Release(ppv);
}
}
return true;
}

public bool Unadvise()
{
if(ppcp != null && pdwCookie != 0x0)
{
try
{
ppcp.Unadvise(pdwCookie);
pdwCookie = -1;
ppcp = null;
}
catch
{
}
}
return true;
}

~SinkHelper()
{
if(autoUnadvise)
Unadvise();
}
}



данное сообщение получено с www.gotdotnet.ru
ссылка на оригинальное сообщение
Re: Требования C# к COM-серверу
От: Eugene Sh Россия  
Дата: 21.02.05 11:20
Оценка:
У меня не компилировалось, поэтому я сделал некоторые предположения относительно поправок.
Сразу извиняюсь, если понял неправильно.

А>SinkHelper sh = new SinkHelper(this.WebBrowser, new Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"), new Event_SinkHelper());

А>sh.Advise();
А>...

Тут не надо поменять выделенное местами?

А>[GuidAttribute("34A715A0-6587-11D0-924A-0020AFC7AC4D")]

А>[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
А>[ComImportAttribute]
А>interface DWebBrowserEvents2
А>{
А> [DispId(0x000000fc)] void NavigateComplete2(object pDisp, ref object URL);
А>}

В моём случае, GuidAttribute надо посмотреть через OLE/COM Object Viewer для интерфейса событий?


А> public SinkHelper(object obj, object sink, Type interfaceType)

А> {
А> this.Source = obj;
А> this.sink = sink;
А> Attribute attr = Attribute.GetCustomAttribute(interfaceType, typeof(GuidAttribute));
А> if(attr != null)
А> this.iid_Events = new Guid(((GuidAttribute)attr).Value);
А> else
А> throw(new ArgumentException("GuidAttribute not find."));
А> }

Можно ли указать параметр типа Cuid вместо выделеннго? И внутри метода просто присвоить this.iid_Events = guidParam?


Вобщем, сделал всё, как и рассказал. Но всё равно сообщения не приходят
Re: Требования C# к COM-серверу
От: Аноним  
Дата: 21.02.05 20:18
Оценка: 7 (2)
> У меня не компилировалось

Прошу прощения, непроверенную версию прислал.

Порядок подключения к событиям COM объекта:

Если посмотреть его Type Info в "Ole/COM Object Viewer", то увидим примерно следующее:

[
uuid(...),
version(...),
helpstring(...)
]
library ...
{
// Forward declare all types defined in this typelib
...
dispinterface SameEvents;
...

[
uuid(...),
helpstring(...),
control
]
coclass CoClassName {
interface ISameInterface1;
[default] interface ISameInterface2;
[source] dispinterface SameEvents;
...
};

...

[
uuid(83A3A9A8-51AD-4cca-9F8A-9B423F618C12),
helpstring(...),
hidden
]
dispinterface SameEvents {
properties:
methods:
...
[id(0x0000003b), helpstring(...)]
void Begin();
...
}
}

--

Теперь известно к чему подключаться. Определяем в программе необходимый интерфейс.

[
Guid("83A3A9A8-51AD-4cca-9F8A-9B423F618C12"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)
]
public interface SameEvents
{
[DispId(0x0000003b)]
void Begin();
}

public class MyClass : SameEvents
{
private UCOMIConnectionPoint _ConnectionPoint;
private int _Cookie = -1;
private object _ComObject;
public MyClass()
{
// создать COM объект
_ComObject = ...;
}
public void Connect()
{
// подключаемся к событию
UCOMIConnectionPointContainer icpc = (UCOMIConnectionPointContainer)_ComObject;
Guid g = typeof(SameEvents).GUID;
icpc.FindConnectionPoint(ref g, out this._ConnectionPoint);

// первый параметр — this, потому что MyClass реализует SameEvents
this._ConnectionPoint.Advise(this, out this._Cookie);
}
public void Disconnect()
{
if (this._Cookie != -1)
this._ConnectionPoint.Unadvise(this._Cookie);

this._Cookie = -1;
this._ConnectionPoint = null;
}
void SameEvents.Begin()
{
MessageBox.Show("Begin");
}
}

Обязательно надо отключиться от событий COM объекта.
Например, так:

~MyClass()
{
Disconnect();
}


данное сообщение получено с www.gotdotnet.ru
ссылка на оригинальное сообщение
Re[2]: Требования C# к COM-серверу
От: Eugene Sh Россия  
Дата: 22.02.05 08:35
Оценка:
Здравствуйте, Аноним, Вы писали:

А> public MyClass()

А> {
А> // создать COM объект
А> _ComObject = ...;
А> }

Может, глупый вопрос, но. Как тут создать объект? Какой объект создавать? Тот, который будет посылать event`ы?
Re[2]: Требования C# к COM-серверу
От: Аноним  
Дата: 22.02.05 12:21
Оценка: 12 (1)
> Как тут создать объект?

Способ создания COM обектов:

1) COM объект должен быть зарегистрирован (см. утилиту regsvr32)

2) Надо знать CLSID необходимого COM объекта.

CLSID можно найти в Type Info, например, в "Ole/COM Object Viewer", увидим примерно следующее:

[
uuid(...),
version(...),
helpstring(...)
]
library BestComObjects
{
...
[
uuid(45FD11F9-DE38-497e-9986-9907A8814B18), // CLSID СOM-объекта
helpstring("Best COM object"),
control
]
coclass BestCom { // СOM-объект
interface ISameInterface1;
[default] interface ISameInterface2;
[b]source[/b]] dispinterface SameEvents; // источник событий
...
};
...
}

Либо надо знать PROGID. Для этого открываем regedit. Потом задаем поиск "Best COM object" (без кавычек). В дереве cлева, имя открытой папки — это есть PROGID, например, Best.Object

3) Создаем COM-объект

// если знаем CLSID
Guid g = new Guid("45FD11F9-DE38-497e-9986-9907A8814B18");
Type t = Type.GetTypeFromCLSID(g, true);
object myComObj = Activator.CreateInstance(t);

— или —

// если знаем PROGID
string progid = "Best.Object";
Type t = Type.GetTypeFromProgID(progid, true);
object myComObj = Activator.CreateInstance(t);


— или —

Если COM объект был подключен к проекту через VS.IDE, т.е. c помощью Add Reference.

см. Solution Explorer

Solution 'MyProject' (1 project)
MyProject
References
BestComObjects

Можно использовать new для создания COM объекта, например:

using BestComObjects;
...
// суффикс Class добавляет VS.IDE при создании обертки для COM объекта
BestComClass bcc = new BestComClass();



данное сообщение получено с www.gotdotnet.ru
ссылка на оригинальное сообщение
Re: Требования C# к COM-серверу
От: Eugene Sh Россия  
Дата: 25.02.05 15:27
Оценка:
Я создал небольшую COM dll-ку, с которой и общается моё C#-приложение. dll-ка просто перенаправляет вызовы от C# к другому COM-серверу. А при получении событий от COM-сервера генерит аналогичное событие. И вот его моё C#-приложение уже удачно ловит.

И тут недавно заметил такую особенность. Если в моём приложении стоит атрибут [STAThread] перед методом Main, то сообщения не приходят! Без атрибута всё работает.
Может, всё дело было только в этом? C# и COM-приложение использовали разные потоковые модели?
Как узнать, какую модель использует COM-сервер?
Re: Требования C# к COM-серверу
От: Аноним  
Дата: 25.02.05 22:19
Оценка: 5 (1)
> Как узнать, какую модель использует COM-сервер?

в MSDN есть статья: "The Rules of the Component Object Model" ms-help://MS.MSDNQTR.2004JAN.1033/dncomg/html/msdn_therules.htm

В самом низу "Apartment Threading Model" про ThreadingModel.

Как прочитать програмно не знаю, но можно посмотреть в реестре:

HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\
{CLSID}\InprocServer32 = <path to 32-bit inproc server>
\ThreadingModel = <threading model>

ThreadingModel=Apartment. Single-threaded apartment.
ThreadingModel=Both. Single-threaded or multithreaded apartment.
ThreadingModel=Free. Multithreaded apartment.
ThreadingModel=Neutral. Neutral apartment (available in Windows 2000).

Подробности в описании InprocServer32
ms-help://MS.MSDNQTR.2004JAN.1033/com/htm/reg_5n8y.htm



данное сообщение получено с www.gotdotnet.ru
ссылка на оригинальное сообщение
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.