microsoft / VSProjectSystem

Documentation for extending Visual Studio with new types of projects.
Other
313 stars 87 forks source link

Custom Debugger extension for C++ #343

Open golf89 opened 3 years ago

golf89 commented 3 years ago

I am trying to Custom Debugger extension for my own C++ project, i have tried step mentioned at https://github.com/microsoft/VSProjectSystem/blob/master/doc/extensibility/IDebugLaunchProvider.md by creating XAML file and extending DebugLaunchProviderBase and providing enum value to be displayed.

But as soon as i load the template i see 4 default options (local windows debugger, remove windows debugger, web service debugger, web browser debugger).

even if i try to run the project i don't get call into my own Implementation of LaunchAsync in DebugLaunchProviderBase child class.

Can anyone suggest what i am missing?

I have applied the project capability to all the classes in sample extension and to vcxproj also.

to summarise

I am trying to create my own debugger implementation for c++ projects so that I can get my custom label on debug button and can handle the debugging/launch of the program.

Thanks in Advance

jobmarley commented 2 years ago

Hey,

that's a bit late, but I ran into the same kind of issues. And after quite a bit of investigation I was able to understand what's going on. So first, the c++ project system doesn't load the debugger extension on project load (I don't even know if it happens with other project types). If you want your extension to be loaded, you can use [Export(ExportContractNames.Scopes.ConfiguredProject, typeof(IProjectDynamicLoadComponent))] If you do that you will see that the LoadAsync/UnloadAsync are called if you have the right capability (you can try with "VisualC").

Even though that's unintuitive, the list of available debuggers is not defined by the exported MEF debuggers, but by the rules registered for the project. When you click the debug button, the exported IDebugLaunchProvider are enumerated and the one with a matching name is instanciated and CanLaunchAsync, etc... are called. Now the issue is the documentation suggest that we use ExportPropertyXamlRuleDefinition, but I wasn't able to make that work at all (in vs 2022). The best way in my opinion is to use IProjectDynamicLoadComponent. Then import a IAdditionalRuleDefinitionsService in the constructor, and add the rule manually. Then your debugger will appears in the drop down menu.

[Export(ExportContractNames.Scopes.ConfiguredProject, typeof(IProjectDynamicLoadComponent))]
[ExportDebugger("MyDebugger")]
[AppliesTo("VisualC")]
public class MyDebugLaunchProvider
    : DebugLaunchProviderBase,
    IProjectDynamicLoadComponent
{
    private ConfiguredProject m_configuredProject = null;

    [ImportMany(ExportContractNames.VsTypes.IVsHierarchy)]
    private OrderPrecedenceImportCollection<IVsHierarchy> IVsHierarchies { get; set; }

    [ImportingConstructor]
    public MyDebugLaunchProvider(ConfiguredProject configuredProject, IAdditionalRuleDefinitionsService rds)
        : base(configuredProject)
    {
        IVsHierarchies = new OrderPrecedenceImportCollection<IVsHierarchy>(projectCapabilityCheckProvider: configuredProject.UnconfiguredProject);
        m_configuredProject = configuredProject;
        Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("XamlRuleToCode:MyDebugger.xaml");
        Rule rule = ((IProjectSchemaNode)XamlServices.Load(stream)).GetSchemaObjects(typeof(Rule)).Cast<Rule>().FirstOrDefault();
        rds.AddRuleDefinition(rule, "Project");
    }

    public override Task<bool> CanLaunchAsync(DebugLaunchOptions launchOptions)
    {
        // perform any necessary logic to determine if the debugger can launch
        return Task.FromResult(true);
    }

    public override async Task<IReadOnlyList<IDebugLaunchSettings>> QueryDebugTargetsAsync(DebugLaunchOptions launchOptions)
    {
        var settings = new DebugLaunchSettings(launchOptions);

        settings.LaunchDebugEngineGuid = DebuggerEngines.NativeOnlyEngine;

        settings.AppPackageLaunchInfo = new VsAppPackageLaunchInfo();
        settings.Arguments = "";
        settings.CurrentDirectory = "<exedir>";
        settings.Executable = "<exepath>.exe";
        settings.LaunchOperation = DebugLaunchOperation.CreateProcess;
        settings.LaunchOptions = launchOptions;
        settings.Options = "";
        settings.PortName = "";
        settings.PortSupplierGuid = Guid.Empty;
        settings.ProcessId = 0;
        settings.ProcessLanguageGuid = Guid.Empty;
        settings.Project = IVsHierarchies.FirstOrDefault()?.Value;
        settings.RemoteMachine = "";
        settings.SendToOutputWindow = false;
        settings.StandardErrorHandle = IntPtr.Zero;
        settings.StandardInputHandle = IntPtr.Zero;
        settings.StandardOutputHandle = IntPtr.Zero;

        return new IDebugLaunchSettings[] { settings };
    }

    public async Task LoadAsync()
    {

    }
    public async Task UnloadAsync()
    {
    }
}

Hopefully that might help you or others who run into the same issue.

svenbieg commented 10 months ago

I have some DesignTime.props

  <ItemGroup>
    <ProjectCapability Include="CustomDebugger" />
  </ItemGroup>

and DesignTime.targets

  <ItemGroup>
    <PropertyPageSchema Include="Rules\CustomDebugger.xaml" />
  </ItemGroup>

like shown in the CpsExtension-example. The Debugger shows up in the UI and the DLL is loaded, but DebugLaunchProvider isn't invoked.

I've added the code above

[Export(ExportContractNames.Scopes.ConfiguredProject, typeof(IProjectDynamicLoadComponent))]

but it does nothing.