/* **************************************************************************** * * Copyright (c) Microsoft Corporation. * * This source code is subject to terms and conditions of the Apache License, Version 2.0. A * copy of the license can be found in the License.html file at the root of this distribution. If * you cannot locate the Apache License, Version 2.0, please send an email to * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound * by the terms of the Apache License, Version 2.0. * * You must not remove this notice, or any other, from this software. * * ***************************************************************************/ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Microsoft.VisualStudio; using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; namespace Microsoft.VisualStudio.Project { public class OleServiceProvider : IOleServiceProvider, IDisposable { #region Public Types [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] public delegate object ServiceCreatorCallback(Type serviceType); #endregion #region Private Types private class ServiceData : IDisposable { private Type serviceType; private object instance; private ServiceCreatorCallback creator; private bool shouldDispose; public ServiceData(Type serviceType, object instance, ServiceCreatorCallback callback, bool shouldDispose) { VsUtilities.ArgumentNotNull("serviceType", serviceType); if ((null == instance) && (null == callback)) { throw new ArgumentNullException("instance"); } this.serviceType = serviceType; this.instance = instance; this.creator = callback; this.shouldDispose = shouldDispose; } public object ServiceInstance { get { if (null == instance) { instance = creator(serviceType); System.Diagnostics.Debug.Assert(serviceType != null); } return instance; } } public Guid Guid { get { return serviceType.GUID; } } public void Dispose() { if ((shouldDispose) && (null != instance)) { IDisposable disp = instance as IDisposable; if (null != disp) { disp.Dispose(); } instance = null; } creator = null; GC.SuppressFinalize(this); } } #endregion #region fields private Dictionary services = new Dictionary(); private bool isDisposed; /// /// Defines an object that will be a mutex for this object for synchronizing thread calls. /// private static volatile object Mutex = new object(); #endregion #region ctors public OleServiceProvider() { } #endregion #region IOleServiceProvider Members public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject) { ppvObject = (IntPtr)0; int hr = VSConstants.S_OK; ServiceData serviceInstance = null; if (services != null && services.ContainsKey(guidService)) { serviceInstance = services[guidService]; } if (serviceInstance == null) { return VSConstants.E_NOINTERFACE; } // Now check to see if the user asked for an IID other than // IUnknown. If so, we must do another QI. // if (riid.Equals(NativeMethods.IID_IUnknown)) { object inst = serviceInstance.ServiceInstance; if (inst == null) { return VSConstants.E_NOINTERFACE; } ppvObject = Marshal.GetIUnknownForObject(serviceInstance.ServiceInstance); } else { IntPtr pUnk = IntPtr.Zero; try { pUnk = Marshal.GetIUnknownForObject(serviceInstance.ServiceInstance); hr = Marshal.QueryInterface(pUnk, ref riid, out ppvObject); } finally { if (pUnk != IntPtr.Zero) { Marshal.Release(pUnk); } } } return hr; } #endregion #region Dispose /// /// The IDispose interface Dispose method for disposing the object determinastically. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } #endregion /// /// Adds the given service to the service container. /// /// The type of the service to add. /// An instance of the service. /// true if the Dipose of the service provider is allowed to dispose the sevice instance. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The services created here will be disposed in the Dispose method of this type.")] public void AddService(Type serviceType, object serviceInstance, bool shouldDisposeServiceInstance) { // Create the description of this service. Note that we don't do any validation // of the parameter here because the constructor of ServiceData will do it for us. ServiceData service = new ServiceData(serviceType, serviceInstance, null, shouldDisposeServiceInstance); // Now add the service desctription to the dictionary. AddService(service); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "The services created here will be disposed in the Dispose method of this type.")] public void AddService(Type serviceType, ServiceCreatorCallback callback, bool shouldDisposeServiceInstance) { // Create the description of this service. Note that we don't do any validation // of the parameter here because the constructor of ServiceData will do it for us. ServiceData service = new ServiceData(serviceType, null, callback, shouldDisposeServiceInstance); // Now add the service desctription to the dictionary. AddService(service); } private void AddService(ServiceData data) { // Make sure that the collection of services is created. if (null == services) { services = new Dictionary(); } // Disallow the addition of duplicate services. if (services.ContainsKey(data.Guid)) { throw new InvalidOperationException(); } services.Add(data.Guid, data); } /// /// Removes the given service type from the service container. /// public void RemoveService(Type serviceType) { VsUtilities.ArgumentNotNull("serviceType", serviceType); if (services.ContainsKey(serviceType.GUID)) { services.Remove(serviceType.GUID); } } #region helper methods /// /// The method that does the cleanup. /// /// protected virtual void Dispose(bool disposing) { // Everybody can go here. if (!this.isDisposed) { // Synchronize calls to the Dispose simulteniously. lock (Mutex) { if (disposing) { // Remove all our services if (services != null) { foreach (ServiceData data in services.Values) { data.Dispose(); } services.Clear(); services = null; } } this.isDisposed = true; } } } #endregion } }