How to: Enable Non-Destructive Debugging for Add-Ins
The following example shows an implementation of Visual Studio Tools for Applications IDE integration and basic non-destructive debugging that opens a new instance of the host application. It is based on the ShapeApp sample application. The concepts behind this example are described in detail in Add-In Debugging.
There is another example that demonstrates Visual Studio Tools for Applications IDE integration and advanced non-destructive debugging in a running instance of the host application. For more information, see How to: Build and Run the ShapeAppMacroRecordingCSharp Sample.
Example
using System;
using System.Text;
using System.Globalization;
using System.Runtime.Remoting;
using Microsoft.VisualStudio.Tools.Applications;
using Microsoft.VisualStudio.Tools.Applications.DesignTime;
using Microsoft.VisualStudio.Tools.Applications.Runtime;
using Microsoft.VisualStudio.Tools.Applications.Hosting;
using System.AddIn;
using System.AddIn.Hosting;
using System.ComponentModel.Design;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Collections;
using VSTADTEProvider.Interop;
using System.Threading;
namespace AIMExtension
{
[System.Runtime.InteropServices.Guid("80A50594-7B6A-4840-B055-1322E487B2B0")]
[System.Runtime.InteropServices.ComVisible(true)]
public class AIMExtension : System.MarshalByRefObject,
ShapeApp.IExtension,
IExternalDebugHost
{
#region Constructors, Destructors
public AIMExtension()
{
addInList = new List<IEntryPoint>();
}
// Define constructors and destructors.
#endregion
#region MarshalByRefObject
public override object InitializeLifetimeService()
{
// Allow remoting from Visual Studio.
return null; }
#endregion MarshalByRefObject
#region IExtension Members
void ShapeApp.IExtension.Connect(
ShapeApp.Application pApplication)
{
// Set the ShapeApp instance.
this.shapeappApp = pApplication;
// Create the service provider.
InstantiateServiceProvider();
string hostDebugReadyEventName = "";
string hostDebugUri = "";
// Load the add-in into an external process.
// Decide whether this is a non-destructive debugging case.
for (int i = 1; i < args.Length; ++i)
{
if (args[i].StartsWith(exAddInPrefix))
{
string assemblyName = args[i].Remove(0,
exAddInPrefix.Length);
string[] addInInfoPartial =
assemblyName.Split(',');
LoadAddInFromDirectory(serviceProvider,
addInInfoPartial[0],
addInInfoPartial[1]);
}
else if (args[i].StartsWith(addInPathPrefix))
{
this.addInPath = args[i].Remove(0,
addInPathPrefix.Length);
}
else if (args[i].StartsWith(vstaHostDebugReadyPrefix))
{
hostDebugReadyEventName = args[i].Remove(0,
vstaHostDebugReadyPrefix.Length);
}
else if (args[i].StartsWith(vstaHostDebugUriPrefix))
{
hostDebugUri = args[i].Remove(0,
vstaHostDebugUriPrefix.Length);
}
}
// Enable non-destructive debugging.
if (!((String.IsNullOrEmpty(hostDebugUri) ||
String.IsNullOrEmpty(hostDebugReadyEventName))))
{
isNDD = true;
ExternalDebugging.RegisterExternalDebugHost(
(IExternalDebugHost)this,
new Uri(hostDebugUri));
}
EventWaitHandle readyEvent = new EventWaitHandle(false,
EventResetMode.ManualReset,
hostDebugReadyEventName);
readyEvent.Set();
}
void ShapeApp.IExtension.Disconnect()
{
if (addInProcess != null)
{
foreach (IEntryPoint ep in this.addInList)
{
ep.OnShutdown();
}
addInList.Clear();
addInProcess.Shutdown();
addInProcess = null;
}
}
// IExtension members.
#endregion
#region IExternalDebugHost
public int OnBeforeDebugStarting()
{
CreateAddInProcess();
int addinProcessID = addInProcess.ProcessId;
isDebugging = true;
return addinProcessID;
}
public void OnDebugStarting()
{
LoadAddInFromDirectory(this.serviceProvider,
this.addInPath);
}
public void OnDebugStopping()
{
if (isDebugging)
{
StopDebugging();
isDebugging = false;
}
}
#endregion //IExternalDebugHost
#region Private Methods
private void InstantiateServiceProvider()
{
itemProvider = new HostItemProvider(shapeappApp);
typeMapProvider = new HostTypeMapProvider();
ServiceContainer container = new ServiceContainer();
container.AddService(typeof(IHostItemProvider),
itemProvider);
container.AddService(typeof(ITypeMapProvider),
typeMapProvider);
serviceProvider = container;
}
private void AddInProcessExiting(object sender,
System.ComponentModel.CancelEventArgs args)
{
if (isNDD)
{
// Clean up before shutting down.
System.Environment.Exit(0);
}
}
private AddInToken CustomFindAddIn(string addInPath,string className)
{
Collection<AddInToken> token = AddInStore.FindAddIn(typeof(IEntryPoint), AddInStoreExtensions.DefaultPipelinePath, addInPath,className);
return token[0];
}
private AddInToken CustomFindAddIn(string addInPath)
{
if (!System.IO.File.Exists(addInPath))
throw new ArgumentException();
string addInDir = System.IO.Path.GetDirectoryName(addInPath);
string addInRoot = null;
addInRoot = System.IO.Path.Combine(addInDir, "..");
AddInStore.UpdateAddIns(addInRoot);
Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(IEntryPoint), AddInStoreExtensions.DefaultPipelinePath, addInRoot);
Collection<AddInToken> addinTokens = null;
foreach (AddInToken token in tokens)
{
addinTokens = AddInStore.FindAddIn(typeof(IEntryPoint), AddInStoreExtensions.DefaultPipelinePath, addInPath, token.AddInFullName);
if (addinTokens.Count > 0)
return addinTokens[0];
}
return null;
}
private void CreateAddInProcess()
{
// Create the process and start it.
this.addInProcess = new AddInProcess();
this.addInProcess.Start();
// Create the event handlers.
addInProcess.ShuttingDown += new EventHandler<System.ComponentModel.CancelEventArgs>(AddInProcessExiting);
}
private void LoadAddInFromDirectory(IServiceProvider
serviceProvider, string addInPath,string className)
{
AddInToken addinToken = CustomFindAddIn(addInPath,
className);
LoadToken(addinToken);
}
private void LoadAddInFromDirectory(IServiceProvider
serviceProvider, string addInPath)
{
AddInToken addinToken = CustomFindAddIn(addInPath);
LoadToken(addinToken);
}
private void LoadToken(AddInToken addinToken)
{
IEntryPoint addin = null;
if (this.addInProcess == null)
{
CreateAddInProcess();
}
// Host the add-ins in the external process.
// Activate the add-in.
addin = addinToken.Activate<IEntryPoint>(this.addInProcess, AddInSecurityLevel.FullTrust);
addin.Initialize(serviceProvider);
addin.InitializeDataBindings();
addin.FinishInitialization();
addInList.Add(addin);
}
private void AddInProcessExited(object sender, EventArgs args)
{
addInProcess = null;
}
private void StopDebugging()
{
if (!this.isDebugging)
return;
if (addInProcess != null)
{
addInProcess.Shutdown();
}
this.isDebugging = false;
}
private void ForcingUnloadCurrentAddins()
{
foreach (IEntryPoint inProcAddin in addInList)
{
if (inProcAddin != null)
{
AddInController controller =
AddInController.GetAddInController(inProcAddin);
controller.Shutdown();
}
addInList.Clear();
}
}
#endregion //Private Methods
#region Private Fields
public delegate void DelegateToUnloadAddIns();
private AddInProcess addInProcess;
private const string exAddInPrefix = "/exAddIn:";
private const string addInPathPrefix = "/addInPath:";
private const string vstaHostDebugUriPrefix = "/vstaHostDebugUri:";
private const string vstaHostDebugReadyPrefix = "/vstaHostDebugReady:";
private IServiceProvider serviceProvider;
private IHostItemProvider itemProvider;
private ITypeMapProvider typeMapProvider;
private ShapeApp.Application shapeappApp;
private List<IEntryPoint> addInList;
private string addInPath;
private bool isDebugging = false;
private bool isNDD = true;
#endregion //Private Fields
#region PInvoke
[DllImport("ole32.dll")]
public static extern int GetRunningObjectTable(int reserved,
out IRunningObjectTable prot);
[DllImport("ole32.dll")]
public static extern int CreateBindCtx(int reserved,
out IBindCtx ppbc);
#endregion
}
#region DTE
[System.Runtime.InteropServices.ComImport]
[System.Runtime.InteropServices.Guid("BA018599-1DB3-44f9-83B4-461454C84BF8")]
public class DTE
{
}
#endregion //DTE
public class HostItemProvider : IHostItemProvider
{
#region Constructors, Destructors
public HostItemProvider(ShapeApp.Application application)
{
this.application = application;
}
#endregion //Constructors, Destructors
#region IHostItemProvider Members
public object GetHostObject(Type primaryType, string primaryCookie)
{
if (primaryType == typeof(ShapeApp.Application))
{
return this.application;
}
else
{
throw new ArgumentOutOfRangeException();
}
}
#endregion //IHostItemProvider Members
#region Private Fields
private ShapeApp.Application application;
#endregion //Private Fields
}
public class HostTypeMapProvider : ITypeMapProvider
{
public HostTypeMapProvider()
{
}
// Get the type name that corresponds to
// the canonical name from the host-provided maps.
public Type GetTypeForCanonicalName(string canonicalName)
{
if (canonicalName == "ShapeApp, ShapeApp.Application")
{
return typeof(ShapeApp.Application);
}
if (canonicalName == "ShapeApp,
ShapeApp.IApplicationEvents")
{
return typeof(ShapeApp.IApplicationEvents);
}
return null;
}
// Query the canonical name.
public string GetCanonicalNameForType(Type type)
{
return null;
}
}
}
See Also
Tasks
Walkthrough: Incorporating the IDE for a Managed Object Model
Concepts
Incorporating the Integrated Development Environment