Вызов метода для объекта, предоставляемого надстройкой Word, вызывает исключение RemotingException - PullRequest
0 голосов
/ 11 мая 2011

Я пишу (совместно используемую) надстройку Word на C # и хочу связаться с ней, выставив объект через свойство Object класса COMAddIn.

Поскольку я хочу, чтобы мой код выполнялся в потоке пользовательского интерфейса, я извлекаю свою надстройку и открытый объект из класса 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
   }
}

1 Ответ

1 голос
/ 12 июня 2011

Итак, я нашел обходной путь для моей проблемы, который приемлем и отлично работает с .NET2.0.Я не нахожу это так элегантно, как могло бы быть, но это работает.Я использую небольшое скрытое окно «прокси», которое позволяет мне маршалировать звонки, сделанные из вне-клиентского клиента, в поток пользовательского интерфейса Word.Я не планирую раскрывать многие методы через 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;
      }
    }

Протестировано в Windows 7, Office 2007. Надеюсь, это поможет другим.

Мне все еще нравится знать, ПОЧЕМУ он работает.NET4.0, а не .NET2.0.Поэтому, если у кого-то есть ответ на этот вопрос, он все равно будет оценен.

...