Подключение к событиям COM в C # - поддержка как управляемых, так и неуправляемых серверов - PullRequest
1 голос
/ 12 ноября 2008

Я пишу код C #, который должен подключаться к событиям COM. Я реализовал использование IConnectionPointContainer и IConnectionPoint, таким образом:

      IConnectionPointContainer connectionPointContainer = internalGenerator as IConnectionPointContainer;
      if (connectionPointContainer == null)
      {
        Debug.Fail("The script generator doesn't support the required interface - IConnectionPointContainer");
        throw new InvalidCastException("The script generator doesn't support the required interface - IConnectionPointContainer");
      }
      Guid IID_IScriptGeneratorEvents = typeof(IScriptGeneratorCallback).GUID;
      connectionPointContainer.FindConnectionPoint(ref IID_IScriptGeneratorEvents, out m_connectionPoint);
      m_connectionPoint.Advise(this, out m_cookie);

Проблема заключается в том, что, когда COM-сервер фактически реализуется в .Net (скажем, C #), после того, как .Net его создает, он обрабатывает его как объект .Net, а не как объект COM. Поскольку объект .Net не реализует интерфейс IConnectionPointContainer, я получаю нулевое значение при попытке привести объект к этому интерфейсу.

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

P.S. Я должен использовать IConnectionPointContainer, поскольку COM-сервер может быть реализован не в .Net (C ++, Java).

Спасибо, Инбар

Ответы [ 5 ]

1 голос
/ 12 ноября 2008

IConnectionPointContainer реализован в CCW (вызываемой оболочке COM), которую .NET автоматически генерирует при внешнем представлении вашего объекта .NET как объекта COM.

Попробуйте вызвать Marshal.GetComInterfaceForObject для объекта .NET, чтобы получить COM-интерфейс для IConnectionPointContainer, а не просто приводить его.

update ... и если это не сработает Marshal.GetIUnknownForObject должен что-то вернуть, и, возможно, тогда будет поддерживаться вызов Marshal.QueryInterface,

0 голосов
/ 02 мая 2013

Я знаю, что немного опоздал, но я смог заставить работать события приемника, используя IConnectionPoint. Смотрите мой ответ здесь . В частности, проверьте класс MyAppDotNetWrapper и как он используется в тесте.

В конечном итоге я думаю, что ваш m_connectionPoint.Advise(this, out m_cookie); терпит неудачу, потому что this должно быть [ComVisible(true)], [ClassInterface(ClassInterfaceType.None)] и public.

0 голосов
/ 26 августа 2009

У меня получилось немного дальше, так как у меня возникла та же проблема с ручным подключением событий при представлении элемента управления .NET в качестве ActiveX с помощью взаимодействия COM.

Если вы немного покопаетесь в классе UserControl с помощью Reflector (например, Redgate Reflector), вы увидите вложенный класс ActiveXImpl, который содержит другой вложенный статический класс с именем 'AdviseHelper', который имеет члены ComConnectionPointContainer и ComConnectionPoint. Он также имеет вспомогательные функции для соединения точек по вашему усмотрению.

Есть проблема. Когда COM-взаимодействие от вашего элемента управления (источника события) подключается к контейнеру точки подключения (который содержит приемник событий для событий вашего элемента управления), вызывается функция IQuickActivate.QuickActivate, которая затем вызывает класс AdviseHelper 'AdviseConnectionPoint 'функция.

Указатель интерфейса IUnknown на приемник событий на вашем клиенте (т.е. не на вашем контроле, на объекте, который его содержит) передается в эту функцию QuickActivate (параметр 'pUnkEventSink'. В отражателе эта функция выглядит следующим образом, и я выделено, где происходит фактическое подключение события:

internal void QuickActivate(UnsafeNativeMethods.tagQACONTAINER pQaContainer, UnsafeNativeMethods.tagQACONTROL pQaControl)
{
    int num;
    this.LookupAmbient(-701).Value = ColorTranslator.FromOle((int) pQaContainer.colorBack);
    this.LookupAmbient(-704).Value = ColorTranslator.FromOle((int) pQaContainer.colorFore);
    if (pQaContainer.pFont != null)
    {
        Control.AmbientProperty ambient = this.LookupAmbient(-703);
        IntSecurity.UnmanagedCode.Assert();
        try
        {
            Font font2 = Font.FromHfont(((UnsafeNativeMethods.IFont) pQaContainer.pFont).GetHFont());
            ambient.Value = font2;
        }
        catch (Exception exception)
        {
            if (ClientUtils.IsSecurityOrCriticalException(exception))
            {
                throw;
            }
            ambient.Value = null;
        }
        finally
        {
            CodeAccessPermission.RevertAssert();
        }
    }
    pQaControl.cbSize = UnsafeNativeMethods.SizeOf(typeof(UnsafeNativeMethods.tagQACONTROL));
    this.SetClientSite(pQaContainer.pClientSite);
    if (pQaContainer.pAdviseSink != null)
    {
        this.SetAdvise(1, 0, (IAdviseSink) pQaContainer.pAdviseSink);
    }
    IntSecurity.UnmanagedCode.Assert();
    try
    {
        ((UnsafeNativeMethods.IOleObject) this.control).GetMiscStatus(1, out num);
    }
    finally
    {
        CodeAccessPermission.RevertAssert();
    }
    pQaControl.dwMiscStatus = num;
    if ((pQaContainer.pUnkEventSink != null) && (this.control is UserControl))
    {
        Type defaultEventsInterface = GetDefaultEventsInterface(this.control.GetType());
        if (defaultEventsInterface != null)
        {
            IntSecurity.UnmanagedCode.Assert();
            try
            {
                **AdviseHelper.AdviseConnectionPoint(this.control, pQaContainer.pUnkEventSink, defaultEventsInterface, out pQaControl.dwEventCookie);**
            }
            catch (Exception exception2)
            {
                if (ClientUtils.IsSecurityOrCriticalException(exception2))
                {
                    throw;
                }
            }
            finally
            {
                CodeAccessPermission.RevertAssert();
            }
        }
    }
    if ((pQaContainer.pPropertyNotifySink != null) && UnsafeNativeMethods.IsComObject(pQaContainer.pPropertyNotifySink))
    {
        UnsafeNativeMethods.ReleaseComObject(pQaContainer.pPropertyNotifySink);
    }
    if ((pQaContainer.pUnkEventSink != null) && UnsafeNativeMethods.IsComObject(pQaContainer.pUnkEventSink))
    {
        UnsafeNativeMethods.ReleaseComObject(pQaContainer.pUnkEventSink);
    }
}

Переменная 'pUnkEventSink' передается в эту функцию через структуру tagQACONTROL, но, как вы можете видеть, в отличие от IAdviseSink, контейнера элемента управления, стиля шрифта и т. Д., Эта переменная не установлена ​​ни для одного элемента ActiveXImpl. класс, и, следовательно, вы не можете получить к нему после того, как эта функция была первоначально вызвана структурой.

Вам нужно получить эту переменную IUnknown pUnkEventSink для вызова функции AdviseHelper.AdviseConnectionPoint (), которая будет выполнять ручное подключение события. И это проблема, с которой я столкнулся - к сожалению, вы просто не можете ее решить.

У кого-нибудь еще были какие-либо изменения в этом, дайте мне знать!

0 голосов
/ 25 августа 2009

Проблема заключается в том, что выполнение вызова GetIUnknownForObject возвращает указатель, который затем можно успешно вызвать, чтобы получить IConnectionPointContainer для объекта, используя его GUID. Но этот вызов QueryInterface затем просто возвращает исходный объект .NET, а не интерфейс IConnectionPointContainer.

Я тоже застрял с этим, и если у кого-то есть какие-либо идеи, пожалуйста, поделитесь. Мой сценарий состоит в том, что я представляю элемент управления .NET как ActiveX с помощью взаимодействия COM. У меня есть ComSourceInterface, определенный для приемника событий, но на хосте VB6 события не подключаются должным образом. Поэтому я пытаюсь получить интерфейс IConnectionPointContainer для моего открытого элемента управления .NET, чтобы вручную подключить события, но не могу добраться до этого интерфейса, если он действительно реализован - или, возможно, я просто смотрю на неправильный объект? 1003 *

0 голосов
/ 12 ноября 2008

Я не нашел способа сделать это. Со временем я определю другой интерфейс в .Net и напишу 2 пути кода, один для объектов .Net и один для реальных объектов COM.

...