连接到C#中的COM事件 - 支持托管和非托管服务器

时间:2008-11-12 01:38:22

标签: c# com interop iconnectionpoint

我正在编写需要连接到COM事件的C#代码。我实现了使用 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接口,因此在尝试将对象强制转换为该接口时,我得到null。

知道我该如何解决这个问题? 我当然可以在C#COM服务器中自己实现IConnectionPointContainer,但是我想要一个更简单的解决方案,我可以很容易地向需要实现COM服务器的其他开发人员解释。

P.S我必须使用IConnectionPointContainer,因为COM服务器可以在非.Net(C ++,Java)中实现。

谢谢, 因巴尔

5 个答案:

答案 0 :(得分:1)

IConnectionPointContainer是在CCW(COM可调用包装器)上实现的,.NET在外部将.NET对象公开为COM对象时自动生成。

尝试在.NET对象上调用Marshal.GetComInterfaceForObject来获取IConnectionPointContainer的COM接口,而不是仅仅转换它。

更新 ...如果不起作用,Marshal.GetIUnknownForObject必须返回一些内容,然后这可能会支持Marshal.QueryInterface来电。

答案 1 :(得分:0)

我没有办法做到这一点。 最终我将在.Net中定义另一个接口,并将编写2个代码路径,一个用于.Net对象,另一个用于真正的COM对象。

答案 2 :(得分:0)

问题是执行GetIUnknownForObject调用会返回一个指针,然后您可以使用其GUID成功调用该指针以获取该对象的IConnectionPointContainer。但是,对QueryInterface的调用只会返回原始的.NET对象,而不是IConnectionPointContainer接口。

我也坚持这一点,如果有人有任何进一步的见解,请分享。我的方案是我使用COM互操作将.NET控件公开为ActiveX。我有一个为事件接收器定义的ComSourceInterface,但在VB6主机中,事件没有按预期连接。因此,我试图为我暴露的.NET控件获取IConnectionPointContainer接口,以便手动挂接事件,但是如果确实实现了,则无法访问此接口 - 或者我可能只是在查看错误的对象? / p>

答案 3 :(得分:0)

我进一步说,因为我在使用COM互操作将.NET控件作为ActiveX公开时手动连接事件时遇到了同样的问题。

如果你使用Reflector(例如Redgate Reflector)挖掘一下UserControl类,你会看到一个'ActiveXImpl'嵌套类成员,它包含另一个名为'AdviseHelper'的嵌套静态类,它有成员ComConnectionPointContainer和ComConnectionPoint。它还具有辅助功能,可根据需要连接点。

有一个问题。当COM interop从您的控件(事件源)连接到连接点容器(其中包含控件事件的事件接收器)时,将调用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'的任何成员class,因此在框架最初调用此函数后,您无法访问它。

您需要获取此IUnknown pUnkEventSink变量来调用AdviseHelper.AdviseConnectionPoint()函数,该函数将执行手动事件连接。这就是我遇到的问题 - 遗憾的是你似乎无法掌握它。

其他人有任何进一步的发展,请告诉我!

答案 4 :(得分:0)

我知道我有点迟到了,但我能够使用IConnectionPoint获得接收事件。请参阅我的回答here。具体检查MyAppDotNetWrapper类及其在测试中的使用方式。

最终,我认为您的m_connectionPoint.Advise(this, out m_cookie);失败了,因为this必须是[ComVisible(true)][ClassInterface(ClassInterfaceType.None)]public