Megosztás a következőn keresztül:


Visual Studio hibakereső vizualizációk létrehozása

A hibakereső vizualizációk olyan Visual Studio-funkciók, amelyek egyéni vizualizációt biztosítanak egy adott .NET-típusú változókhoz vagy objektumokhoz a hibakeresési munkamenet során.

A hibakereső vizualizációk elérhetők a DataTip-ből, amely megjelenik, amikor az egérmutatót egy változóra helyezi, vagy az Automatikus, Helyi és Figyelő ablakokból:

Képernyőkép a hibakereső vizualizációkról az óraablakban.

Első lépések

Kövesse a Bővítményprojekt létrehozása szakaszt az Első lépések szakaszban.

Ezután adjon hozzá egy kiterjesztésű DebuggerVisualizerProvider osztályt, és alkalmazza rá az VisualStudioContribution attribútumot:

/// <summary>
/// Debugger visualizer provider class for <see cref="System.String"/>.
/// </summary>
[VisualStudioContribution]
internal class StringDebuggerVisualizerProvider : DebuggerVisualizerProvider
{
    /// <summary>
    /// Initializes a new instance of the <see cref="StringDebuggerVisualizerProvider"/> class.
    /// </summary>
    /// <param name="extension">Extension instance.</param>
    /// <param name="extensibility">Extensibility object.</param>
    public StringDebuggerVisualizerProvider(StringDebuggerVisualizerExtension extension, VisualStudioExtensibility extensibility)
        : base(extension, extensibility)
    {
    }

    /// <inheritdoc/>
    public override DebuggerVisualizerProviderConfiguration DebuggerVisualizerProviderConfiguration => new("My string visualizer", typeof(string));

    /// <inheritdoc/>
    public override async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken cancellationToken)
    {
        string targetObjectValue = await visualizerTarget.ObjectSource.RequestDataAsync<string>(jsonSerializer: null, cancellationToken);

        return new MyStringVisualizerControl(targetObjectValue);
    }
}

Az előző kód egy új hibakereső vizualizációt definiál, amely a következő típusú stringobjektumokra vonatkozik:

  • A DebuggerVisualizerProviderConfiguration tulajdonság határozza meg a vizualizáció megjelenítendő nevét és a támogatott .NET-típust.
  • A CreateVisualizerAsync metódust a Visual Studio hívja meg, amikor a felhasználó egy bizonyos értékre kéri a hibakereső vizualizáció megjelenítését. CreateVisualizerAsync Az objektum használatával VisualizerTarget lekéri a vizualizálandó értéket, és átadja azt egy egyéni távoli felhasználói vezérlőnek (lásd a távoli felhasználói felület dokumentációját). Ezután a rendszer visszaadja a távoli felhasználó vezérlőt, és megjelenik a Visual Studióban egy előugró ablakban.

Több típus megcélzása

A konfigurációs tulajdonság lehetővé teszi, hogy a vizualizáló kényelmesen több típust célba vegyen. Erre tökéletes példa a DataSet Visualizer, amely támogatja a , DataSet, DataTableés DataView objektumok vizualizációjátDataViewManager. Ez a funkció megkönnyíti a bővítmények fejlesztését, mivel a hasonló típusok ugyanazt a felhasználói felületet, a modelleket és a vizualizációs objektumforrást használhatják.

    /// <inheritdoc/>
    public override DebuggerVisualizerProviderConfiguration DebuggerVisualizerProviderConfiguration => new DebuggerVisualizerProviderConfiguration(
        new VisualizerTargetType("DataSet Visualizer", typeof(System.Data.DataSet)),
        new VisualizerTargetType("DataTable Visualizer", typeof(System.Data.DataTable)),
        new VisualizerTargetType("DataView Visualizer", typeof(System.Data.DataView)),
        new VisualizerTargetType("DataViewManager Visualizer", typeof(System.Data.DataViewManager)));

    /// <inheritdoc/>
    public override async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken cancellationToken)
    {
        ...
    }

A vizualizáció objektumforrása

A vizualizáció objektumforrása egy .NET-osztály, amelyet a hibakereső tölt be a hibakeresési folyamat során. A hibakereső vizualizáció képes adatokat lekérni a vizualizáció objektumforrásából a közzétett VisualizerTarget.ObjectSourcemetódusok használatával.

Az alapértelmezett vizualizációs objektumforrás lehetővé teszi a hibakereső vizualizációk számára, hogy a metódus meghívásával RequestDataAsync<T>(JsonSerializer?, CancellationToken) lekérje a vizualizálandó objektum értékét. Az alapértelmezett vizualizációs objektumforrás a Newtonsoft.Json használatával szerializálja az értéket, a VisualStudio.Bővíthetőségi kódtárak pedig a Newtonsoft.Jsont is használják a deszerializáláshoz. Alternatívaként a RequestDataAsync(CancellationToken)-t használhatja a szerializált érték JToken lekérdezésére.

Ha olyan .NET-típust szeretne vizualizálni, amelyet a Newtonsoft.Json natív módon támogat, vagy a saját típusát szeretné vizualizálni, és szerializálhatóvá szeretné tenni, az előző utasítások elegendőek egy egyszerű hibakereső vizualizáció létrehozásához. Olvassa el, ha összetettebb típusokat szeretne támogatni, vagy speciálisabb funkciókat szeretne használni.

Egyéni vizualizációs objektumforrás használata

Ha a vizualizálandó típust nem tudja automatikusan szerializálni a Newtonsoft.Json, létrehozhat egy egyéni vizualizációs objektumforrást a szerializálás kezeléséhez.

  • Hozzon létre egy új .NET-osztálykönyvtár projektet, amely a(z) netstandard2.0 célozza meg. Megcélozhatja a .NET-keretrendszer vagy a .NET egy konkrétabb verzióját, például net472 vagy net6.0, ha szükséges, a vizualizálandó objektum szerializálásához.
  • Adjon hozzá egy csomaghivatkozást a DebuggerVisualizers 17.6-os vagy újabb verzióhoz.
  • Adjon hozzá egy olyan osztályt, amely kiterjeszti VisualizerObjectSource, és felülbírálja GetData, hogy a target szerializált értékét a outgoingData folyamra írja.
public class MyObjectSource : VisualizerObjectSource
{
    /// <inheritdoc/>
    public override void GetData(object target, Stream outgoingData)
    {
        MySerializableType result = Convert(match);
        SerializeAsJson(outgoingData, result);
    }

    private static MySerializableType Convert(object target)
    {
        // Add your code here to convert target into a type serializable by Newtonsoft.Json
        ...
    }
}

Egyéni szerializálás használata

Ezzel a VisualizerObjectSource.SerializeAsJson módszerrel szerializálhat egy objektumot a Newtonsoft.Json használatával egy Stream-ra anélkül, hogy hivatkozást adna a Newtonsoft.Json-ra a könyvtárban. A SerializeAsJson használatával, tükrözés révén, a Newtonsoft.Json szerelvény egy verziója betöltődik a hibakeresési folyamatba.

Ha a Newtonsoft.Json-ra kell hivatkoznia, ugyanazt a verziót használja, amelyre a Microsoft.VisualStudio.Extensibility.Sdk csomag hivatkozik, de célszerű a DataContract és DataMember attribútumokat alkalmazni az objektumok szerializálására ahelyett, hogy a Newtonsoft.Json típusokra támaszkodna.

Másik lehetőségként saját egyéni szerializálást (például bináris szerializálást) is implementálhat közvetlenül a fájlba outgoingData.

A vizualizációs objektumforrás DLL-jének hozzáadása a bővítményhez

Módosítsa a .csproj kiterjesztés fájlt úgy, hogy hozzáad egy ProjectReference elemet a visualizer objektum forráskönyvtár-projekthez, ezzel biztosítva, hogy az objektum forráskönyvtárának felépítése a kiterjesztés csomagolása előtt megtörténjen.

Adjon hozzá egy Content elemet is, beleértve a vizualizáló objektum forráskönyvtárának DLL-ét, a netstandard2.0 bővítmény almappájába.

  <ItemGroup>
    <Content Include="pathToTheObjectSourceDllBinPath\$(Configuration)\netstandard2.0\MyObjectSourceLibrary.dll" Link="netstandard2.0\MyObjectSourceLibrary.dll">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\MyObjectSourceLibrary\MyObjectSourceLibrary.csproj" />
  </ItemGroup>

Másik lehetőségként használhatja az net4.6.2 vagy netcoreapp almappákat, ha a vizualizációs objektumforrástárat a .NET Framework vagy a .NET célzásával építette. A vizualizációs objektumforrástár különböző verzióival mindhárom almappát belefoglalhatja, de jobb, ha csak a netstandard2.0 célpontot célozza meg.

Próbálja meg minimalizálni a vizualizációs objektum forráskódtár DLL-jének függőségeinek számát. Ha a vizualizációs objektum forrástárának vannak más függőségei, mint a Microsoft.VisualStudio.DebuggerVisualizers és a már garantáltan betöltött könyvtárak a hibakeresési folyamat során, ügyeljen arra, hogy ezeket a DLL-fájlokat is helyezze be ugyanabba az almappába, mint a vizualizációs objektum forrástárának DLL-fájlja.

A hibakereső vizualizációszolgáltató frissítése az egyéni vizualizációs objektumforrás használatára

Ezután frissítheti a saját DebuggerVisualizerProvider konfigurációját, hogy az az egyéni vizualizációs objektumforrást hivatkozza.

    public override DebuggerVisualizerProviderConfiguration DebuggerVisualizerProviderConfiguration => new("My visualizer", typeof(TypeToVisualize))
    {
        VisualizerObjectSourceType = new(typeof(MyObjectSource)),
    };

    public override async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken cancellationToken)
    {
        MySerializableType result = await visualizerTarget.ObjectSource.RequestDataAsync<MySerializableType>(jsonSerializer: null, cancellationToken);
        return new MyVisualizerUserControl(result);
    }

Nagyméretű és összetett objektumokkal végzett munka

Ha a vizualizáció objektumforrásából nem lehet adatokat beolvasni egyetlen paraméter nélküli hívással RequestDataAsync, akkor összetettebb üzenetcserét végezhet a vizualizációs objektumforrással úgy, hogy többször invokál RequestDataAsync<TMessage, TResponse>(TMessage, JsonSerializer?, CancellationToken) , és különböző üzeneteket küld a vizualizáció objektumforrásának. Az üzenetet és a választ a VisualStudio.Extensibility infrastruktúra szerializálja a Newtonsoft.Json használatával. A(z) RequestDataAsync további felülbírálások lehetővé teszik a JToken objektumok használatát, vagy az egyéni szerializálás és deszerializálás megvalósítását.

Bármilyen egyéni protokollt implementálhat különböző üzenetek használatával a vizualizáció objektumforrásának információinak lekéréséhez. Ez a funkció leggyakrabban arra szolgál, hogy egy potenciálisan nagy objektum lekérését több hívásra bontsák az időtúllépés elkerülése érdekében RequestDataAsync.

Ez egy példa arra, hogyan kérhető le egyszerre egy potenciálisan nagy gyűjtemény tartalma:

for (int i = 0; ; i++)
{
    MySerializableType? collectionEntry = await visualizerTarget.ObjectSource.RequestDataAsync<int, MySerializableType?>(i, jsonSerializer: null, cancellationToken);
    if (collectionEntry is null)
    {
        break;
    }

    observableCollection.Add(collectionEntry);
}

A fenti kód egy egyszerű indexet használ a RequestDataAsync hívások üzeneteként. A megfelelő vizualizációs objektum forráskódja felülírná a metódust TransferData (ahelyett, hogy GetData):

public class MyCollectionTypeObjectSource : VisualizerObjectSource
{
    public override void TransferData(object target, Stream incomingData, Stream outgoingData)
    {
        var index = (int)DeserializeFromJson(incomingData, typeof(int))!;

        if (target is MyCollectionType collection && index < collection.Count)
        {
            var result = Convert(collection[index]);
            SerializeAsJson(outgoingData, result);
        }
        else
        {
            SerializeAsJson(outgoingData, null);
        }
    }

    private static MySerializableType Convert(object target)
    {
        // Add your code here to convert target into a type serializable by Newtonsoft.Json
        ...
    }
}

A fenti vizualizáló objektumforrás a VisualizerObjectSource.DeserializeFromJson metódust használja a vizualizációszolgáltató által incomingData küldött üzenet deszerializálására.

Ha olyan hibakereső vizualizáló szolgáltatót implementál, amely összetett üzenetkezelést végez a vizualizáló objektumforrásával, általában jobb átadni a VisualizerTarget vizualizálónak RemoteUserControl, hogy az üzenetcsere aszinkron módon történjen a vezérlő betöltése közben. VisualizerTarget A továbbítással üzeneteket küldhet a vizualizáció objektumforrásának, hogy adatokat kérjen le a felhasználó és a vizualizáció felhasználói felületének interakciói alapján.

public override Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken cancellationToken)
{
    return Task.FromResult<IRemoteUserControl>(new MyVisualizerUserControl(visualizerTarget));
}
internal class MyVisualizerUserControl : RemoteUserControl
{
    private readonly VisualizerTarget visualizerTarget;

    public MyVisualizerUserControl(VisualizerTarget visualizerTarget)
        : base(new MyDataContext())
    {
        this.visualizerTarget = visualizerTarget;
    }

    public override async Task ControlLoadedAsync(CancellationToken cancellationToken)
    {
        // Start querying the VisualizerTarget here
        ...
    }
    ...

Vizualizációk megnyitása eszközablakként

Alapértelmezés szerint az összes hibakereső vizualizációs bővítmény modális párbeszédpanelként nyílik meg a Visual Studio előterében. Ezért ha a felhasználó továbbra is használni szeretné az IDE-t, a vizualizációt be kell zárni. Ha azonban a Style tulajdonság ToolWindow van beállítva a DebuggerVisualizerProviderConfiguration tulajdonságban, akkor a visualizer nem modális eszközablakként lesz megnyitva, amely a hibakeresési munkamenet többi részében is nyitva maradhat. Ha nincs stílus deklarálva, a rendszer az alapértelmezett értéket ModalDialog használja.

    public override DebuggerVisualizerProviderConfiguration DebuggerVisualizerProviderConfiguration => new("My visualizer", typeof(TypeToVisualize))
    {
        Style = VisualizerStyle.ToolWindow
    };

    public override async Task<IRemoteUserControl> CreateVisualizerAsync(VisualizerTarget visualizerTarget, CancellationToken cancellationToken)
    {
        // The control will be in charge of calling the RequestDataAsync method from the visualizer object source and disposing of the visualizer target.
        return new MyVisualizerUserControl(visualizerTarget);
    }

Amikor egy vizualizáló úgy dönt, hogy ToolWindow-ként lesz megnyitva, elő kell fizetnie a StateChanged eseményre a VisualizerTarget-n. Ha egy vizualizáció eszközablakként van megnyitva, az nem blokkolja a felhasználót a hibakeresési munkamenet megszüntetésében. Ezért a hibakereső aktiválja a fent említett eseményt, amikor a hibakeresési cél állapota megváltozik. A vizualizációs bővítmények szerzőinek különös figyelmet kell fordítaniuk ezekre az értesítésekre, mivel a vizualizációs cél csak akkor érhető el, ha a hibakeresési munkamenet aktív, és a hibakeresési cél szüneteltetve van. Ha a visualizer célpont nem elérhető, a ObjectSource metódusok meghívása VisualizerTargetUnavailableException hibával sikertelen lesz.

internal class MyVisualizerUserControl : RemoteUserControl
{
    private readonly VisualizerDataContext dataContext;

#pragma warning disable CA2000 // Dispose objects before losing scope
    public MyVisualizerUserControl(VisualizerTarget visualizerTarget)
        : base(dataContext: new VisualizerDataContext(visualizerTarget))
#pragma warning restore CA2000 // Dispose objects before losing scope
    {
        this.dataContext = (VisualizerDataContext)this.DataContext!;
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            this.dataContext.Dispose();
        }
    }

    [DataContract]
    private class VisualizerDataContext : NotifyPropertyChangedObject, IDisposable
    {
        private readonly VisualizerTarget visualizerTarget;
        private MySerializableType? _value;
        
        public VisualizerDataContext(VisualizerTarget visualizerTarget)
        {
            this.visualizerTarget = visualizerTarget;
            visualizerTarget.StateChanged += this.OnStateChangedAsync;
        }

        [DataMember]
        public MySerializableType? Value
        {
            get => this._value;
            set => this.SetProperty(ref this._value, value);
        }

        public void Dispose()
        {
            this.visualizerTarget.Dispose();
        }

        private async Task OnStateChangedAsync(object? sender, VisualizerTargetStateNotification args)
        {
            switch (args)
            {
                case VisualizerTargetStateNotification.Available:
                case VisualizerTargetStateNotification.ValueUpdated:
                    Value = await visualizerTarget.ObjectSource.RequestDataAsync<MySerializableType>(jsonSerializer: null, CancellationToken.None);
                    break;
                case VisualizerTargetStateNotification.Unavailable:
                    Value = null;
                    break;
                default:
                    throw new NotSupportedException("Unexpected visualizer target state notification");
            }
        }
    }
}

Az Available értesítés a RemoteUserControl létrehozás után és az újonnan létrehozott vizualizációs eszközablakban való megjelenítés előtt érkezik meg. Amíg a vizualizáció nyitva marad, a többi VisualizerTargetStateNotification érték minden alkalommal fogadható, amikor a hibakeresési cél módosítja az állapotát. Az ValueUpdated értesítés azt jelzi, hogy a vizualizáció által utoljára megnyitott kifejezést sikeresen újra kiértékelték, ahol a hibakereső leállt, és a felhasználói felületnek frissítenie kell. Ha viszont a hibakeresési cél újraindul, vagy a kifejezés nem értékelhető újra a leállítás után, az Unavailable értesítés meg fog érkezni.

A vizualizált objektum értékének frissítése

Ha VisualizerTarget.IsTargetReplaceable igaz, a hibakereső vizualizációs a metódus használatával ReplaceTargetObjectAsync frissítheti a vizualizált objektum értékét a hibakeresési folyamatban.

A vizualizáció objektumforrásának felül kell bírálnia a metódust CreateReplacementObject :

public override object CreateReplacementObject(object target, Stream incomingData)
{
    // Use DeserializeFromJson to read from incomingData
    // the new value of the object being visualized
    ...
    return newValue;
}

Próbálja ki a RegexMatchDebugVisualizer mintát, hogy lássa ezeket a technikákat működés közben.