Parcourez les épines des vélos, premiÚre partie: découvrez les bases de la personnalisation du débogueur Visual Studio à l'aide de plugins

L'une des innovations de Visual Studio 2012 s'est accompagnĂ©e de l'apparition d'un nouveau dĂ©bogueur personnalisĂ© appelĂ© Concord. Son systĂšme de composants permet aux plug-ins VSIX de personnaliser le comportement du dĂ©bogueur et d'Ă©crire de nouveaux outils contextuels qui peuvent utiliser le dĂ©bogueur pour leurs besoins. Son API fournit de nombreuses fonctionnalitĂ©s QOL, telles que le marshaling entre le code managĂ© / non managĂ©, l'intĂ©gration transparente avec un processus distant / dĂ©boguĂ© localement, et plus encore. En fait, presque tout ce qui peut ĂȘtre fait dans l'EDI peut ĂȘtre fait par programme en utilisant l'API Concord! Modifiez les valeurs de variables spĂ©cifiques Ă  la volĂ©e, appelez des fonctions sur demande (ou faites spĂ©cifiquement le programme leur sauter des appels!), Les plugins peuvent rechercher par PDB (!), Bypass Ă©tape par Ă©tape et mĂȘme modification de code! Ouvrez le chat et vous dĂ©couvrirez ces innovations peu connues dans le domaine de la construction de vĂ©los.

Vous devriez probablement recommencer depuis le dĂ©but. Le dĂ©bogueur dĂ©tecte les composants en lisant les informations du fichier vsdconfig rĂ©fĂ©rencĂ© par le manifeste du plugin VSIX. À son tour, vsdconfig indique quelles interfaces sont implĂ©mentĂ©es par les composants du plugin et comment trouver ces composants (lien vers un fichier .dll, indiquant la classe ou, dans le cas de l'implĂ©mentation native, indiquant CLSID. Je donnerai des exemples en C #). De plus, un identifiant unique (GUID) pour chaque composant est indiquĂ©, ainsi que son "niveau". Un niveau est ce qui dĂ©termine dans quel ordre les plugins seront traitĂ©s, ainsi que dans le contexte de quel processus cette implĂ©mentation sera chargĂ©e - dans le processus IDE ou dans le processus de l'application dĂ©boguĂ©e. Cela est dĂ» au fait que certaines fonctionnalitĂ©s ne peut fonctionner que dans le cadre de l'IDE, et vice versa - que dans le contexte du processus en cours de dĂ©bogage. Certaines fonctions API fonctionnent de la mĂȘme maniĂšre ici et lĂ . En outre, un certain nombre de composants ont leurs propres rĂšgles de disposition, car ils peuvent dĂ©pendre des Ă©lĂ©ments de dĂ©bogueur existants situĂ©s Ă  leurs niveaux fixes. Pour Ă©viter les incidents, je recommande RTFM (https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.debugger.componentinterfaces?view=visualstudiosdk-2017) et des expĂ©riences indĂ©pendantes dans un bac Ă  sable sĂ©parĂ©, ce qui ne sera pas dommage. supprimer si quelque chose se produit (cela est connectĂ©, encore une fois, avec une telle nuance - dans certains cas, il n'est mĂȘme pas clair pourquoi l'assemblage ou le type ne se charge pas, car je ne pouvais toujours pas trouver oĂč les journaux apparaĂźtraient qui signaleraient le problĂšme de maniĂšre stable. Erreur, par exemple, en rĂ©fĂ©rence Ă  une dĂ©pendance qui ne peut pas ĂȘtre chargĂ©e dans le processus cible, elle peut apparaĂźtre dans la sortie st di-, ou non. Soyez akuratno, prenez commits frĂ©quentes, et ne restez pas assis derriĂšre le volant ivre).

La liste des niveaux est la suivante (je donnerai le texte en anglais pour que le lecteur n'ait pas d'incidents lors de la réalisation d'actes RFTM):

Niveaux des composants IDE (valeurs> 100 000):
ComposantNiveau composant
AD7 AL1 000 000 000
Fournisseur de démontage9,998,000
RequĂȘte de pile - Composants qui souhaitent interroger la pile d'appels9 997 000
Fournisseur de pile9,996,000
Filtre de pile - niveau oĂč les piles peuvent ĂȘtre filtrĂ©es et annotĂ©es9 995 000
Gestionnaire de points d'arrĂȘt9 994 000
Évaluation de l'expression IDE9 992 000
Marcheurs de pile de symboles - marcheurs de pile qui ont besoin d'accéder aux symboles9 991 000
IDE SymbolProvider - Composants qui fournissent des informations sur les symboles au reste du dĂ©bogueur. Le chemin du symbole ne doit pas ĂȘtre utilisĂ© en dessous de ce niveau.1,999,000

Niveaux de composants du processus cible (valeurs <99,999):
ComposantNiveau composant
Surveiller le fournisseur de symboles - Fournisseurs de symboles lorsque l'état symbolique est créé sur l'ordinateur cible (ex: interprÚte, code compilé / émis dynamiquement)75 000
Processeur de condition de point d'arrĂȘt - Ce niveau est destinĂ© au traitement des conditions de point d'arrĂȘt telles que les expressions de condition et le nombre de rĂ©sultats. En dessous de ce point de tous les Ă©vĂ©nements physiques des points d'arrĂȘt seront visibles, peu importe si oui ou non ils ont des conditions fausses.70 000
Surveiller le fournisseur de tùches - Il s'agit du niveau d'exploration de données de tùches dans le processus cible65 500
Surveiller l'Ă©valuateur d'expression65 000
Coordination du moniteur - Composants qui arbitrent entre les diffĂ©rents moniteurs pour les Ă©tapes, les points d'arrĂȘt par adresse native, la marche de la pile, etc.60 000
Surveiller les marcheurs de pile55 000
Moniteur de débogage personnalisé - Réservé aux moniteurs de débogage tiers qui souhaitent utiliser les services fournis par les moniteurs de débogage standard.40 500
Moniteur de débogage d'exécution - Fournit une inspection des données et un contrÎle d'exécution pour le code managé / natif / script40 000
Moniteur de débogage de base10 000
Services de moniteur de débogage de base - fournit des services utilitaires aux moniteurs de débogage de base (ex: création de processus) ainsi que des services de pré-débogage (ex: énumération de processus)1 000

Ensuite, dans l'ordre, le processus de création d'un projet. S'il n'y avait pas de nuances importantes, je pourrais décrire ce processus de maniÚre minimale ou l'ignorer du tout, mais les réalités sont complÚtement différentes - nous avons besoin d'un certain nombre de dépendances de bibliothÚque, ainsi que d'un outil pour créer des fichiers de configuration, qui pour une raison quelconque n'est pas distribué avec VisualStudio, mais est disponible uniquement avec pépite. En fait, nous devons maintenant passer à l'essentiel. Le processus de création et de configuration d'un projet est structuré comme suit:

  1. Ouvrez Visual Studio. Dans mon cas, 2017 Community Edition
  2. Projet VSIX ( Visual C # -> onglet Extensibilité , ou via la recherche). Appelons ça "HelloVSIX"
  3. Ajoutez un nouveau projet de bibliothÚque de classes dans la solution et appelez-le «DebuggeePlugin»
  4. Nous mettons la référence sur le projet "DebuggeePlugin" dans le projet "HelloVSIX"
  5. Nous mettons la référence à l'assembly "Microsoft.VisualStudio.Debugger.Engine" dans le projet DebuggeePlugin
  6. Ajouter au projet «DebuggeePlugin» une référence à l'ensemble NuGet Microsoft.VSSDK.Debugger.VSDConfigTool. Ceci est notre outil pour générer des configurations VSD.

Maintenant, nous sommes prĂȘts Ă  faire de notre plugin quelque chose d'utile. Faisons la chose la plus simple que vous puissiez faire - laissez-le afficher un MessageBox qui dit "Bonjour VSIX" lorsque le processus cible rencontre un point d'entrĂ©e. Pour ce faire, nous devons crĂ©er une classe qui implĂ©mente l'interface IDkmEntryPointNotification , ainsi que remplir plusieurs fichiers de configuration. Ajoutez une nouvelle classe publique appelĂ©e DkmEntryPointNotificationService et hĂ©ritez de l' interface IDkmEntryPointNotification et laissez l'implĂ©mentation par dĂ©faut pour l'instant:

using Microsoft.VisualStudio.Debugger; using Microsoft.VisualStudio.Debugger.ComponentInterfaces; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DebuggeePlugin { class DkmEntryPointNotificationService : IDkmEntryPointNotification { public void OnEntryPoint(DkmProcess process, DkmThread thread, DkmEventDescriptor eventDescriptor) { throw new NotImplementedException(); } } } 

Ajoutez le fichier «DkmEntryPointNotificationService.vsdconfigxml» au projet «DebuggeePlugin». Pour chaque classe dĂ©clarĂ©e qui doit recevoir des notifications via les implĂ©mentations des interfaces d'espace de noms Microsoft.VisualStudio.Debugger.ComponentInterfaces, vous devez disposer d'un tel fichier. Soit dit en passant, il est possible d'implĂ©menter plusieurs de ces interfaces Ă  la fois dans une mĂȘme classe. Nous devons maintenant modifier l'action de gĂ©nĂ©ration de notre fichier ".vsdconfigxml". Pour ce faire, vous devez Ă©diter manuellement le fichier de projet (j'ai sĂ©rieusement). Nous dĂ©chargeons le projet DebuggeePlugin et l'ouvrons avec l'Ă©diteur de studio. Nous devons trouver la balise XLM suivante:

 <None Include="DkmEntryPointNotificationService.vsdconfigxml" /> 

et déplacez cette balise vers votre propre ItemGroup, en changeant le type de None en VsdConfigXmlFiles:
 <ItemGroup> <VsdConfigXmlFiles Include="DkmEntryPointNotificationService.vsdconfigxml" /> </ItemGroup> 

Vous pouvez enregistrer et recharger le projet.

Maintenant, allez dans les configs. La premiĂšre chose Ă  faire: si «DebuggeePlugin» a ajoutĂ© le fichier de projet vsdconfig.xsd, alors il doit ĂȘtre retirĂ©. Nous allons le remplacer maintenant, car il est plus facile de travailler avec du texte brut. Ouvrez DkmEntryPointNotificationService.vsdconfigxml et remplacez le texte par ce qui suit:

 <?xml version="1.0" encoding="utf-8"?> <Configuration xmlns="http://schemas.microsoft.com/vstudio/vsdconfig/2008"> <ManagedComponent ComponentId="422413E1-450E-40A6-AE24-7E80A81CC668" ComponentLevel=^_^quot𘙼quot^_^ AssemblyName="DebuggeePlugin"> <Class Name="DebuggeePlugin.DkmEntryPointNotificationService"> <Implements> <InterfaceGroup> <NoFilter/> <Interface Name="IDkmEntryPointNotification"/> </InterfaceGroup> </Implements> </Class> </ManagedComponent> </Configuration> 

Dans tout fichier de ce type, nous devrons indiquer les éléments suivants:

  1. ComponentId - cette valeur peut ĂȘtre gĂ©nĂ©rĂ©e Ă  l'aide de l'outil de gĂ©nĂ©ration de GUID (Tools -> CreateGUID)
  2. ComponentLevel est le niveau de notre composant dans la hiérarchie. Consultez le tableau ci-dessus et les informations d'aide sur MSDN pour sélectionner la plage de valeurs souhaitée.
  3. Assemblyname est le nom de notre assemblage (pas une solution!). Dans ce cas, il y aura un DebuggeePlugin
  4. Nom de classe - doit ĂȘtre indiquĂ©, y compris l'espace de noms dans lequel se trouve la classe. Dans ce cas, DebuggeePlugin.DkmEntryPointNotificationService
  5. Tableau InterfaceGroup - chaque entrĂ©e qu'il contient indique une interface implĂ©mentĂ©e par ce composant. À l'intĂ©rieur de chaque nƓud InterfaceGroup, il devrait y avoir un sous-nƓud indiquant les interfaces communes Ă  tous, dans ce groupe, filtre, mais sur les filtres plus tard. Nous avons maintenant un seul nƓud d'interface et porte le nom de l'interface IdkmEntryPointNotification. Si nous avions plusieurs interfaces, il y aurait plusieurs nƓuds d'interface.

Permettez-moi de vous rappeler que pour chaque classe qui devrait recevoir des notifications du dĂ©bogueur, il doit y avoir un tel fichier. Mais le plaisir ne s'arrĂȘte pas lĂ . Chacun de ces fichiers est ensuite collectĂ© dans un fichier .vsdconfig dans le rĂ©pertoire de sortie du projet. Et ils doivent ĂȘtre rĂ©fĂ©rencĂ©s dans le manifeste du plugin. Cela se fait comme suit:

  1. AprÚs avoir formé le fichier ".vsdconfigxml", nous devons ... collecter la solution une fois, sinon nous n'aurons aucun fichier .vsdconfig dans le répertoire de sortie du projet)
  2. AprĂšs cela, ouvrez l'Ă©diteur de texte pour le fichier source.extension.vsixmanifest et ajoutez le code suivant avant la balise PackageManifest de fermeture:

  <Assets> <Asset Type="DebuggerEngineExtension" d:Source="File" Path="DebuggeePlugin.vsdconfig" /> </Assets> 

Si, aprĂšs les actions terminĂ©es, le fichier «DebuggeePlugin.vsdconfig» apparaĂźt dans le projet HelloVSIX, il doit ĂȘtre SUPPRIMÉ DU PROJET et la solution doit ĂȘtre collectĂ©e Ă  nouveau, sinon elle ne sera pas mise Ă  jour.

Les travaux prĂ©paratoires sont terminĂ©s! Vous pouvez commencer Ă  dĂ©boguer notre plugin. Cela se fait en dĂ©marrant une instance expĂ©rimentale de VisualStudio (pour les projets VSIX, il s'agit de la cible de dĂ©bogage par dĂ©faut, donc aucune Ă©tape supplĂ©mentaire n'est nĂ©cessaire). En fait, nous cliquons sur Debug-> StartDebugging et nous voyons une instance expĂ©rimentale de VisualStudio. Dans celui-ci, par dĂ©faut, notre plugin devrait dĂ©jĂ  ĂȘtre installĂ©. Vous pouvez le vĂ©rifier via le menu Outils-> Extensions et mises Ă  jour.

Du fait que nous avons implémenté l'interface IDkmEntryPointNotification, nous devrons créer un projet de test dans une instance expérimentale de VisualStudio. En fait, nous créons un nouveau projet, sélectionnez C ++ -> Application console (choisissez C ++, car les exemples suivants contiendront des spécificités C ++), appelez-le VSIXTestApp , exécutez sans aucune modification, collectez et voyez que notre instance expérimentale a cessé de lever une exception à l'intérieur de la méthode DebuggeePlugin. DkmEntryPointNotificationService.OnEntryPoint. Super! Vous devez maintenant afficher la MessageBox. Pour ce faire, il est nécessaire d'ajouter les références suivantes au projet DebuggeePlugin:

  • Microsoft.VisualStudio.Shell.15.0
  • Microsoft.VisualStudio.Shell.Interop
  • Microsoft.VisualStudio.Shell.Interop.8.0
  • Microsoft.VisualStudio.OLE.Interop

Ajoutez deux utilisations au début du fichier DkmEntryPointNotificationService.cs:

 using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; 

Et ajoutez un appel à la méthode VsShellUtilities.ShowMessageBox dans la méthode DkmEntryPointNotificationService.OnEntryPoint:

 using Microsoft.VisualStudio.Debugger; using Microsoft.VisualStudio.Debugger.ComponentInterfaces; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DebuggeePlugin { class DkmEntryPointNotificationService : IDkmEntryPointNotification { public void OnEntryPoint(DkmProcess process, DkmThread thread, DkmEventDescriptor eventDescriptor) { VsShellUtilities.ShowMessageBox(Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider, "Hello VSIX", "Hello VSIX", OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); } } } l' using Microsoft.VisualStudio.Debugger; using Microsoft.VisualStudio.Debugger.ComponentInterfaces; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DebuggeePlugin { class DkmEntryPointNotificationService : IDkmEntryPointNotification { public void OnEntryPoint(DkmProcess process, DkmThread thread, DkmEventDescriptor eventDescriptor) { VsShellUtilities.ShowMessageBox(Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider, "Hello VSIX", "Hello VSIX", OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); } } } du using Microsoft.VisualStudio.Debugger; using Microsoft.VisualStudio.Debugger.ComponentInterfaces; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DebuggeePlugin { class DkmEntryPointNotificationService : IDkmEntryPointNotification { public void OnEntryPoint(DkmProcess process, DkmThread thread, DkmEventDescriptor eventDescriptor) { VsShellUtilities.ShowMessageBox(Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider, "Hello VSIX", "Hello VSIX", OLEMSGICON.OLEMSGICON_INFO, OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); } } } 

Nous reconstruisons, lançons une instance expérimentale du studio, lançons un projet de test et le tour est joué!

Nous voyons que l'instance de test du studio a créé MessageBox!



Et qu'en est-il en fait?

Ici, nous avons appris à configurer un projet VSIX contenant un plug-in pour le débogueur Visual Studio, en tenant compte de la plupart des nuances qui entravent le résultat. C'est le point de départ d'un travail plus détaillé. Dans le prochain article, je vais vous montrer un autre point important: comment la communication a lieu entre l'IDE et les composants cibles de débogage.

Pour obtenir de l'aide sur l'utilisation de l'API Concord, vous pouvez vous référer non seulement à MSDN, mais également aux référentiels Microsoft suivants sur le github:
github.com/microsoft/PTVS
github.com/Microsoft/ConcordExtensibilitySamples

Source: https://habr.com/ru/post/fr476298/


All Articles