Welcome to MSDN Blogs Sign in | Join | Help

Exposing a service through an Add-In

In the previous post we did a walkthrough on how to do simple runtime integration with VSTA 2.0. In this post we would see if how the sample can be modified to support a scenario where an Add-In exposes a service and another Add-In consumes it. I modified our simple OM in the previous blog to add two methods one that accepts new service registrations and other that returns the service when queried by the caller (In this case our Add-In). How these methods are implemented and exposed are just host specific details and we won’t concern ourselves with these details as of now.

    //this is simple class that will interact with addins.

    //different addins will use this provider to query for services!

    public class ServiceProvider

    {

      

       private Dictionary<string, object> _registeredServices = new Dictionary<stringobject>();

 

        public object GetService(string ServiceName)

        {

            if (ServiceName == "TemperatureService")

            {

                return new TemperatureService();

            }

 

            if (ServiceName == "HumidityService")

            {

                return new HumidityService();

            }

 

            return null;

        }

 

        //Added methods

        public void RegisterService(string serviceIdentifier, object serviceObject)

        {

            if(!_registeredServices.ContainsKey(serviceIdentifier))

            {

                _registeredServices.Add(serviceIdentifier, serviceObject);

            }

        }

 

        public object GetRegisteredService(string serviceIdentifier)

        {

            return _registeredServices[serviceIdentifier];

        }

 

    }

As we have changes our OM we will have to regenerate the proxy dll by following the steps mentioned in the previous post. After we have the new proxy make sure your previous add-in works as expected as a sanity check for the changes made.

Now let’s create a service object for our add-in. Here I modified the Add-In to add a class that will act as a service type here and would be exposed by the add-in

public class AddIn:ServiceProviderEntryPoint

    {

        protected override void FinishInitialization()

        {

            TemperatureService tempServ = (TemperatureService)this.GetService("TemperatureService");

            Console.WriteLine(tempServ.Temperature.ToString());

            HumidityService humidServ = (HumidityService)this.GetService("HumidityService");

            Console.WriteLine(humidServ.Humidity.ToString());

            //Service registration

            AddInService service = new AddInService();

            this.RegisterService("MYADDINSERVICE", service);

           

        }

    }

 

    //Service Class

    public class AddInService

    {

        public void HandShake()

        {

            Console.WriteLine("Hello World");

        }

    }

 

We now have an add-in that creates a service object and gets registered with the host. Let’s create another Add-In that will consume this service!

Create a second add-in as the first one referencing proxy and Microsoft.VisualStudio.Tools.Applications.Runtime.v9.0.dl as did in the first add-in. Add the following code as a consumer of the service in the add-in.

//Second Add-In 

namespace App

{

    public class AddIn2:ServiceProviderEntryPoint

    {

        protected override void FinishInitialization()

        {

            object serviceObject = this.GetRegisteredService("MYADDINSERVICE");

            MethodInfo info =

            serviceObject.GetType().GetMethod("HandShake", BindingFlags.Instance |

                                                           BindingFlags.InvokeMethod |   

                                                           BindingFlags.Public);

            info.Invoke(serviceObject, null);

        }

    }

 

}

Lets add the integration code to load this second add-in now in the integration exe that we had.

class Program

    {

        static IServiceProvider serviceProvider = null;

        private const string vstaPipelinePath = @"Microsoft Shared\VSTA\Pipeline";

 

        static void Main(string[] args)

        {

            InstantiateServiceProvider();

            //this invokes the deriver and binds the driver to the listeners

            string addinPath = @"C:\App\AddIn\bin\Output\AddIn.dll";

            string commonPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.CommonProgramFiles);

            string pipelinePath = System.IO.Path.Combine(commonPath, vstaPipelinePath);

            Type havType = typeof(IEntryPoint);                                      

            Collection<AddInToken> addins = AddInStore.FindAddIn(havType,  pipelinePath, addinPath, "App.AddIn");

 

            if (addins.Count > 0)

            {

                IEntryPoint addin = addins[0].Activate<IEntryPoint>(AddInSecurityLevel.FullTrust);

                addin.Initialize(serviceProvider);

                addin.InitializeDataBindings();

                addin.FinishInitialization();

            }

            else

            {

             Console.WriteLine("Add In could not be discovered");

            }

 

            //Second Add-In loaded below

                     addinPath = @" C:\App\AddIn\bin\Output\AddIn2.dll ";

            addins = AddInStore.FindAddIn(havType, pipelinePath, addinPath, "App.AddIn2");

            if (addins.Count > 0)

            {

                IEntryPoint addin = addins[0].Activate<IEntryPoint>(AddInSecurityLevel.FullTrust);

                addin.Initialize(serviceProvider);

                addin.InitializeDataBindings();

                addin.FinishInitialization();

            }

            else

            {

                Console.WriteLine("Add In could not be discovered");

            }

        }

 

        static private void InstantiateServiceProvider()

        {

            if (serviceProvider == null)

            {

                ServiceProvider provider = new ServiceProvider();

                IHostItemProvider itemProvider = new HostItemProvider(provider);

                ITypeMapProvider typeMapProvider = new HostTypeMapProvider();

                ServiceContainer container = new ServiceContainer();

                container.AddService(typeof(IHostItemProvider), itemProvider);

                container.AddService(typeof(ITypeMapProvider), typeMapProvider);

                serviceProvider = container;

            }

        }

    }

 

Running the exe and setting the breakpoint in the second add-in would show that the second add-in is able to invoke the method on the service object.

In the sample here we have worked late bound with the service object using reflection to invoke the method. Let’s now see how we can achieve early bound semantics by exposing an interface to both the add-ins.

Create a separate interface assembly that can be referred by both the add-ins and add the service interface that exposes the appropriate service method here (you will need to refer Microsoft.VisualStudio.Tools.Applications.Runtime.v9.0.dll)

You will have to add a host type attribute to the interface type added and host assembly attribute to the interface assembly as below. Later we would discuss in subsequent posts how these declarative attributes provide a mapping mechanism to isolate types between hosts and add-ins.

[assembly: global::Microsoft.VisualStudio.Tools.Applications.Runtime.HostAssemblyAttribute()]

 

namespace ServiceInterfaces

{

    [global::Microsoft.VisualStudio.Tools.Applications.Runtime.HostTypeAttribute("App, App.IService")]

    public interface IService

    {

        void HandShake();

    }

}

Refer the assembly in both the add-ins and make the following changes in add-ins

First Add-In changed as below

public class AddInService:IService

    {

        public void HandShake()

        {

            Console.WriteLine("Hello World");

        }

    }

 

Second Add-In Changed as below

object serviceObject = this.GetRegisteredService("MYADDINSERVICE");

MethodInfo info = serviceObject.GetType().GetMethod("HandShake", BindingFlags.Instance | BindingFlags.InvokeMethod |     BindingFlags.Public);

info.Invoke(serviceObject, null);

//Use the interface to call the method

((IService)(serviceObject)).HandShake();

 

Now we are able to cast to the interface and make the call directly rather than invoing it through MethodInfo object.

 

 

Published Saturday, December 06, 2008 12:06 PM by Sandeep Bhatia

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

No Comments

Leave a Comment

(required) 
required 
(required) 

  
Enter Code Here: Required
 
Page view tracker