参考:Migrating a Shared Add-in to a Visual Studio Tools for Office Add-In
http://msdn.microsoft.com/zh-cn/library/aa663367(en-us).aspx
我的小结:
1.外接共享插件可以通过事件,将插件类对象赋值给addInInstance.Object ,这样外部调用程序,可以读取
COMAddIn addIn = wordApp.COMAddIns.Item(ref oI);
addIn.Object,从而进行交互控制。
public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom)
{
wordApp = (Word.Application)application;
addInInstance = (Office.COMAddIn)addInInst;
//提供一个外部程序调用插件方法的对象引用,晚绑定调用方法
//外部程序通过COMAddIn.Object远程访问此插件程序的对象
addInInstance.Object = this;
}
VSTO SE 则是通过
protected override object RequestComAddInAutomationService()
{
if (utilities == null)
utilities = new AddInUtilities();
return utilities;
}
方法,将返回值给COMAddIn.Object . 也实现了同样的功能.注意一定要设置[System.Runtime.InteropServices.ComVisibleAttribute(true)]
参考:
AddIn.RequestComAddInAutomationService Method
How to cast COMAddIn.Object in C#?
http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/4ca46a99-9d48-4ba1-af3a-8d5c0c18f99e/
First, lets see what is actuall happening:
In Excel.exe process:
Addin Obj (managed) ---> Com Callable Wrapper created (CLR) ---> Excel recieves pointer to the CCW
In external app (automating Excel):
External App (managed) reads Addin object from Excel <---- Runtime Callable Wrapper created (CLR) <--- Excel returns CCW
As far as the instance of CLR running in the external app is concerned, it recieved a pointer to a COM interface (actually proxy). It can not cast a COM object to a managed object because it has no idea what the object in the other process (Excel.exe) is.
In fact if you tried your exact same code from another addin running in-proc (i.e within Excel.exe) it would work. In this case CLR always knows the underlying type of the object and is able to perform a legal cast.
Solutions:
1. You can make late-bound calls on the COM object using reflection. This is actually the reason why VBA code works. VBA runtime always makes late bound calls using IDispatch interface based on the code you typed (remember it's interpreted not compiled).
// Making late-bound call on object. We lose out on compile time checking
object [] invokeArgs = {4};
object retVal = addInObj.GetType().InvokeMember( "SomeMethod" ,
System.Reflection. BindingFlags .InvokeMethod, null , addInObj, invokeArgs);
Console .WriteLine(retVal.ToString()); // output: "4"
2. Declare an interface in your addin and only pass the interface "pointer" around. In this case CLR will make the late-bound calls for you based on the interface definition.
In your addin:
IExposedClass expClsPtr = null;
public void OnConnection(object application, Extensibility.ext_ConnectMode connectMode, object addInInst, ref System.Array custom) {
expClsPtr = new ExposedClass();
Office.COMAddIn addin = (Office.COMAddIn)addInInst;
addin.Object = expClsPtr;
}
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
[System.Runtime.InteropServices.InterfaceType(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIDispatch)]
public interface IExposedClass {
string SomeMethod(int r);
}
[System.Runtime.InteropServices.ComVisibleAttribute(true)]
[System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.None)]
public class ExposedClass : IExposedClass {
public string SomeMethod(int r) { return r.ToString(); }
}
In external app (automating Excel):
object addInObj = isentrisAddIn.Object;
MyExcelAddin1.IExposedClass addInExposedAgain = (MyExcelAddin1.IExposedClass)addInObj;
string temp = addInExposedAgain.SomeMethod(4); // Call a method on addin object
上面提供的方法,第一种回调方式,我OK,但是第二种方式,我失败了,会出错!
2、VSTOSE插件是托管的,所以插件的异常可以被WORD程序捕获,并显示,而共享插件出现异常时,无法获取到。所以VSTOSE插件更易于维护。