【问题标题】:Connect to COM events in C# - support both managed and unmanaged servers连接到 C# 中的 COM 事件 - 支持托管和非托管服务器
【发布时间】:2008-11-12 01:38:21
【问题描述】:

我正在编写需要连接到 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)中实现。

谢谢, 英巴

【问题讨论】:

    标签: c# com interop iconnectionpoint


    【解决方案1】:

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

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

    更新 ...如果这不起作用,Marshal.GetIUnknownForObject 必须返回一些东西,也许这将支持Marshal.QueryInterface 调用。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

          【解决方案4】:

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

          如果您使用 Reflector(例如 Redgate Reflector)深入了解 UserControl 类,您会看到一个“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() 函数,该函数将执行手动事件连接。这就是我遇到的问题 - 遗憾的是,您似乎无法掌握它。

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

          【讨论】:

            【解决方案5】:

            我知道我有点晚了,但我能够使用 IConnectionPoint 让接收器事件工作。请参阅我的回答here。具体检查MyAppDotNetWrapper 类以及它是如何在测试中使用的。

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

            【讨论】:

              猜你喜欢
              • 2019-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-01-30
              • 2017-11-21
              • 2017-11-21
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多