对 Word 加载项公开的对象调用方法会引发 RemotingException

发布于 2024-11-06 15:19:25 字数 2099 浏览 0 评论 0原文

我正在用 C# 编写一个(共享)Word 加载项,并希望通过 COMAddIn 类的 Object 属性公开对象来与其进行通信。

因为我希望我的代码在 UI 线程上执行,所以我从 StandardOleMarshalObject 类派生我的外接程序和公开对象。这应该按照此处此处。

但是通过这样做,当我针对 .NET 2.0 或 .NET 4.0 进行编译时,我会得到不同的行为。当针对 .NET 4.0 进行编译时,我公开的对象是 __ComObject 类型,并且可以将其自身强制转换为我公开定义的接口。这反过来又让我可以调用对象的方法并且工作得很好。

针对 .NET 2.0 进行编译时,公开的对象属于 __TransparentProxy 类型。这也可以转换为我的接口,但是当我尝试调用方法时,它会抛出 System.Runtime.Remoting.RemotingException 并显示以下消息:

此远程处理代理没有通道接收器,这意味着服务器没有正在侦听的注册服务器通道,或者此应用程序没有合适的客户端通道来与服务器通信。

当我不从 StandardOleMarshalObject 继承时,它似乎可以工作,但随后我的代码将在任意 RPC 线程上执行,这不是我想要的。

我已经在互联网上搜索过,但无法找到解决方案或无法在 .NET 2.0 中工作的原因。我确实发现了一些类似的问题,但它们似乎都解决了 Excel。

目前我还不能切换到 .NET 4.0,所以我真的希望 .NET 2.0 可以解决这个问题。

有没有人有这个问题的解决方案,或者至少有一个解释?

这是我的测试代码:

[ComVisible(true)][Guid("...")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IService
{
   void Hello();
}

[ComVisible(true)][Guid("...")]
[ClassInterface(ClassInterfaceType.None)]
public class MyService : StandardOleMarshalObject, IService
{
   public void Hello()
   {
      MessageBox.Show("Hello");
   }
}

public class MyAddIn : StandardOleMarshalObject, IDTExtensibility2
{
  public void OnConnection(object application, ext_ConnectMode connectMode, 
     object addInInst, ref Array custom)
  {
        _service = new MyService();
        ((COMAddIn)addInInst).Object = _service;
  }

  //Rest of the IDTExtensibility2 implementation
}

public class Test
{
   public static void Main(string[] args)
   {
      Application app = new Application();
      app.Visible = true;

      COMAddIn addIn = app.COMAddIns.Item("MyAddin");
      IService service = addIn.Object as IService;
      if (service != null)
         service.Hello(); // <-- RemotingException happening here
   }
}

I am writing a (Shared) Word Add-In in C# and want to communicate with it by exposing an object through the Object property of the COMAddIn class.

Because I want my code to be executed on the UI thread I derive my add-in and exposed object from the StandardOleMarshalObject class. This should take care of the marshaling as described here and here.

But by doing this I get different behavior when i compile against .NET 2.0 or.NET 4.0. When compiling against .NET 4.0 my exposed object is of type __ComObject and lets itself be cast to my publicly comvisible defined interface. This in turn lets me call methods on the object and works perfectly.

When compiling against .NET 2.0 the exposed object is of type __TransparentProxy. This can also be cast to my interface but when i try to call a method it wil throw a System.Runtime.Remoting.RemotingException with the message:

This remoting proxy has no channel sink which means either the server has no registered server channels that are listening, or this application has no suitable client channel to talk to the server.

When I do not inherit from StandardOleMarshalObject it does seem to work but then my code would execute on an arbitrary RPC thread which is not what i'm looking for.

I have searched the internet but was not able to find a solution or reason why this is not working in .NET 2.0. I did find some similar problems, but they all seem to address Excel.

At this moment I'm not in the position of switching to .NET 4.0 so i'm really hoping this can be solved for .NET 2.0.

Does anyone have a solution for this problem, or at least an explanation?

Here is my test code :

[ComVisible(true)][Guid("...")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IService
{
   void Hello();
}

[ComVisible(true)][Guid("...")]
[ClassInterface(ClassInterfaceType.None)]
public class MyService : StandardOleMarshalObject, IService
{
   public void Hello()
   {
      MessageBox.Show("Hello");
   }
}

public class MyAddIn : StandardOleMarshalObject, IDTExtensibility2
{
  public void OnConnection(object application, ext_ConnectMode connectMode, 
     object addInInst, ref Array custom)
  {
        _service = new MyService();
        ((COMAddIn)addInInst).Object = _service;
  }

  //Rest of the IDTExtensibility2 implementation
}

public class Test
{
   public static void Main(string[] args)
   {
      Application app = new Application();
      app.Visible = true;

      COMAddIn addIn = app.COMAddIns.Item("MyAddin");
      IService service = addIn.Object as IService;
      if (service != null)
         service.Hello(); // <-- RemotingException happening here
   }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

我三岁 2024-11-13 15:19:25

所以我找到了一个可以接受并且与.NET2.0完美配合的解决方法来解决我的问题。我觉得它并不那么优雅,但它确实有效。我正在使用一个隐藏的小“代理”窗口,它可以让我将从进程外客户端发出的调用编组到 Word 的 UI 线程。我不打算通过 COM 公开许多方法,因此额外的代码行不会成为问题。我在下面添加了重要的代码片段。

    /// <summary>
    /// HiddenForm can be used to marshal calls to the UI thread but is not visible
    /// </summary>
    public class HiddenForm : Form
    {
      public HiddenForm()
      {
       //Making a dummy call to the Handle property will force the native 
       //window handle to be created which is the minimum requirement for 
       //InvokeRequired to work.
       IntPtr hWnd = Handle;
      }
    }

    /// <summary>
    /// AddInService will be exposed through the Object property of the AddIn but does NOT derive 
    /// from StandardOleMarshalObject but instead uses a <see cref="HiddenForm"/> to marshal calls
    /// from an arbitrary RPC thread to the UI thread.
    /// </summary>
    public class AddInService : IAddInService
    {
      private readonly Form _invokeForm;

      public AddInService()
      {
       //create an instance of the HiddenForm which allows to marshal COM
       //calls to the UI thread.
       _invokeForm = new HiddenForm();
      }

      public void HelloOutOfProc()
      {
       if(_invokeForm.InvokeRequired)
       {
         _invokeForm.Invoke(
          new Action<object>(o => HelloOutOfProc()), new object()); //not really elegant yet but Action<> was the only "out of the box" solution that I could find
       }
       else
       {
         MessageBox.Show("HelloOutOfProc on thread id " + Thread.CurrentThread.ManagedThreadId);
       }
      }
    }

    /// <summary>
    /// AddIn Class which DOES derive from StandardOleMarshalObject so it's executed on the UI thread
    /// </summary>
    public class Connect : StandardOleMarshalObject, IDTExtensibility2
    {
      private IAddInService _service;

      public void OnConnection(object application, ext_ConnectMode connectMode,
                   object addInInst, ref Array custom)
      {
       //create service object that will be exposed to out-of-proc processes
       _service = new AddInService();

       //expose AddInService through the COMAddIn.Object property
       ((COMAddIn)addInInst).Object = _service;
      }
    }

在 Window 7、Office 2007 上测试。希望这对其他人有帮助。

我仍然想知道为什么它在 .NET4.0 而不是 .NET2.0 中工作。因此,如果有人对此有答案,我们仍然不胜感激。

So I found a workaround for my problem that is acceptable and works perfectly with .NET2.0. I don't find it as elegant as it could have been, but it works. I'm using a little hidden "proxy" window that lets me marshal calls made from an out-of-proc client to the UI thread of Word. I'm not planning to have many methods exposed through COM so the extra lines of code won't be a problem. I've added the important pieces of code below.

    /// <summary>
    /// HiddenForm can be used to marshal calls to the UI thread but is not visible
    /// </summary>
    public class HiddenForm : Form
    {
      public HiddenForm()
      {
       //Making a dummy call to the Handle property will force the native 
       //window handle to be created which is the minimum requirement for 
       //InvokeRequired to work.
       IntPtr hWnd = Handle;
      }
    }

    /// <summary>
    /// AddInService will be exposed through the Object property of the AddIn but does NOT derive 
    /// from StandardOleMarshalObject but instead uses a <see cref="HiddenForm"/> to marshal calls
    /// from an arbitrary RPC thread to the UI thread.
    /// </summary>
    public class AddInService : IAddInService
    {
      private readonly Form _invokeForm;

      public AddInService()
      {
       //create an instance of the HiddenForm which allows to marshal COM
       //calls to the UI thread.
       _invokeForm = new HiddenForm();
      }

      public void HelloOutOfProc()
      {
       if(_invokeForm.InvokeRequired)
       {
         _invokeForm.Invoke(
          new Action<object>(o => HelloOutOfProc()), new object()); //not really elegant yet but Action<> was the only "out of the box" solution that I could find
       }
       else
       {
         MessageBox.Show("HelloOutOfProc on thread id " + Thread.CurrentThread.ManagedThreadId);
       }
      }
    }

    /// <summary>
    /// AddIn Class which DOES derive from StandardOleMarshalObject so it's executed on the UI thread
    /// </summary>
    public class Connect : StandardOleMarshalObject, IDTExtensibility2
    {
      private IAddInService _service;

      public void OnConnection(object application, ext_ConnectMode connectMode,
                   object addInInst, ref Array custom)
      {
       //create service object that will be exposed to out-of-proc processes
       _service = new AddInService();

       //expose AddInService through the COMAddIn.Object property
       ((COMAddIn)addInInst).Object = _service;
      }
    }

Tested on Window 7, Office 2007. Hope this helps others.

I do still like to know WHY it's working in .NET4.0 and not .NET2.0. So if anyone has a answer to this, it is still appreciated.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文