Calling a method on Object exposed by Word Add-in throws RemotingException
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 开发者_如何学Goleast 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
}
}
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.
精彩评论