Pergi melalui duri untuk sepeda, bagian satu: mempelajari dasar-dasar menyesuaikan debugger Visual Studio menggunakan plugin

Salah satu inovasi Visual Studio 2012 disertai dengan penampilan debugger kustom baru yang disebut Concord. Sistem komponennya memungkinkan plug-in VSIX untuk menyesuaikan perilaku debugger dan menulis alat baru yang peka konteks yang dapat menggunakan debugger untuk kebutuhan mereka. API-nya menyediakan banyak fitur QOL, seperti mengatur di antara kode yang dikelola / tidak dikelola, integrasi tanpa batas dengan proses jarak jauh / lokal yang dibajak, dan banyak lagi. Faktanya, hampir semua yang dapat dilakukan dalam IDE dapat dilakukan secara terprogram menggunakan Concord API! Ubah nilai variabel tertentu dengan cepat, fungsi panggilan atas permintaan (atau secara khusus membuat program lewati panggilan ke mereka!), Plugin dapat mencari dengan PDB (!), Memotong langkah-demi-langkah dan bahkan modifikasi kode! Buka kucing dan Anda akan belajar tentang inovasi yang tidak banyak diketahui di bidang konstruksi sepeda ini.

Anda mungkin harus mulai dari awal. Debugger mendeteksi komponen dengan membaca informasi dari file vsdconfig yang dirujuk oleh manifes dari plugin VSIX. Pada gilirannya, vsdconfig menunjukkan antarmuka mana yang diimplementasikan oleh komponen plugin dan bagaimana menemukan komponen-komponen ini (tautan ke file .dll, yang menunjukkan kelas atau, dalam hal implementasi asli, menunjukkan CLSID. Saya akan memberikan contoh dalam C #). Juga, pengidentifikasi unik (GUID) untuk setiap komponen diindikasikan, serta "level" -nya. Level adalah apa yang menentukan dalam urutan apa plugin akan diproses, serta dalam konteks proses mana implementasi ini akan dimuat - ke dalam proses IDE atau ke dalam proses aplikasi debugged. Ini disebabkan oleh kenyataan bahwa beberapa fungsionalitas hanya dapat berfungsi dalam konteks IDE, dan sebaliknya - hanya dalam konteks proses yang di-debug. Beberapa fungsi API bekerja dengan cara yang sama di sana-sini. Juga, sejumlah komponen memiliki aturan tata letak sendiri, karena mereka mungkin bergantung pada elemen debugger yang ada yang terletak di tingkat tetap mereka. Untuk menghindari insiden, saya sarankan RTFM (https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.debugger.componentinterfaces?view=visualstudiosdk-2017) dan eksperimen independen dalam kotak pasir terpisah, yang tidak akan disayangkan hapus jika sesuatu terjadi (ini terhubung, sekali lagi, dengan nuansa seperti itu - dalam beberapa kasus bahkan tidak jelas mengapa perakitan atau tipe tidak memuat, karena saya masih tidak dapat menemukan di mana log akan muncul yang akan memberi sinyal masalah secara stabil. Kesalahan, misalnya, dengan merujuk pada ketergantungan yang tidak dapat dimuat ke proses target, mungkin muncul di st output di-, atau tidak. Jadilah akuratno, sering-seringlah komit, dan tidak duduk di belakang mabuk roda).

Daftar level adalah sebagai berikut (Saya akan memberikan teks dalam bahasa Inggris sehingga pembaca tidak memiliki insiden ketika melakukan tindakan RFTM):

Level komponen IDE (nilai> 100.000):
KomponenTingkat komponen
AD7 AL1.000.000.000
Penyedia pembongkaran9.998.000
Stack Query - Komponen yang ingin menanyakan tumpukan panggilan9.997.000
Penyedia tumpukan9.996.000
Stack filter - tingkat di mana tumpukan dapat disaring dan dijelaskan9.995.000
Manajer breakpoint9.994.000
Evaluasi Ekspresi IDE9.992.000
Symbol Stack Walkers - stack walker yang membutuhkan akses ke simbol9,991,000
IDE SymbolProvider - Komponen yang memberikan informasi simbol ke seluruh debugger. Jalur simbol tidak boleh digunakan di bawah level ini.1.999.000

Tingkat komponen proses target (nilai <99,999):
KomponenTingkat komponen
Monitor Symbol Provider - Penyedia simbol ketika status simbolis dibangun pada komputer target (mis: penerjemah, kode yang dikompilasi / dikeluarkan secara dinamis)75.000
Breakpoint Condition Processor - Level ini untuk memproses kondisi breakpoint seperti ekspresi kondisi dan jumlah hit. Di bawah titik ini semua peristiwa breakpoint fisik akan terlihat terlepas dari apakah mereka memiliki kondisi salah atau tidak.70.000
Monitor Penyedia Tugas - Ini adalah level untuk penggalian data tugas dalam proses target65.500
Monitor Evaluator Ekspresi65.000
Koordinasi Monitor - Komponen yang melakukan arbitrase di antara berbagai monitor untuk melangkah, breakpoint berdasarkan alamat asli, stack walk, dll.60.000
Monitor stack walker55.000
Custom Debug Monitor - Dicadangkan untuk monitor debug pihak ketiga yang ingin memanfaatkan layanan yang disediakan oleh monitor debug standar.40.500
Runtime Debug Monitor - Menyediakan inspeksi data dan kontrol pelaksanaan untuk kode yang dikelola / asli / skrip40.000
Monitor debug dasar10.000
Layanan Monitor Debug Dasar - menyediakan layanan utilitas untuk monitor debug dasar (mis: pembuatan proses) serta layanan pra-debugging (mis: enumerasi proses)1.000

Selanjutnya, secara berurutan, proses membuat proyek. Jika tidak ada nuansa penting, saya bisa menggambarkan proses ini minimal atau melewatkannya sama sekali, tetapi kenyataannya sangat berbeda - kita memerlukan sejumlah dependensi perpustakaan, serta alat untuk membuat file konfigurasi, yang karena alasan tertentu tidak didistribusikan dengan VisualStudio, tetapi tersedia hanya dengan nuget. Bahkan, sekarang kita perlu beralih ke esensi. Proses membuat dan menyiapkan proyek disusun sebagai berikut:

  1. Buka Visual Studio. Dalam kasus saya, Edisi Komunitas 2017
  2. Proyek VSIX ( Visual C # -> tab Extensibility , atau melalui pencarian). Sebut saja "HelloVSIX"
  3. Tambahkan proyek perpustakaan kelas baru dalam solusi, dan menyebutnya "DebuggeePlugin"
  4. Kami menempatkan referensi pada proyek "DebuggeePlugin" di proyek "HelloVSIX"
  5. Kami menempatkan referensi ke majelis "Microsoft.VisualStudio.Debugger.Engine" di proyek DebuggeePlugin
  6. Tambahkan referensi untuk paket nuget Microsoft.VSSDK.Debugger.VSDConfigTool ke proyek "DebuggeePlugin". Ini adalah alat kami untuk menghasilkan konfigurasi VSD.

Sekarang, kami siap membuat plugin kami melakukan sesuatu yang bermanfaat. Mari kita lakukan hal paling sederhana yang dapat Anda lakukan - biarkan ia menampilkan MessageBox yang mengatakan "Hello VSIX" ketika proses target menemukan titik masuk. Untuk melakukan ini, kita perlu membuat kelas yang mengimplementasikan antarmuka IDkmEntryPointNotification , serta mengisi beberapa file konfigurasi. Tambahkan kelas publik baru yang disebut DkmEntryPointNotificationService, dan mewarisi antarmuka IDkmEntryPointNotification , dan biarkan implementasi default untuk saat ini:

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(); } } } 

Tambahkan file "DkmEntryPointNotificationService.vsdconfigxml" ke proyek "DebuggeePlugin". Untuk setiap kelas yang dideklarasikan yang harus menerima pemberitahuan melalui implementasi antarmuka namespace Microsoft.VisualStudio.Debugger.ComponentInterfaces, Anda harus memiliki file seperti itu. Omong-omong, dimungkinkan untuk mengimplementasikan beberapa antarmuka seperti itu sekaligus dalam satu kelas. Sekarang kita perlu mengubah action build dari file ".vsdconfigxml" kita. Untuk melakukan ini, Anda harus mengedit file proyek secara manual (saya serius). Kami membongkar proyek DebuggeePlugin, dan membukanya dengan editor studio. Kami perlu menemukan tag XLM berikut:

 <None Include="DkmEntryPointNotificationService.vsdconfigxml" /> 

dan pindahkan tag ini ke ItemGroup Anda sendiri, ubah jenisnya dari Tidak Ada menjadi VsdConfigXmlFiles:
 <ItemGroup> <VsdConfigXmlFiles Include="DkmEntryPointNotificationService.vsdconfigxml" /> </ItemGroup> 

Anda dapat menyimpan dan memuat kembali proyek.

Sekarang, buka konfigurasi. Hal pertama yang harus dilakukan: jika file vsdconfig.xsd ditambahkan ke proyek DebuggeePlugin, maka itu harus dihapus. Kami akan menggantinya sekarang, karena lebih mudah untuk bekerja dengan teks mentah. Buka DkmEntryPointNotificationService.vsdconfigxml dan ganti teks dengan yang berikut ini:

 <?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> 

Dalam file semacam itu, kami akan diminta untuk menunjukkan hal-hal berikut:

  1. ComponentId - nilai ini dapat dihasilkan menggunakan alat pembuatan GUID (Alat -> CreateGUID)
  2. ComponentLevel adalah level komponen kami dalam hierarki. Lihat tabel di atas dan bantu informasi tentang MSDN untuk memilih rentang nilai yang diinginkan.
  3. Assemblyname adalah nama pertemuan kami (bukan solusi!). Dalam hal ini, akan ada DebuggeePlugin
  4. Nama Kelas - harus ditunjukkan termasuk namespace tempat kelas tersebut berada. Dalam hal ini, DebuggeePlugin.DkmEntryPointNotificationService
  5. Array InterfaceGroup - setiap entri di dalamnya menunjukkan antarmuka yang diimplementasikan oleh komponen ini. Di dalam setiap simpul InterfaceGroup, harus ada sub-simpul yang menunjukkan antarmuka umum untuk semua, dalam grup ini, filter, tetapi tentang filter nanti. Sekarang kami hanya memiliki satu simpul Antarmuka, dan membawa nama antarmuka IdkmEntryPointNotification. Jika kami memiliki beberapa antarmuka, akan ada beberapa node Interface.

Biarkan saya mengingatkan Anda bahwa untuk setiap kelas yang harus menerima pemberitahuan dari debugger, harus ada file seperti itu. Tapi kesenangan tidak berakhir di sana. Setiap file seperti itu, selanjutnya, dikumpulkan dalam file .vsdconfig di direktori output proyek. Dan mereka harus dirujuk dalam manifes plugin. Ini dilakukan sebagai berikut:

  1. Setelah kita membentuk file ".vsdconfigxml", kita harus ... mengumpulkan solusinya sekali, kalau tidak kita tidak akan memiliki file .vsdconfig di direktori output proyek)
  2. Setelah itu, buka editor teks untuk file source.extension.vsixmanifest dan tambahkan kode berikut sebelum tag PackageManifest penutup:

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

Jika, setelah tindakan selesai, file "DebuggeePlugin.vsdconfig" muncul di proyek HelloVSIX, itu harus DIHAPUS DARI PROYEK dan solusinya harus dikumpulkan lagi, jika tidak maka tidak akan diperbarui.

Pekerjaan persiapan sudah selesai! Anda dapat mulai men-debug plugin kami. Ini dilakukan dengan memulai instance eksperimental VisualStudio (untuk proyek VSIX ini adalah target debug default, jadi tidak diperlukan langkah-langkah tambahan). Sebenarnya, kita klik Debug-> StartDebugging dan kita melihat contoh eksperimental dari VisualStudio. Di dalamnya, secara default, plugin kami harus sudah diinstal. Anda dapat memverifikasi ini melalui Alat-> Ekstensi dan menu pembaruan.

Karena kami menerapkan antarmuka IDkmEntryPointNotification, kami harus membuat proyek uji coba dalam contoh eksperimental VisualStudio. Sebenarnya, kami membuat proyek baru, pilih C ++ -> Aplikasi Konsol (pilih C ++, karena contoh berikut ini akan berisi spesifik C ++), sebut saja VSIXTestApp , jalankan tanpa perubahan apa pun, kumpulkan dan lihat bahwa contoh eksperimental kami berhenti melemparkan pengecualian di dalam metode DebuggeePlugin. DkmEntryPointNotificationService.OnEntryPoint. Hebat! Sekarang Anda perlu menunjukkan MessageBox. Untuk melakukan ini, perlu menambahkan referensi berikut ke proyek DebuggeePlugin:

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

Tambahkan dua penggunaan di awal file DkmEntryPointNotificationService.cs:

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

Dan tambahkan panggilan ke metode VsShellUtilities.ShowMessageBox di metode 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); } } } 

Kami membangun kembali, meluncurkan contoh eksperimental studio, meluncurkan proyek uji dan voila!

Kami melihat bahwa instance uji dari studio membuat MessageBox!



Dan apa manfaatnya?

Di sini kami belajar cara mengatur proyek VSIX yang berisi plug-in untuk Visual Studio debugger, dengan mempertimbangkan sebagian besar nuansa yang menghalangi hasil. Ini adalah titik awal untuk pekerjaan yang lebih detail. Pada artikel berikutnya, saya akan menunjukkan kepada Anda poin penting lainnya: bagaimana komunikasi antara IDE dan komponen target Debug dilakukan.

Untuk bantuan lebih lanjut menggunakan Concord API, Anda dapat merujuk tidak hanya ke MSDN, tetapi juga ke repositori Microsoft berikut di github:
github.com/microsoft/PTVS
github.com/Microsoft/ConcordExtensibilitySamples

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


All Articles