// Copyright Andreas Kirsch 2008 & Microsoft (for ideas taken from providelanga
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Shell;
using System.Reflection;
using System.Globalization;
using System.ComponentModel;
using System.Windows.Forms;
using System.Collections;
namespace Microsoft.VisualStudio.Shell
{
///
/// Register the class this attribute is applied to as debug engine.
/// Don't forget that you still need to provide it to Visual Studio as COM object using ProvideObject.
///
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public sealed class ProvideDebugEngineAttribute : RegistrationAttribute
{
private Hashtable optionsTable = new Hashtable();
#region Engine Parameters
/// Set to nonzero to indicate support for address breakpoints.
public bool AddressBP
{
get
{
object val = optionsTable["AddressBP"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["AddressBP"] = value; }
}
/// Set to nonzero in order to always load the debug engine locally.
public bool AlwaysLoadLocal
{
get
{
object val = optionsTable["AlwaysLoadLocal"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["AlwaysLoadLocal"] = value; }
}
/// Set to nonzero to indicate that the debug engine will always be loaded with or by the program being debugged.
public bool LoadedByDebuggee
{
get
{
object val = optionsTable["LoadedByDebuggee"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["LoadedByDebuggee"] = value; }
}
/// Set to nonzero to indicate support for attachment to existing programs.
public bool Attach
{
get
{
object val = optionsTable["Attach"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["Attach"] = value; }
}
/// Set to nonzero to indicate support for call stack breakpoints.
public bool CallStackBP
{
get
{
object val = optionsTable["CallStackBP"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["CallStackBP"] = value; }
}
/// Set to nonzero to indicate support for the setting of conditional breakpoints.
public bool ConditionalBP
{
get
{
object val = optionsTable["ConditionalBP"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["ConditionalBP"] = value; }
}
/// Set to nonzero to indicate support for the setting of breakpoints on changes in data.
public bool DataBP
{
get
{
object val = optionsTable["DataBP"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["DataBP"] = value; }
}
/// Set to nonzero to indicate support for the production of a disassembly listing.
public bool Disassembly
{
get
{
object val = optionsTable["Disassembly"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["Disassembly"] = value; }
}
/// Set to nonzero to indicate support for dump writing (the dumping of memory to an output device).
public bool DumpWriting
{
get
{
object val = optionsTable["DumpWriting"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["DumpWriting"] = value; }
}
/// Set to nonzero to indicate support for exceptions.
public bool Exceptions
{
get
{
object val = optionsTable["Exceptions"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["Exceptions"] = value; }
}
/// Set to nonzero to indicate support for named breakpoints (breakpoints that break when a certain function name is called).
public bool FunctionBP
{
get
{
object val = optionsTable["FunctionBP"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["FunctionBP"] = value; }
}
/// Set to nonzero to indicate support for the setting of "hit point" breakpoints (breakpoints that are triggered only after being hit a certain number of times).
public bool HitCountBP
{
get
{
object val = optionsTable["HitCountBP"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["HitCountBP"] = value; }
}
/// Set to nonzero to indicate support for just-in-time debugging (the debugger is launched when an exception occurs in a running process).
public bool JITDebug
{
get
{
object val = optionsTable["JITDebug"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["JITDebug"] = value; }
}
/// Set this to the CLSID of the port supplier if one is implemented.
public Type PortSupplier
{
get
{
object val = optionsTable["PortSupplier"];
return (null == val) ? null : (Type)val;
}
set { optionsTable["PortSupplier"] = value; }
}
/// Set to nonzero to indicate support for setting the next statement (which skips execution of intermediate statements).
public bool SetNextStatement
{
get
{
object val = optionsTable["SetNextStatement"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["SetNextStatement"] = value; }
}
/// Set to nonzero to indicate support for suspending thread execution.
public bool SuspendThread
{
get
{
object val = optionsTable["SuspendThread"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["SuspendThread"] = value; }
}
/// Set to nonzero to indicate that the user should be notified if there are no symbols.
public bool WarnIfNoSymbols
{
get
{
object val = optionsTable["WarnIfNoSymbols"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["WarnIfNoSymbols"] = value; }
}
/// Set this to the CLSID of the program provider.
public Type ProgramProvider
{
get
{
object val = optionsTable["ProgramProvider"];
return (null == val) ? null : (Type)val;
}
set { optionsTable["ProgramProvider"] = value; }
}
/// Set this to nonzero to indicate that the program provider should always be loaded locally.
public bool AlwaysLoadProgramProviderLocal
{
get
{
object val = optionsTable["AlwaysLoadProgramProviderLocal"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["AlwaysLoadProgramProviderLocal"] = value; }
}
/// Set this to nonzero to indicate that the debug engine will watch for process events instead of the program provider.
public bool EngineCanWatchProcess
{
get
{
object val = optionsTable["EngineCanWatchProcess"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["EngineCanWatchProcess"] = value; }
}
/// Set this to nonzero to indicate support for remote debugging.
public bool RemoteDebugging
{
get
{
object val = optionsTable["RemoteDebugging"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["RemoteDebugging"] = value; }
}
/// Set this to nonzero to indicate that the debug engine should be loaded in the debuggee process under WOW when debugging a 64-bit process; otherwise, the debug engine will be loaded in the Visual Studio process (which is running under WOW64).
public bool LoadUnderWOW64
{
get
{
object val = optionsTable["LoadUnderWOW64"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["LoadUnderWOW64"] = value; }
}
/// Set this to nonzero to indicate that the program provider should be loaded in the debuggee process when debugging a 64-bit process under WOW; otherwise, it will be loaded in the Visual Studio process.
public bool LoadProgramProviderUnderWOW64
{
get
{
object val = optionsTable["LoadProgramProviderUnderWOW64"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["LoadProgramProviderUnderWOW64"] = value; }
}
/// Set this to nonzero to indicate that the process should stop if an unhandled exception is thrown across managed/unmanaged code boundaries.
public bool StopOnExceptionCrossingManagedBoundary
{
get
{
object val = optionsTable["StopOnExceptionCrossingManagedBoundary"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["StopOnExceptionCrossingManagedBoundary"] = value; }
}
/// Set this to a priority for automatic selection of the debug engine (higher values equals higher priority).
public int AutoSelectPriority
{
get
{
object val = optionsTable["AutoSelectPriority"];
return (null == val) ? 0 : (int)val;
}
set { optionsTable["AutoSelectPriority"] = value; }
}
/*
/// Registry key containing entries that specify GUIDs for debug engines to be ignored in automatic selection. These entries are a number (0, 1, 2, and so on) with a GUID expressed as a string.
public Guid[] AutoSelectIncompatibleList
{
get
{
object val = optionsTable["AutoSelectIncompatibleList"];
return val as Guid[];
}
set { optionsTable["AutoSelectIncompatibleList"] = value; }
}
/// Registry key containing entries that specify GUIDs for debug engines that are incompatible with this debug engine.
public Guid[] IncompatibleList
{
get
{
object val = optionsTable["IncompatibleList"];
return val as Guid[];
}
set { optionsTable["IncompatibleList"] = value; }
}*/
/// Set this to nonzero to indicate that just-in-time optimizations (for managed code) should be disabled during debugging.
public bool DisableJITOptimization
{
get
{
object val = optionsTable["DisableJITOptimization"];
return (null == val) ? false : (bool)val;
}
set { optionsTable["DisableJITOptimization"] = value; }
}
#endregion
private string name;
private String engineGuidString;
public string Name
{
get
{
return name;
}
}
public String EngineGuidString
{
get { return engineGuidString; }
}
public string EngineRegKey
{
get { return string.Format(CultureInfo.InvariantCulture, "AD7Metrics\\Engine\\{0}", EngineGuidString); }
}
public ProvideDebugEngineAttribute(String engineGuidString, string name)
{
// make sure that it uses the right GUID format
this.engineGuidString = new Guid(engineGuidString).ToString("B");
this.name = name;
}
private void WriteValue(RegistrationContext context, Key targetKey, string name, object value)
{
if (value == null)
{
return;
}
else if (value is Type)
{
Type type = (Type)value;
Guid guid = type.GUID;
if (guid != Guid.Empty)
{
targetKey.SetValue(name, guid.ToString("B"));
}
}
else if (value is Array)
{
Array array = value as Array;
using (Key childKey = targetKey.CreateSubkey(name))
{
for (int i = 0; i < array.Length; i++)
{
Object element = array.GetValue(i);
WriteValue(context, childKey, i.ToString(), element);
}
}
}
else if (value.GetType().IsPrimitive)
{
targetKey.SetValue(name, Convert.ToInt32(value));
}
else
{
String str = value.ToString();
if (!String.IsNullOrEmpty(str))
{
targetKey.SetValue(name, context.EscapePath(str));
}
}
}
private String[] GetEngineGUIDs(RegistrationContext context, Type attributeType)
{
AProvideEngineInfo[] engines = (AProvideEngineInfo[])context.ComponentType.GetCustomAttributes(attributeType, true);
if (engines.Length == 0)
{
return null;
}
return Array.ConvertAll(engines, delegate(AProvideEngineInfo engine) { return engine.EngineGUID; });
}
public override void Register(RegistrationContext context)
{
context.Log.WriteLine(string.Format(CultureInfo.InvariantCulture, "Registering Debug Engine {0}", EngineGuidString));
using (Key childKey = context.CreateKey(EngineRegKey))
{
//use a friendly description if it exists.
DescriptionAttribute attr = TypeDescriptor.GetAttributes(context.ComponentType)[typeof(DescriptionAttribute)] as DescriptionAttribute;
if (attr != null && !String.IsNullOrEmpty(attr.Description))
{
childKey.SetValue(string.Empty, attr.Description);
}
else
{
childKey.SetValue(string.Empty, context.ComponentType.AssemblyQualifiedName);
}
childKey.SetValue("CLSID", context.ComponentType.GUID.ToString("B"));
childKey.SetValue("Name", Name);
foreach (object key in optionsTable.Keys)
{
string keyName = key.ToString();
WriteValue(context, childKey, keyName, optionsTable[key]);
}
WriteValue(context, childKey, "IncompatibleList", GetEngineGUIDs(context, typeof(ProvideIncompatibleEngineInfo)));
WriteValue(context, childKey, "AutoSelectIncompatibleList", GetEngineGUIDs(context, typeof(ProvideAutoSelectIncompatibleEngineInfo)));
}
}
public override void Unregister(RegistrationAttribute.RegistrationContext context)
{
context.RemoveKey(EngineRegKey);
}
}
}