Q:
I’m using ShapeAppBasicCSharp sample.
I'm pretty clear on using Adapters to pass types that are not in VSTA proxy. Not clear on how to mix a proxied type with a custom adapter type.
In this sample,I’ll start simply by passing the current Document as IElement through custom adapter:
Add new type with DocElement property off Application.
public partial class Application . . .
public ShapeAppCSharp.Contracts.IElementContract DocElement
{
get
{
return this.document as ShapeAppCSharp.Contracts.IElementContract;
}
}
Document implements IElementContract
public sealed class Document : ShapeAppCSharp.Contracts.IElementContract //ShapeAppCSharp.IElement
{
. . .
#region IElementContract Members
private string elementID;
public string ElementID
{
get
{
if (String.IsNullOrEmpty(elementID))
{
Guid guid = Guid.NewGuid();
elementID = guid.ToString();
}
return elementID;
}
}
#endregion
Add-In calls API:
private void AppAddIn_Startup(object sender, EventArgs e)
{
//serializationexception -- Document does not serialize, add ShapeAppCSharp.IElement to ShapeAppCSharpProxy?
ShapeAppCSharp.Contracts.IElementContract docElement = this.DocElement;
string id = DocElement.ElementID;
// normal API
Document doc = this.Document;
id = doc.ElementID;
}
Type is expected to resolve, but throws serialization exception (Document as ObjectToPack does not serialize).
This occurs after assigning e.ObjectToPack (Document instance) to e.AdapterToReturn as ShapeAppCSharp.Contracts.IElementContract:
in ShapeAppCSharp.HostSideAdapters
[System.AddIn.Pipeline.HostAdapterAttribute()]
public class IShapeAppEntryPointContractToViewHostAdapter
void TypeInfrastructureManager_AdapterResolve(object sender, AdapterResolveEventArgs e)
{
if (typeof(ShapeAppCSharp.Contracts.IElementContract).IsAssignableFrom(e.ExpectedType))
{
e.AdapterToReturn = e.ObjectToPack as ShapeAppCSharp.Contracts.IElementContract;
}
}
Any suggestions?
A:
It looks like public sealed class Document is effectively acting as an adapter in this case in that case for contract to be marshalable across the boundary you need to have the adapter type derive from MBRO.
Q:
Yes, that helped shed some light.
I was confused by the proxied Document class def, (which is MBRO):
public abstract partial class Document : global::System.MarshalByRefObject
Now The Document's IContract impl is called:
public System.AddIn.Contract.IContract QueryContract(string contractIdentifier)
{
return this;
}
But, on the way out of proxy's ApplicationEntryPoint Document property
public partial class ApplicationEntryPoint : global::ShapeAppCSharp.IShapeAppEntryPoint
public virtual global::Microsoft.VisualStudio.Tools.Applications.Samples.ShapeApp.Document Document
{
get { return ((global::Microsoft.VisualStudio.Tools.Applications.Samples.ShapeApp.Application)(remoteObject)).Document; }
}
I hit a new exception:
System.InvalidCastException {"Unable to cast transparent proxy to type 'Microsoft.VisualStudio.Tools.Applications.Contract.IInteropObjectContract2'."}
A:
Return of null should be done in the QueryContract instead of returning this for every contract identifier. In this case when trying to get object from contract the runtime checks if it implements IInteropObjectContract2 it gets a not null value assuming it got an implementation it tries the cast.
So only return a not null value if you recognize the contractIdentifier. Also in this case it would be better if you create a new adapter otherwise you would have versioning issues with document type that is acting as a adapter now. In the new adapter you could derive it from ContractBase and that should take care of QueryContract requests. Also this you would refrain the object model of knowing contract details.
Posted
May 22 2009, 02:40 PM
by
BillL