我正在C#中编写(共享)Word加载项,并希望通过COMAddIn类的Object属性公开对象来与之通信。
因为我希望我的代码在UI线程上执行,所以我从StandardOleMarshalObject类派生了我的加载项和公开对象。这应该照顾here和here所述的编组。
但是通过这样做,当我针对.NET 2.0或.NET 4.0进行编译时,我得到了不同的行为。在针对.NET 4.0进行编译时,我的公开对象的类型为__ComObject,并且可以将其自身强制转换为我公开可见的已定义接口。这反过来让我可以调用对象上的方法并完美地工作。
在针对.NET 2.0进行编译时,公开的对象的类型为__TransparentProxy。这也可以转换为我的界面,但是当我尝试调用一个方法时,它会抛出一个带有消息的System.Runtime.Remoting.RemotingException:
此远程处理代理没有通道接收器,这意味着服务器没有正在侦听的已注册服务器通道,或者此应用程序没有合适的客户端通道与服务器通信。
当我不从StandardOleMarshalObject继承它似乎工作但我的代码将在任意RPC线程上执行,这不是我正在寻找的。 p>
我搜索了互联网,但无法找到解决方案或原因,因为这不适用于.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
}
}
答案 0 :(得分:1)
所以我找到了一个可接受的问题的解决方法,并且与.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;
}
}
在Office 7上测试Office 2007.希望这有助于其他人。
我仍然想知道为什么它在.NET4.0而不是.NET2.0中运行。所以,如果有人对此有答案,我们仍然感激不尽。