Verwenden von LINQ mit den Debuggerobjekten
LINQ-Syntax kann mit den Debuggerobjekten verwendet werden, um Daten zu durchsuchen und zu bearbeiten. Die Verwendung der LINQ-Syntax mit dem Dx-Befehl ermöglicht eine konsistentere Erfahrung im Vergleich zur Verwendung von Debuggerbefehlen. Die Ausgabe und die Optionen sind unabhängig davon, welches Debuggerobjekt Sie betrachten, konsistent. LINQ-Abfragen ermöglichen Es Ihnen, Fragen zu stellen, z. B. "Was sind die fünf wichtigsten Prozesse, die die meisten Threads ausführen?".
Debuggerobjekte werden in einen Namespace projiziert, der unter "Debugger" verwurzelt ist. Prozesse, Module, Threads, Stapel, Stapelframes und lokale Variablen sind für die Verwendung in einer LINQ-Abfrage verfügbar.
LINQ ähnelt vom Konzept her dem strukturierte Abfragesprache (SQL), das zum Abfragen von Datenbanken verwendet wird. Sie können eine Reihe von LINQ-Methoden verwenden, um Debugdaten zu durchsuchen, zu filtern und zu analysieren. Die Syntax der LINQ-C#-Methode wird verwendet. Weitere Informationen zu LINQ und der LINQ C#-Syntax finden Sie unter Erste Schritte mit LINQ in C#
LINQ, das in der Debuggerunterstützung verwendet wird, verwendet die "Methodensyntax" von LINQ und nicht die "Abfragesyntax". Weitere Informationen zu den Unterschieden in LINQ (Language-Integrated Query) finden Sie.
LINQ-Befehle wie die folgenden können mit den Debuggerobjekten verwendet werden. Alle. Jegliche. Count. Ersten. Glätten. Groupby. Letzte. Orderby. Orderbydescending. Wählen Sie und aus. Wo. Diese Methoden folgen (so eng wie möglich) dem C#-LINQ-Methodenformular.
Native Debuggerobjekte
Native Debuggerobjekte stellen verschiedene Konstrukte und Verhaltensweisen der Debuggerumgebung dar. Beispiele für Debuggerobjekte sind:
- Sitzung
- Threads/Thread
- Prozesse/Prozess
- Stapelrahmen/Stapelrahmen
- Lokale Variablen
- Modules / Module
- Hilfsprogramm
- State
- Einstellungen
Sie können auch mit den Debuggerobjekten mit NatVis arbeiten. Weitere Informationen finden Sie unter Native Debugger-Objekte in NatVis. Informationen zur Verwendung von Debuggerobjekten mit JavaScript finden Sie unter Native Debuggerobjekte in JavaScript-Erweiterungen. Informationen zum Arbeiten mit C++ und den Treiberobjekten finden Sie unter Debugger Data Model C++– Übersicht.
Dx-Befehl
In den hier gezeigten Beispielen wird der Dx-Befehl verwendet. Weitere Informationen zum Arbeiten mit dem Dx-Befehl finden Sie unter dx (Debuggerobjektmodellausdruck anzeigen).
Entwickeln einer LINQ-Abfrage
Eine Möglichkeit zum Entwickeln einer LINQ-Debuggerobjektabfrage besteht darin, die angezeigten DML-Links zu verwenden, um das Datenmodell zu untersuchen, um zuerst das Debuggerobjekt zu finden, das in der Abfrage verwendet wird.
In diesem Beispiel möchten wir eine Liste der Prozesse in einer Kerneldebugsitzung und die Anzahl der Threads für jeden dieser Prozesse anzeigen.
Um mit der Untersuchung zu beginnen, können wir den Dx-Befehl verwenden, um das Debuggerobjekt der obersten Ebene anzuzeigen.
0: kd> dx Debugger
Debugger
Sessions
Settings
State
Utility
Nachdem Wir die Themen der obersten Ebene ausgewählt haben, stellen wir fest, dass Sitzungen am interessantesten ist. Daher wählen wir den DML-Link aus, um anzuzeigen, dass er Prozesse enthält.
0: kd> dx -r1 Debugger.Sessions[0]
Debugger.Sessions[0] : Remote KD: KdSrv:Server=@{<Local>},Trans=@{NET:Port=50005,Key=MyKey}
Processes
Id : 0
Attributes
Dann wählen wir weiter unten aus, um einen bestimmten Prozess zu betrachten, und wir sehen, dass die diesem Prozess zugeordneten Threads verfügbar sind. Wenn wir Threads für einen der Prozesse auswählen, sehen wir, dass alle diesem Prozess zugeordneten Threads verfügbar sind.
0: kd> dx -r1 Debugger.Sessions[0].Processes[1428].Threads
Debugger.Sessions[0].Processes[1428].Threads
[0x598] : <Unable to get stack trace> [Switch To]
[0x1220] : <Unable to get stack trace> [Switch To]
[0x6f8] : nt!KiSwapContext+0x76 (fffff806`4466a186) [Switch To]
[0x128c] : <Unable to get stack trace> [Switch To]
[0x27e4] : nt!KiSwapContext+0x76 (fffff806`4466a186) [Switch To]
Wir wissen nun, dass die Daten, die wir benötigen, um die Anzahl von Threads anzuzeigen, die einem Prozess zugeordnet sind, im Debuggerobjektmodell verfügbar sind.
Um die LINQ-Abfrage etwas kürzer zu machen, können wir die weiter unten in diesem Thema beschriebenen systemdefinierten Variablen verwenden, um die Prozesse anzuzeigen, die der aktuellen Sitzung zugeordnet sind.
0: kd> dx @$cursession.Processes
@$cursession.Processes
[0x0] : Idle [Switch To]
[0x4] : System [Switch To]
[0x90] : Registry [Switch To]
...
Fügen Sie als Nächstes eine select-Anweisung hinzu. Zunächst können wir das Feld Name angeben.
0: kd> dx @$cursession.Processes.Select(p => p.Name)
@$cursession.Processes.Select(p => p.Name)
[0x0] : Idle
[0x4] : System
[0x90] : Registry
...
Für unser Szenario benötigen wir auch die Anzahl von Threads. Da zwei Felder vorhanden sind, erstellen Sie einen anonymen Typ mithilfe von new, ähnlich wie die syntax des anonymen Typs von C#, die unten unter Benutzerdefinierte Variablen beschrieben wird.
dx @$cursession.Processes.Select(p => new {Name = p.Name, Threads = p.Threads})
Mit diesem Befehl druckt "dx" den Namen nicht mehr aus. Fügen Sie daher -r2 (rekursieren Sie zwei Ebenen) hinzu, um Name und Threads anzuzeigen.
dx -r2 @$cursession.Processes.Select(p => new {Name = p.Name, Threads = p.Threads})
@$cursession.Processes.Select(p => new {Name = p.Name, Threads = p.Threads})
[0x0]
Name : Idle
Threads
[0x4]
Name : System
Threads
[0x90]
Name : Registry
Threads
An diesem Punkt werden der Name des Prozesses und eine Liste der Threads angezeigt. Verwenden Sie zum Anzeigen des ThreadCount-Werts . Count() -Methode.
0: kd> dx -r2 @$cursession.Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count()})
@$cursession.Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count()})
[0x0]
Name : Idle
ThreadCount : 0x4
[0x4]
Name : System
ThreadCount : 0xe7
[0x90]
Name : Registry
ThreadCount : 0x4
...
Um zu sehen, welche Prozesse über eine große Anzahl von Threads verfügen, ordnen Sie die Liste mithilfe von OrderByDescending nach Threadanzahl an.
0: kd> dx -r2 @$cursession.Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count()}).OrderByDescending(p => p.ThreadCount)
@$cursession.Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count()}).OrderByDescending(p => p.ThreadCount)
[0x4]
Name : System
ThreadCount : 0xe7
[0xa38]
Name : svchost.exe
ThreadCount : 0x45
[0x884]
Name : MemCompression
ThreadCount : 0x3e
Um in einem formatierten Raster zu rendern, ändern Sie "-r2" in "-g". Die Rekursionsstufe muss nicht angegeben werden, da die Rasteroption die Spalten entsprechend anzeigt. Fügen Sie schließlich den Formatbezeichner ",d" hinzu, um Dezimalwerte auszugeben.
0: kd> dx -g @$cursession.Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count()}).OrderByDescending(p => p.ThreadCount),d
===========================================================================================
= = Name = ThreadCount =
===========================================================================================
= [4] - System - 231 =
= [2616] - svchost.exe - 69 =
= [2180] - MemCompression - 62 =
= [968] - explorer.exe - 61 =
Beispiele für Debuggerobjekte
Dieses Beispiel zeigt die fünf wichtigsten Prozesse, die die meisten Threads ausführen:
0: kd> dx -r2 Debugger.Sessions.First().Processes.Select(p => new { Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.ThreadCount),5
Debugger.Sessions.First().Processes.Select(p => new { Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.ThreadCount),5
:
[0x4] :
Name : <Unknown Image>
ThreadCount : 0x73
[0x708] :
Name : explorer.exe
ThreadCount : 0x2d
[0x37c] :
Name : svchost.exe
ThreadCount : 0x2c
[0x6b0] :
Name : MsMpEng.exe
ThreadCount : 0x22
[0x57c] :
Name : svchost.exe
ThreadCount : 0x15
[...]
Dieses Beispiel zeigt die Geräte in der Plug-and-Play-Gerätestruktur, gruppiert nach dem Namen des Treibers des physischen Geräteobjekts. Nicht die gesamte Ausgabe wird angezeigt.
kd> dx -r2 Debugger.Sessions.First().Devices.DeviceTree.Flatten(n => n.Children).GroupBy(n => n.PhysicalDeviceObject->Driver->DriverName.ToDisplayString())
Debugger.Sessions.First().Devices.DeviceTree.Flatten(n => n.Children).GroupBy(n => n.PhysicalDeviceObject->Driver->DriverName.ToDisplayString())
:
["\"\\Driver\\PnpManager\""] :
[0x0] : HTREE\ROOT\0
[0x1] : ROOT\volmgr\0000 (volmgr)
[0x2] : ROOT\BasicDisplay\0000 (BasicDisplay)
[0x3] : ROOT\CompositeBus\0000 (CompositeBus)
[0x4] : ROOT\vdrvroot\0000 (vdrvroot)
...
Automatische Vervollständigung der Dx-Befehlsregisterkarte
Die automatische Vervollständigung der kontextbezogenen TAB-Taste kennt die LINQ-Abfragemethoden und funktioniert für Parameter von Lambdas.
Geben Sie beispielsweise den folgenden Text in den Debugger ein (oder kopieren sie ihn ein). Drücken Sie dann mehrmals die TAB-TASTE, um potenzielle Vervollständigungen zu durchlaufen.
dx -r2 Debugger.Sessions.First().Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.
Drücken Sie die TAB-TASTE bis ". Name" wird angezeigt. Fügen Sie eine schließende Klammer "" hinzu, und drücken Sie die EINGABETASTE, um den Befehl auszuführen.
kd> dx -r2 Debugger.Sessions.First().Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.Name)
Debugger.Sessions.First().Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.Name) :
[0x274] :
Name : winlogon.exe
ThreadCount : 0x4
[0x204] :
Name : wininit.exe
ThreadCount : 0x2
[0x6c4] :
Name : taskhostex.exe
ThreadCount : 0x8
...
Dieses Beispiel zeigt die Vervollständigung mit einer wichtigen Vergleichsmethode. Die Ersetzung zeigt Zeichenfolgenmethoden an, da der Schlüssel eine Zeichenfolge ist.
dx -r2 Debugger.Sessions.First().Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.Name, (a, b) => a.
Drücken Sie die TAB-TASTE bis ". Length" wird angezeigt. Fügen Sie eine schließende Klammer "" hinzu, und drücken Sie die EINGABETASTE, um den Befehl auszuführen.
kd> dx -r2 Debugger.Sessions.First().Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.Name, (a, b) => a.Length)
Debugger.Sessions.First().Processes.Select(p => new {Name = p.Name, ThreadCount = p.Threads.Count() }).OrderByDescending(p => p.Name, (a, b) => a.Length) :
[0x544] :
Name : spoolsv.exe
ThreadCount : 0xc
[0x4d4] :
Name : svchost.exe
ThreadCount : 0xa
[0x438] :
Name : svchost.exe
Benutzerdefinierte Variablen
Eine benutzerdefinierte Variable kann definiert werden, indem dem Variablennamen @$ vorangestellt wird. Eine benutzerdefinierte Variable kann alles zugewiesen werden, was dx verwenden kann, z. B. Lambdas, die Ergebnisse von LINQ-Abfragen usw.
Sie können den Wert einer Benutzervariablen wie folgt erstellen und festlegen.
kd> dx @$String1="Test String"
Sie können die definierten Benutzervariablen mithilfe von Debugger.State.UserVariables oder @$vars anzeigen.
kd> dx Debugger.State.UserVariables
Debugger.State.UserVariables :
mySessionVar :
String1 : Test String
Sie können eine Variable mit entfernen. Entfernen.
kd> dx @$vars.Remove("String1")
In diesem Beispiel wird gezeigt, wie Sie eine Benutzervariable definieren, um auf Debugger.Sesssions zu verweisen.
kd> dx @$mySessionVar = Debugger.Sessions
Die benutzerdefinierte Variable kann dann wie unten dargestellt verwendet werden.
kd> dx -r2 @$mySessionVar
@$mySessionVar :
[0x0] : Remote KD: KdSrv:Server=@{<Local>},Trans=@{COM:Port=\\.\com3,Baud=115200,Timeout=4000}
Processes :
Devices
Systemdefinierte Variablen
Die folgenden systemdefinierten Variablen können in jeder LINQ dx-Abfrage verwendet werden.
@$cursession : Die aktuelle Sitzung
@$curprocess : Der aktuelle Prozess
@$curthread : Der aktuelle Thread
In diesem Beispiel wird die Verwendung der vom System definierten Variablen veranschaulicht.
kd> dx @$curprocess.Threads.Count()
@$curprocess.Threads.Count() : 0x4
kd> dx -r1 @$curprocess.Threads
@$curprocess.Threads :
[0x4adc] :
[0x1ee8] :
[0x51c8] :
[0x62d8] :
...
Benutzerdefinierte Variablen – Anonyme Typen
Diese Erstellung dynamischer Objekte erfolgt mithilfe der Syntax des anonymen C#-Typs (neu { ... }). Weitere Informationen finden Sie unter Anonyme Typen (C#-Programmierhandbuch). In diesem Beispiel wird ein anonymer Typ mit einer ganzen Zahl und einem Zeichenfolgenwert erstellt.
kd> dx -r1 new { MyInt = 42, MyString = "Hello World" }
new { MyInt = 42, MyString = "Hello World" } :
MyInt : 42
MyString : Hello World
Funktionsobjekte (Lambdaausdrücke)
Viele der Methoden, die zum Abfragen von Daten verwendet werden, basieren auf dem Konzept der wiederholten Ausführung einer vom Benutzer bereitgestellten Funktion über Objekte in einer Auflistung hinweg. Um die Möglichkeit zum Abfragen und Bearbeiten von Daten im Debugger zu unterstützen, unterstützt der dx-Befehl Lambdaausdrücke mit der entsprechenden C#-Syntax. Ein Lambdaausdruck wird durch Verwendung des => -Operators wie folgt definiert:
(Argumente) => (Ergebnis)
Um zu sehen, wie LINQ mit dx verwendet wird, probieren Sie dieses einfache Beispiel aus, um 5 und 7 hinzuzufügen.
kd> dx ((x, y) => (x + y))(5, 7)
Der dx-Befehl gibt den Lambdaausdruck zurück und zeigt das Ergebnis von 12 an.
((x, y) => (x + y))(5, 7) : 12
In diesem Lambda-Beispielausdruck werden die Zeichenfolgen "Hello" und "World" kombiniert.
kd> dx ((x, y) => (x + y))("Hello", "World")
((x, y) => (x + y))("Hello", "World") : HelloWorld
Unterstützte LINQ-Syntax: Abfragemethoden
Jedes Objekt, das dx als iterierbar definiert (sei es, dass ein natives Array, ein Typ, für den NatVis als Container geschrieben wurde, oder ein Debuggererweiterungsobjekt) verfügt über eine Reihe von LINQ-Methoden (oder LINQ-äquivalenten) projiziert. Diese Abfragemethoden werden unten beschrieben. Die Signaturen der Argumente für die Abfragemethoden werden nach allen Abfragemethoden aufgelistet.
Filtermethoden
. Where ( PredicateMethod ): Gibt eine neue Auflistung von -Objekten zurück, die jedes Objekt in der Eingabeauflistung enthalten, für das die Prädikatmethode true zurückgegeben hat.
Projektionsmethoden
. Flatten ( [KeyProjectorMethod] ): Verwendet einen Eingabecontainer von Containern (eine Struktur) und vereinfacht ihn in einen einzelnen Container, der jedes Element in der Struktur enthält. Wenn die optionale Schlüsselprojektormethode bereitgestellt wird, wird die Struktur als Container von Schlüsseln betrachtet, die selbst Container sind, und diese Schlüssel werden durch einen Aufruf der Projektionsmethode bestimmt.
. Select ( KeyProjectorMethod ): Gibt eine neue Auflistung von Objekten zurück, die das Ergebnis des Aufrufs der Projektormethode für jedes Objekt in der Eingabeauflistung enthalten.
Gruppierungsmethoden
. GroupBy ( KeyProjectorMethod, [KeyComparatorMethod] ): Gibt eine neue Auflistung von Sammlungen zurück, indem alle Objekte in der Eingabeauflistung mit demselben Schlüssel gruppiert werden, der durch Aufrufen der Schlüsselprojektormethode bestimmt wurde. Eine optionale Vergleichsmethode kann bereitgestellt werden.
Join (InnerCollection, Outer key selector method, Inner key selector method, Result selector method, [ComparatorMethod]): Verknüpft zwei Sequenzen basierend auf Schlüsselauswahlfunktionen und extrahiert Wertepaare. Es kann auch eine optionale Vergleichsmethode angegeben werden.
Intersect (InnerCollection, [ComparatorMethod]):: Gibt die festgelegte Schnittmenge zurück, d. h. Elemente, die in jeder von zwei Auflistungen angezeigt werden. Es kann auch eine optionale Vergleichsmethode angegeben werden.
Union (InnerCollection, [ComparatorMethod]): Gibt die Mengenunion zurück, d. h. eindeutige Elemente, die in einer der beiden Auflistungen angezeigt werden. Es kann auch eine optionale Vergleichsmethode angegeben werden.
Data Set-Methoden
Contains (Object, [ComparatorMethod]):: Bestimmt, ob eine Sequenz ein angegebenes Element enthält. Es kann eine optionale Vergleichsmethode bereitgestellt werden, die jedes Mal aufgerufen wird, wenn das Element mit einem Eintrag in der Sequenz verglichen wird.
Distinct ([ComparatorMethod]): Entfernt doppelte Werte aus einer Auflistung. Eine optionale Vergleichsmethode kann bereitgestellt werden, um jedes Mal aufgerufen zu werden, wenn Objekte in der Auflistung verglichen werden müssen.
Außer (InnerCollection, [ComparatorMethod]): Gibt den Satzunterschied zurück, d. h. die Elemente einer Auflistung, die in einer zweiten Auflistung nicht angezeigt werden. Eine optionale Vergleichsmethode kann angegeben werden.
Concat (InnerCollection):Verkettet zwei Sequenzen, um eine Sequenz zu bilden.
Sortiermethoden
. OrderBy ( KeyProjectorMethod, [KeyComparatorMethod] ): Sortiert die Auflistung in aufsteigender Reihenfolge nach einem Schlüssel, der durch Aufrufen der Schlüsselprojektionsmethode für jedes Objekt in der Eingabeauflistung bereitgestellt wird. Eine optionale Vergleichsmethode kann bereitgestellt werden.
. OrderByDescending ( KeyProjectorMethod, [KeyComparatorMethod] ): Sortiert die Auflistung in absteigender Reihenfolge nach einem Schlüssel, der durch Aufrufen der Schlüsselprojektionsmethode für jedes Objekt in der Eingabeauflistung bereitgestellt wird. Eine optionale Vergleichsmethode kann bereitgestellt werden.
Aggregieren von Methoden
Count (): Eine Methode, die die Anzahl der Elemente in der Auflistung zurückgibt.
Sum ([ProjectionMethod]): Berechnet die Summe der Werte in einer Auflistung. Kann optional eine Projektormethode angeben, um die Elemente vor dem Summieren zu transformieren.
Skip-Methoden
Überspringen (Anzahl): Überspringt Elemente bis zu einer angegebenen Position in einer Sequenz.
SkipWhile (PredicateMethod): Überspringt Elemente basierend auf einer Prädikatfunktion, bis ein Element die Bedingung nicht erfüllt.
Take-Methoden
Take (Count): Akzeptiert Elemente bis zu einer angegebenen Position in einer Sequenz.
TakeWhile (PredicateMethod):Verwendet Elemente basierend auf einer Prädikatfunktion, bis ein Element die Bedingung nicht erfüllt.
Vergleichsmethoden
SequenceEqual (InnerCollection, [ComparatorMethod]): Bestimmt, ob zwei Sequenzen gleich sind, indem Elemente paarweise verglichen werden. Ein optionaler Vergleich kann angegeben werden.
Methoden zur Fehlerbehandlung
AllNonError (PredicateMethod): Gibt zurück, ob alle Nichtfehlerelemente einer Auflistung eine bestimmte Bedingung erfüllen.
FirstNonError ([PredicateMethod]):: Gibt das erste Element einer Auflistung zurück, das kein Fehler ist.
LastNonError ([PredicateMethod]): Gibt das letzte Element einer Auflistung zurück, das kein Fehler ist.
Andere Methoden
. All ( PredicateMethod ): Gibt zurück, ob das Ergebnis des Aufrufs der angegebenen Prädikatmethode für jedes Element in der Eingabeauflistung true ist.
. Any ( PredicateMethod ): Gibt zurück, ob das Ergebnis des Aufrufs der angegebenen Prädikatmethode für ein Element in der Eingabeauflistung true ist.
. First ( [PredicateMethod] ): Gibt das erste Element in der Auflistung zurück. Wenn das optionale Prädikat übergeben wird, gibt das erste Element in der Auflistung zurück, für das ein Aufruf des Prädikats true zurückgibt.
. Last ( [PredicateMethod] ): Gibt das letzte Element in der Auflistung zurück. Wenn das optionale Prädikat übergeben wird, gibt das letzte Element in der Auflistung zurück, für das ein Aufruf des Prädikats true zurückgibt.
Min([KeyProjectorMethod]): Gibt das mindeste Element der Auflistung zurück. Eine optionale Projektormethode kann angegeben werden, um jede Methode zu projizieren, bevor sie mit anderen verglichen wird.
Max([KeyProjectorMethod]): Gibt das maximum-Element der Auflistung zurück. Eine optionale Projektormethode kann angegeben werden, um jede Methode zu projizieren, bevor sie mit anderen verglichen wird.
Single([PredicateMethod]): Gibt das einzige Element aus der Liste zurück (oder einen Fehler, wenn die Auflistung mehr als ein Element enthält). Wenn ein Prädikat angegeben ist, gibt das einzelne Element zurück, das dieses Prädikat erfüllt (wenn mehr als ein Element es erfüllt, gibt die Funktion stattdessen einen Fehler zurück).
Signaturen der Argumente
KeyProjectorMethod : ( obj => beliebiger Schlüssel ) | Übernimmt ein -Objekt der Auflistung und gibt einen Schlüssel aus diesem -Objekt zurück. |
KeyComparatorMethod: ( (a, b) => integer value ) | Verwendet zwei Schlüssel und vergleicht sie, die zurückgegeben werden: -1, wenn ( a < b ) 0, wenn ( a == b) 1, wenn ( a > b ) |
PredicateMethod: ( obj => boolescher Wert ) | Verwendet ein Objekt der Auflistung und gibt true oder false zurück, je nachdem, ob dieses Objekt bestimmte Kriterien erfüllt. |
Unterstützte LINQ-Syntax: Zeichenfolgenbearbeitung
Für alle Zeichenfolgenobjekte sind die folgenden Methoden projiziert, sodass sie zur Verwendung verfügbar sind:
Abfragen relevanter Methoden & Eigenschaften
. Contains ( OtherString ): Gibt einen booleschen Wert zurück, der angibt, ob die Eingabezeichenfolge OtherString enthält.
. EndsWith ( OtherString ):: Gibt einen booleschen Wert zurück, der angibt, ob die Eingabezeichenfolge mit OtherString endet.
Length: Eine Eigenschaft, die die Länge der Zeichenfolge zurückgibt.
. StartsWith ( OtherString ):: Gibt einen booleschen Wert zurück, der angibt, ob die Eingabezeichenfolge mit OtherString beginnt.
. Teilzeichenfolge ( StartPos, [Length] ): Gibt eine Teilzeichenfolge innerhalb der Eingabezeichenfolge ab der angegebenen Startposition zurück. Wenn die optionale Länge angegeben wird, hat die zurückgegebene Teilzeichenfolge die angegebene Länge. andernfalls : Sie wird an das Ende der Zeichenfolge übergeben.
Verschiedene Methoden
. IndexOf ( OtherString ):: Gibt den Index des ersten Vorkommens von OtherString innerhalb der Eingabezeichenfolge zurück.
. LastIndexOf ( OtherString ): Gibt den Index des letzten Vorkommens von OtherString innerhalb der Eingabezeichenfolge zurück.
Formatierungsmethoden
. PadLeft ( TotalWidth ): Fügt nach Bedarf Leerzeichen auf der linken Seite der Zeichenfolge hinzu, um die Gesamtlänge der Zeichenfolge auf die angegebene Breite zu bringen.
. PadRight ( TotalWidth ): Fügt nach Bedarf Leerzeichen auf der rechten Seite der Zeichenfolge hinzu, um die Gesamtlänge der Zeichenfolge auf die angegebene Breite zu bringen.
. Remove ( StartPos, [Length] ): Entfernt Zeichen aus der Eingabezeichenfolge, beginnend als die angegebene Startposition. Wenn der optionale Längenparameter angegeben wird, wird diese Anzahl von Zeichen entfernt. Andernfalls werden alle Zeichen am Ende der Zeichenfolge entfernt.
. Replace ( SearchString, ReplaceString ): Ersetzt jedes Vorkommen von SearchString innerhalb der Eingabezeichenfolge durch den angegebenen ReplaceString.
Zeichenfolgenobjektprojektionen
Zusätzlich zu den Methoden, die direkt auf Zeichenfolgenobjekte projiziert werden, wird für jedes Objekt, das selbst über eine Zeichenfolgenkonvertierung verfügt, die folgende Methode darauf projiziert, sodass es zur Verwendung verfügbar ist:
. ToDisplayString ( ): Gibt eine Zeichenfolgenkonvertierung des -Objekts zurück. Dies ist die Zeichenfolgenkonvertierung, die in einem DX-Aufruf für das -Objekt angezeigt wird. Sie können einen Formatierungsspezifizierer bereitstellen, um die Ausgabe von ToDisplayString zu formatieren. Weitere Informationen finden Sie unter Formatbezeichner für C++ im Visual Studio-Debugger.
Die folgenden Beispiele veranschaulichen die Verwendung von Formatbezeichnern.
kd> dx (10).ToDisplayString("d")
(10).ToDisplayString("d") : 10
kd> dx (10).ToDisplayString("x")
(10).ToDisplayString("x") : 0xa
kd> dx (10).ToDisplayString("o")
(10).ToDisplayString("o") : 012
kd> dx (10).ToDisplayString("b")
(10).ToDisplayString("b") : 0y1010
kd> dx ("some wchar string here").ToDisplayString("su")
("some wchar string here").ToDisplayString("su") : "some wchar string here"
kd> dx ("some wchar string here").ToDisplayString("sub")
("some wchar string here").ToDisplayString("sub") : some wchar string here
Debugbeispiel für Plug & Play
In diesem Abschnitt wird veranschaulicht, wie die integrierten Debuggerobjekte, die mit LINQ-Abfragen verwendet werden, zum Debuggen von Plug-and-Play-Objekten verwendet werden können.
Anzeigen aller Geräte
Verwenden Sie "Flatten" in der Gerätestruktur, um alle Geräte anzuzeigen.
1: kd> dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children)
@$cursession.Devices.DeviceTree.Flatten(n => n.Children)
[0x0] : HTREE\ROOT\0
[0x1] : ROOT\volmgr\0000 (volmgr)
[0x2] : ROOT\BasicDisplay\0000 (BasicDisplay)
[0x3] : ROOT\CompositeBus\0000 (CompositeBus)
[0x4] : ROOT\vdrvroot\0000 (vdrvroot)
[0x5] : ROOT\spaceport\0000 (spaceport)
[0x6] : ROOT\KDNIC\0000 (kdnic)
[0x7] : ROOT\UMBUS\0000 (umbus)
[0x8] : ROOT\ACPI_HAL\0000
...
Rasteranzeige
Wie bei anderen dx-Befehlen können Sie einen Befehl auswählen und halten (oder mit der rechten Maustaste darauf klicken), nachdem er ausgeführt wurde, und "Als Raster anzeigen" auswählen oder dem Befehl "-g" hinzufügen, um eine Rasteransicht der Ergebnisse zu erhalten.
# 0: kd> dx -g @$cursession.Devices.DeviceTree.Flatten(n => n.Children)
=====================================================================================================================================================================================================================================================================================================================
# = = (+) DeviceNodeObject = InstancePath = ServiceName = (+) PhysicalDeviceObject = State = (+) Resources = (+) Children =
=====================================================================================================================================================================================================================================================================================================================
= [0x0] : HTREE\ROOT\0 - {...} - HTREE\ROOT\0 - - 0xffffb6075614be40 : Device for "\Driver\PnpManager" - DeviceNodeStarted (776) - {...} - [object Object] =
= [0x1] : ROOT\volmgr\0000 (volmgr) - {...} - ROOT\volmgr\0000 - volmgr - 0xffffb607561fbe40 : Device for "\Driver\PnpManager" - DeviceNodeStarted (776) - {...} - [object Object] =
= [0x2] : ROOT\BasicDisplay\0000 (BasicDisplay) - {...} - ROOT\BasicDisplay\0000 - BasicDisplay - 0xffffb607560739b0 : Device for "\Driver\PnpManager" - DeviceNodeStarted (776) - {...} - [object Object] =
= [0x3] : ROOT\CompositeBus\0000 (CompositeBus) - {...} - ROOT\CompositeBus\0000 - CompositeBus - 0xffffb607561f9060 : Device for "\Driver\PnpManager" - DeviceNodeStarted (776) - {...} - [object Object] =
...
Geräte nach Zustand anzeigen
Verwenden Sie Where , um einen bestimmten Gerätestatus anzugeben.
dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.State <operator> <state number>)
Verwenden Sie beispielsweise diesen Befehl, um Geräte im Zustand DeviceNodeStarted anzuzeigen.
1: kd> dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.State == 776)
@$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.State == 776)
[0x0] : HTREE\ROOT\0
[0x1] : ROOT\volmgr\0000 (volmgr)
[0x2] : ROOT\BasicDisplay\0000 (BasicDisplay)
[0x3] : ROOT\CompositeBus\0000 (CompositeBus)
[0x4] : ROOT\vdrvroot\0000 (vdrvroot)
...
Nicht gestartete Geräte anzeigen
Verwenden Sie diesen Befehl, um Geräte anzuzeigen, die sich nicht im Zustand DeviceNodeStarted befinden.
1: kd> dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.State != 776)
@$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.State != 776)
[0x0] : ACPI\PNP0C01\1
[0x1] : ACPI\PNP0000\4&215d0f95&0
[0x2] : ACPI\PNP0200\4&215d0f95&0
[0x3] : ACPI\PNP0100\4&215d0f95&0
[0x4] : ACPI\PNP0800\4&215d0f95&0
[0x5] : ACPI\PNP0C04\4&215d0f95&0
[0x6] : ACPI\PNP0700\4&215d0f95&0 (fdc)
[0x7] : ACPI\PNP0C02\1
[0x8] : ACPI\PNP0C02\2
Anzeigen von Geräten nach Problemcode
Verwenden Sie das DeviceNodeObject.Problem-Objekt , um Geräte mit bestimmten Problemcodes anzuzeigen.
dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem <operator> <problemCode>)
Verwenden Sie beispielsweise diesen Befehl, um Geräte anzuzeigen, die einen Problemcode ungleich Null aufweisen. Dies enthält ähnliche Informationen wie "!devnode 0 21".
1: kd> dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem != 0)
@$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem != 0)
[0x0] : HTREE\ROOT\0
[0x1] : ACPI\PNP0700\4&215d0f95&0 (fdc)
Alle Geräte ohne Probleme anzeigen
Verwenden Sie diesen Befehl, um alle Geräte ohne Probleme anzuzeigen.
1: kd> dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem == 0)
@$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem == 0)
[0x0] : ROOT\volmgr\0000 (volmgr)
[0x1] : ROOT\BasicDisplay\0000 (BasicDisplay)
[0x2] : ROOT\CompositeBus\0000 (CompositeBus)
[0x3] : ROOT\vdrvroot\0000 (vdrvroot)
...
Anzeigen aller Geräte mit einem bestimmten Problem
Verwenden Sie diesen Befehl, um Geräte mit dem Problemstatus 0x16 anzuzeigen.
1: kd> dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem == 0x16)
@$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.DeviceNodeObject.Problem == 0x16)
[0x0] : HTREE\ROOT\0
[0x1] : ACPI\PNP0700\4&215d0f95&0 (fdc)
Anzeigen von Geräten nach Funktionstreiber
Verwenden Sie diesen Befehl, um Geräte nach Funktionstreiber anzuzeigen.
dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.ServiceName <operator> <service name>)
Verwenden Sie diesen Befehl, um Geräte anzuzeigen, die einen bestimmten Funktionstreiber verwenden, z. B. atapi.
1: kd> dx @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.ServiceName == "atapi")
@$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => n.ServiceName == "atapi")
[0x0] : PCIIDE\IDEChannel\4&10bf2f88&0&0 (atapi)
[0x1] : PCIIDE\IDEChannel\4&10bf2f88&0&1 (atapi)
Anzeigen einer Liste der Starttreiber
Um die Liste der geladenen Winload-Starttreiber anzuzeigen, müssen Sie sich in einem Kontext befinden, in dem Sie Zugriff auf den LoaderBlock haben und der LoaderBlock noch früh genug vorhanden ist. Beispiel: während nt! IopInitializeBootDrivers. Ein Haltepunkt kann in diesem Kontext so festgelegt werden, dass er beendet wird.
1: kd> g
Breakpoint 0 hit
nt!IopInitializeBootDrivers:
8225c634 8bff mov edi,edi
Der Operator ?? - Befehl, um die Starttreiberstruktur anzuzeigen.
1: kd> ?? LoaderBlock->BootDriverListHead
struct _LIST_ENTRY
[ 0x808c9960 - 0x808c8728 ]
+0x000 Flink : 0x808c9960 _LIST_ENTRY [ 0x808c93e8 - 0x808a2e18 ]
+0x004 Blink : 0x808c8728 _LIST_ENTRY [ 0x808a2e18 - 0x808c8de0 ]
Verwenden Sie das Debugger.Utility.Collections.FromListEntry-Debuggerobjekt, um die Daten unter Verwendung der Startadresse der struktur nt!_LIST_ENTRY anzuzeigen.
1: kd> dx Debugger.Utility.Collections.FromListEntry(*(nt!_LIST_ENTRY *)0x808c9960, "nt!_BOOT_DRIVER_LIST_ENTRY", "Link")
Debugger.Utility.Collections.FromListEntry(*(nt!_LIST_ENTRY *)0x808c9960, "nt!_BOOT_DRIVER_LIST_ENTRY", "Link")
[0x0] [Type: _BOOT_DRIVER_LIST_ENTRY]
[0x1] [Type: _BOOT_DRIVER_LIST_ENTRY]
[0x2] [Type: _BOOT_DRIVER_LIST_ENTRY]
[0x3] [Type: _BOOT_DRIVER_LIST_ENTRY]
[0x4] [Type: _BOOT_DRIVER_LIST_ENTRY]
[0x5] [Type: _BOOT_DRIVER_LIST_ENTRY]
...
Verwenden Sie die Option -g, um eine Rasteransicht der Daten zu erstellen.
dx -r1 -g Debugger.Utility.Collections.FromListEntry(*(nt!_LIST_ENTRY *)0x808c9960, "nt!_BOOT_DRIVER_LIST_ENTRY", "Link")
Anzeigen von Geräten nach Funktion
Anzeigen von Geräten nach Funktion mithilfe des DeviceNodeObject.CapabilityFlags-Objekts.
dx -r1 @$cursession.Devices.DeviceTree.Flatten(n => n.Children).Where(n => (n.DeviceNodeObject.CapabilityFlags & <flag>) != 0)
In dieser Tabelle wird die Verwendung des dx-Befehls mit gängigen Gerätefunktionsflags zusammengefasst.
Wechselmedium |
|
UniqueID |
|
SilentInstall |
|
RawDeviceOk |
|
SurpriseRemovalOK |
|
Weitere Informationen zu den CapabilityFlags finden Sie unter DEVICE_CAPABILITIES.
Siehe auch
dx (Debuggerobjektmodellausdruck anzeigen)
Native Debuggerobjekte in NatVis
Native Debuggerobjekte in JavaScript-Erweiterungen