By JohnHMcCauley in VSTA Tech Discussions
I have an Add-In that defines and implements a new class (extending the available object model dynamically) that another Add-In or the Host can make calls to.
This is possible with MAF by implementing a two-way pipeline.
Is this possible using VSTA?
By Melody in VSTA Tech Discussions
John,
No, with VSTA the object model is represented through a proxy class which is not dynamic. All types must be pre-defined.
Hope this helps, please let me know if you have any questions.
Thanks!
By JohnHMcCauley in VSTA Tech Discussions
Sorry I should have been clearer.
The type is pre-defined by the Add-In. If the Add-In is loaded then another Add-In or Host should be able to make use of it.
By Gary in VSTA Tech Discussions
Starting with your first question:
>>I have an Add-In that defines and implements a new class (extending the available object model dynamically) that another Add-In or the Host can make calls to.
This is possible with MAF by implementing a two-way pipeline.
Is this possible using VSTA?
>>
There are two ways that I know of to accomplish this:
1. Starting with the IExtendedEntryPoint.GetEntryPointObject and Type t.InvokeMember() as demonstrated, in the ShapeAppCSharpMacroRecording sample:
internal void ExecuteMacroInternal(string macroName)
{
// If no Macros loaded
if (this.application.VstaRuntimeIntegration.MacroAddIn == null)
return;
IExtendedEntryPoint entryPoint = this.application.VstaRuntimeIntegration.MacroAddIn as IExtendedEntryPoint;
System.Diagnostics.Debug.Assert(entryPoint != null);
if (entryPoint == null)
return;
object obj = entryPoint.GetEntryPointObject();
Type t = obj.GetType();
try
{
t.InvokeMember(macroName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, obj, null);
return;
}
catch (System.Runtime.Remoting.RemotingException ex)
{
// Swallow remoting exception.
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
catch (Exception e)
{
System.Windows.Forms.MessageBox.Show("Error executing macro\n" + e.Message, "ShapeAppCSharp", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
// Didn't find a method to invoke, so show a message box.
System.Windows.Forms.MessageBox.Show("Unable to execute macro: " + macroName, "ShapeAppCSharp", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
And adding a method in the addin that allows passing objects, we get a little further:
{
...host side code ie: CreatePart method can return System.Object objPart . . .
object obj = entryPoint.GetEntryPointObject();
Type t = obj.GetType();
MethodInfo objMethodInfo = t.GetMethod("CreatePart");
if ((objMethodInfo == null) || (objMethodInfo.GetParameters().Length > 0))
return;
try
{
System.Object objPart = objMethodInfo.Invoke(remoteObject, null);
classType = objPart.GetType();
PropertyInfo objPropertyInfo = classType.GetProperty("Label");
if (objPropertyInfo == null)
return;
System.Object objLabel = objPropertyInfo.GetValue(objPart,null);
MessageBox.Show(objLabel.ToString());
objMethodInfo = objPropertyInfo.GetGetMethod(true);
objLabel = objMethodInfo.Invoke(objPart, null);
MessageBox.Show(objLabel.ToString());
}
catch (System.Runtime.Remoting.RemotingException)
{
return;
}
catch (Exception ex)
{
MessageBox.Show(String.Format("CreatePart Invocation failed: {0}", ex.Message), "Invocation Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
==
Macro Code:
==
using System;
using System.Windows.Forms;
namespace ShapeAppMacros.csproj
{
public partial class AppAddIn
{
public void AppAddIn_Startup(object sender, EventArgs e)
{
}
public void AppAddIn_Shutdown(object sender, EventArgs e)
{
}
public PartFamily CreatePart()
{
PartFamily part = new PartFamily();
MessageBox.Show(part.Label);
return new PartFamily();
}
#region ShapeAppCSharp Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(AppAddIn_Startup);
this.Shutdown += new System.EventHandler(AppAddIn_Shutdown);
}
#endregion
}
public class PartFamily
{
public PartFamily()
{
PartFamilyLabel = "2345";
MessageBox.Show("2345");
}
string PartFamilyLabel = "1234";
public string Label
{
get
{
return PartFamilyLabel;
}
set
{
PartFamilyLabel = value;
}
}
}
}
...The second way is more complicated. I have not tried this, but it should be feasible:
2. create a proxy assembly layer for the add-in's OM (ie: use proxygen) and reference the add-in OM proxy, for type info, from the host; implement the IEntryPoint stuff in a host-side class; host gets a remoteObject instance of the add-in after the add-in activates an assembly in the host-domain (also via a VSTA pipeline); host automates add-in
hope this helps. let us know how you fare.