Dieser Artikel wurde maschinell übersetzt.

Debugmodul-API

Schreiben eine Debugtools für Windows-Erweiterung, Teil 2: Ausgabe

Andrew Richards

Downloaden des Codebeispiels

In diesem zweiten Teil meiner Serie über die Debugger-API werde ich Ihnen zeigen, wie Sie von der Erweiterungs-Debugger-Modul (DbgEng) generierte Ausgabe verbessern können. Sie können in einer Anzahl von Traps, obwohl dabei fallen. Ich hoffe, dass alle die fallen für Sie zu markieren.

Vor dem Lesen auf, müssen Sie zum Lesen der letzten Ausgabe zu verstehen, welche eine Debuggererweiterung ist (und ich bin wie erstellen und der Beispiele in diesem Artikel testen). You can read it at msdn.microsoft.com/magazine/gg650659.

Debugger-Markup-Sprache

Debugger Markup Language (DML) ist eine HTML-inspired Markupsprache. Schwerpunkt über kursiv/Fettformatierung/Unterstreichung und Navigation über Hyperlinks unterstützt. DML wurde die Debugger-API in Version 6.6 hinzugefügt.

Das Windows SDK für Windows Vista zunächst diese API-Version 6.6.7.5 geliefert und unterstützt X 86, X 64 und IA64. Windows 7 /.NET 3.5 SDK/WDK geliefert, die nächste Version (Version 6.11.1.404). Windows 7 /.NET 4 SDK/WDK wird die aktuelle Version (Version 6.12.2.633) ausgeliefert. Windows 7 /.NET 4 Release Fahrzeug ist die einzige Möglichkeit, die neueste Version von der Debugging Tools for Windows von Microsoft erhalten. Es ist keine direkten Download von X 86, X 64 oder IA64-Pakete verfügbar. Beachten Sie, dass es sich bei den nachfolgenden Releases erweitern Sie nicht, auf die DML-bezogene APIs, die in Version 6.6 definiert. Sie tun, allerdings lohnenswert Fehlerbehebungen, die im Zusammenhang mit DML-Unterstützung verfügen.

'Hello Dml World'

Wie Sie wahrscheinlich erraten können, ist das Markup von DML zur Hervorhebung verwendet dasselbe Markup wie von HTML verwendet. Verwenden Sie zum Markieren von Text als fett "<b> … </b>"; Kursiv, verwendet "<i> … </i>"; und verwenden Sie für unterstreichen "<u> … </u>". Abbildung 1 zeigt einen Beispielbefehl, der "DML Hallo!" mit dieser drei Arten von Markup ausgibt.

Abbildung 1 !hellodml Implementation

HRESULT CALLBACK 
hellodml(PDEBUG_CLIENT pDebugClient, PCSTR args)
{
  UNREFERENCED_PARAMETER(args);

  IDebugControl* pDebugControl;
  if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugControl), 
    (void **)&pDebugControl)))
  {
    pDebugControl->ControlledOutput(
      DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL,  
      "<b>Hello</b> <i>DML</i> <u>World!</u>\n");
    pDebugControl->Release();
  }
  return S_OK;
}

Um die Erweiterung zu testen, habe ich ein Skript namens test_windbg.cmd im Ordner Example04 der Codedownload für diesen Artikel. Das Skript kopiert die Erweiterung in den Ordner C:\Debuggers_x86. Das Skript dann gestartet WinDbg, die Erweiterung lädt und startet eine neue Instanz der Editor (wie das Debugziel). Wenn alles nach Plan begeben hat, kann ich geben "! Hellodml" im Debuggers in der Eingabeaufforderung, und finden Sie eine Antwort "DML Hallo!" mit fett, kursiv und unterstrichen Markup:

0: 000 > ! Hellodml
Hello DML World!

Ich habe auch ein Skript namens test_ntsd.cmd, hat die gleichen Schritte, die aber lädt der NTSD-Debugger. Wenn ich drücke "! Hellodml" in der Eingabeaufforderung des Debuggers sehen ich die Antwort "DML Hallo!", aber kein Markup. Die DML wird in Text konvertiert, da NTSD einen nur-Text-Debugclient handelt. Alle Markups werden entfernt, wenn DML, um einen Text (nur ausgegeben wird)-basierten Client:

0: 000 > ! Hellodml
DML-Hallo!

Markup

Ähnlich wie bei HTML, müssen Sie zum Starten und Beenden alle Markups sorgfältig vorgegangen werden. Abbildung 2 hat einen einfache Erweiterung-Befehl (! Echoasdml), gibt das Befehlsargument als DML mit Datenpunkten, die vor und nach der DML-Ausgabe als Textausgabe.

Abbildung 2 !echoasdml Implementation

HRESULT CALLBACK 
echoasdml(PDEBUG_CLIENT pDebugClient, PCSTR args)
{
  IDebugControl* pDebugControl;
  if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugControl), 
    (void **)&pDebugControl)))
  {
    pDebugControl->Output(DEBUG_OUTPUT_NORMAL, "[Start DML]\n");
    pDebugControl->ControlledOutput(
      DEBUG_OUTCTL_AMBIENT_DML, 
      DEBUG_OUTPUT_NORMAL, "%s\n", args);
    pDebugControl->Output(DEBUG_OUTPUT_NORMAL, "[End DML]\n");
    pDebugControl->Release();
  }
  return S_OK;
}

In diesem Beispiel wird veranschaulicht, was passiert, wenn Sie Markup schließen nicht-Sequenz:

0: 000 > ! Echoasdml Hello World
[Start DML]
Hello World
[End DML]

0: 000 > ! Echoasdml <b> Hello World </b>
[Start DML]
Hello World
[End DML]
 
0: 000 > ! Echoasdml <b> Hello
[Start DML]
Hello
[End DML]

0: 000 > ! Echoasdml World </b>
[Start DML]
World
[End DML]

Die "<b> Hello" Befehl Blätter fett aktiviert ist, verursachen alle folgenden Erweiterung und Eingabeaufforderung ausgeben als fett angezeigt werden soll. Dies ist unabhängig davon, ob der Text in Text oder DML-Modus ausgegeben wurde. Wie zu erwarten ist, wird das darauffolgende Schließen der fett Markup den Zustand zurückgesetzt.

Ein weiteres häufiges Problem ist, wenn die Zeichenfolge XML-Tags in ihm hat. Die Ausgabe kann entweder abgeschnitten werden, wie im ersten Beispiel geschieht, oder die XML-Tags entfernt werden konnte:

0: 000 > ! Echoasdml < Xml
[Start DML]
 
0: 000 > ! Echoasdml <xml> Hello World </xml>
[Start DML]
Hello World
[End DML]

Sie dies in gleicher Weise behandeln, wie Sie mit HTML: Escape-Zeichenfolge die Zeichenfolge vor der Ausgabe. Sie können selbst tun oder über den Debugger für Sie erledigen. Die vier Zeichen, die Escape-Sequenzen benötigen &, <,> und ". Die gleichbedeutend mit Escapezeichen versehene Versionen sind: "& Amp;", "& Lt;", "& Gt;" und "& Quot;".

Abbildung 3 zeigt ein Beispiel für Escape-Sequenzen.

Abbildung 3 !echoasdmlescape Implementation

HRESULT CALLBACK 
echoasdmlescape(PDEBUG_CLIENT pDebugClient, PCSTR args)
{
  IDebugControl* pDebugControl;
  if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugControl), 
    (void **)&pDebugControl)))
  {
    pDebugControl->Output(DEBUG_OUTPUT_NORMAL, "[Start DML]\n");
    if ((args != NULL) && (strlen(args) > 0))
    {
      char* szEscape = (char*)malloc(strlen(args) * 6);
      if (szEscape == NULL)
      {
        pDebugControl->Release();
        return E_OUTOFMEMORY;
      }
      size_t n=0; size_t e=0;
      for (; n<strlen(args); n++)
      {
        switch (args[n])
        {
          case '&':
                memcpy(&szEscape[e], "&amp;", 5);
                e+=5;
                break;
          case '<':
                memcpy(&szEscape[e], "&lt;", 4);
                e+=4;
                break;
          case '>':
                memcpy(&szEscape[e], "&gt;", 4);
                e+=4;
                break;
          case '"':
                memcpy(&szEscape[e], "'", 6);
                e+=6;
                break;
          default:
                szEscape[e] = args[n];
                e+=1;
                break;
        }
      }
      szEscape[e++] = '\0';
      pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML,  
        DEBUG_OUTPUT_NORMAL, "%s\n", szEscape);
      free(szEscape);
    }
    pDebugControl->Output(DEBUG_OUTPUT_NORMAL, "[End DML]\n");
    pDebugControl->Release();
  }
  return S_OK;
}

Echoasdmlescape-Befehl weist einen neuen Puffer, der bis zu sechs Mal die Größe des Originals ist. Dies ist ausreichend Speicherplatz für die Argumentzeichenfolge mit rein behandeln" Zeichen. Die Funktion durchläuft die Argumentzeichenfolge (die immer ANSI) und fügt den entsprechenden Text in den Puffer. Anschließend verwendet, den Escape-sequenced Puffer mit dem %s-Formatierungsprogramm, das an die IDebugClient::ControlledOutput-Funktion übergeben. Die! Echoasdmlescape Befehl gibt das Argument ohne die Zeichenfolge als DML-Markup interpretiert wird:

0: 000 > ! Echoasdmlescape < Xml
[Start DML]
< Xml
[End DML]
 
0: 000 > ! Echoasdmlescape <xml> Hello World </xml>
[Start DML]
<xml> Hello World </xml>
[End DML]

Beachten Sie, dass mit einigen Zeichenfolgen immer noch nicht die Ausgabe Sie möglicherweise, die Sie erwarten erhalten, die Eingaben zur Verfügung gestellt. Diese Inkonsistenzen keinen nichts zu tun mit der Escape-Sequenzen (oder DML); Sie sind durch den Debugger-Parser verursacht. Notiz in den beiden Fällen sind die " Zeichen (String Content) und das Zeichen ";" (Befehl Beenden):

0: 000 > ! Echoasdmlescape "Hello World"
[Start DML]
Hello World
[End DML]
 
0: 000 > ! Echoasdmlescape Hello World;
[Start DML]
Hello World
[End DML]
 
0: 000 > ! Echoasdmlescape "Hello World";
[Start DML]
Hello World;
[End DML]

Sie verfügen nicht über diese Escape-Sequenzen Bemühungen selbst, obwohl wechseln. Der Debugger unterstützt eine spezielle Formatierungsprogramm für diesen Fall. Statt die Escape sequenced Zeichenfolge und dann mit das Formatierungsprogramm %s, können Sie einfach das Formatierungsprogramm %Y {t} auf die ursprüngliche Zeichenfolge.

Wenn Sie ein Speicher-Formatierungsprogramm verwenden, können Sie auch den Escape-Sequenzen Aufwand vermeiden. Die % Ma, % Mu, Msa % und % Msu Formatierungsprogramme ermöglichen die Ausgabe eine Zeichenfolge direkt aus der Ziel-Adressraum; Debugmoduls behandelt das Lesen der Zeichenfolge und die Anzeige, wie im Abbildung 4.

Abbildung 4 **beim Lesen der Zeichenfolge und der Anzeige von einem Speicher-Formatierungsprogramm **

0: 000 > ! Memorydml test02! g_ptr1
[Start DML]
Error (  %ma): File not found
Error (%Y{t}): File not found
Error (   %s): File not found
[End DML]
 
0: 000 > ! Memorydml test02! g_ptr2
[Start DML]
Error (  %ma): Value is < 0
Error (%Y{t}): Value is < 0
Error (   %s): Value is [End DML]
 
0: 000 > ! Memorydml test02! g_ptr3
[Start DML]
Error (  %ma): Missing <xml> element
Error (%Y{t}): Missing <xml> element
Error (   %s): Missing  element
[End DML]

In der zweiten und dritten Beispiel in Abbildung 4, die Zeichenfolgen mit %s gedruckt werden abgeschnitten oder ausgelassen, da die <und > Zeichen, aber die %Ma und %Y{t} Ausgabe korrekt ist. Die Test02-Anwendung ist in Abbildung 5.

Abbildung 5 Test02-Implementierung

// Test02.cpp : definiert den Einstiegspunkt für die Konsolenanwendung.
//

#include <windows.h>

void* g_ptr1;
void* g_ptr2;
void* g_ptr3;

int main(int argc, char* argv[])
{
  g_ptr1 = "File not found";
  g_ptr2 = "Value is < 0";
  g_ptr3 = "Missing <xml> element";
  Sleep(10000);
  return 0;
}

Die! Memorydml-Implementierung ist im Abbildung 6.

Abbildung 6 !Memorydml Implementierung

HRESULT CALLBACK 
memorydml(PDEBUG_CLIENT pDebugClient, PCSTR args)
{
  IDebugDataSpaces* pDebugDataSpaces;
  if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugDataSpaces), 
    (void **)&pDebugDataSpaces)))
  {
    IDebugSymbols* pDebugSymbols;
    if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugSymbols), 
      (void **)&pDebugSymbols)))
    {
      IDebugControl* pDebugControl;
      if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugControl), 
        (void **)&pDebugControl)))
      {
        // Resolve the symbol
        ULONG64 ulAddress = 0;
        if ((args != NULL) && (strlen(args) > 0) && 
          SUCCEEDED(pDebugSymbols->GetOffsetByName(args, &ulAddress)))
        {   // Read the value of the pointer from the target address space
          ULONG64 ulPtr = 0;
          if (SUCCEEDED(pDebugDataSpaces->
            ReadPointersVirtual(1, ulAddress, &ulPtr)))
          {
            char szBuffer[256];
            ULONG ulBytesRead = 0;
            if (SUCCEEDED(pDebugDataSpaces->ReadVirtual(
              ulPtr, szBuffer, 255, &ulBytesRead)))
            {
              szBuffer[ulBytesRead] = '\0';

              // Output the value via %ma and %s
              pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_TEXT,
                DEBUG_OUTPUT_NORMAL, "[Start DML]\n");
              pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, 
                DEBUG_OUTPUT_ERROR, "<b>Error</b> (  %%ma): %ma\n", ulPtr);
              pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, 
                DEBUG_OUTPUT_ERROR, "<b>Error</b> (%%Y{t}): %Y{t}\n", szBuffer);
              pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, 
                DEBUG_OUTPUT_ERROR, "<b>Error</b> (   %%s): %s\n", szBuffer);
              pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_TEXT, 
                DEBUG_OUTPUT_NORMAL, "[End DML]\n");
            }
          }
        }
        pDebugControl->Release();
      }
      pDebugSymbols->Release();
    }
    pDebugDataSpaces->Release();
  }
  return S_OK;
}

Die Testskripts (im Ordner "Example05") wurden geändert, um ein Abbild der Anwendung Test02 anstelle von Notepad gestartet, so dass Sie eine Zeichenfolge, die Ausgabe zu laden.

Also ist die einfachste Methode zum Implementieren der Anzeige von Zeichenfolgen aus der Ziel-Adressraum % Ma und so weiter verwenden. Wenn Sie eine Zeichenfolge zu manipulieren, die wurde, lesen Sie vor dem anzeigen oder nur eine Zeichenfolge mit Ihren eigenen vorgenommen haben, dann wenden Sie die Escape-Sequenzen über %Y {t}. Wenn Sie die Zeichenfolge als die Format-Zeichenkette übergeben müssen, wenden Sie die Escape-Sequenzierung selbst. Alternativ, teilen Sie die Ausgabe in mehrere Aufrufe von IDebugControl::ControlledOutput, und verwenden Sie die DML-Output-Control (DEBUG_OUTCTL_AMBIENT_DML) einfach auf die DML-Teil des Inhalts, den Rest als TEXT (DEBUG_OUTCTL_AMBIENT_TEXT) mit keine Escape-Sequenzen ausgeben.

Die anderen Einschränkung in diesem Bereich ist die maximale Länge von IDebugClient::ControlledOutput und IDebugClient::Output; Sie können jeweils nur ca. 16.000 Zeichen ausgeben. Ich habe festgestellt, dass ich regelmäßig hat mit ControlledOutput-Limit, erreicht Wenn auf diese Weise DML ausgeben. Das Markup und die Escape-Sequenzen können einfach eine Zeichenfolge innerhalb der letzten 16.000 Zeichen Aufblasen, bei der Suche nach wie vor relativ klein im Ausgabefenster.

Wenn Sie große Zeichenfolgen erstellen und das Escapezeichen Sequenzierung selbst ausführen, müssen Sie sicherstellen, dass Sie die Zeichenfolge an den entsprechenden Punkten schneiden. Nicht zerlegt werden innerhalb der DML-Markup oder eine Escape-Sequenz. Andernfalls wird nicht sie richtig interpretiert werden.

Es gibt zwei Möglichkeiten, einen Hyperlink im Debugger zu erreichen: Sie können <link> verwenden oder <exec> Markup. Der eingeschlossene Text wird in beide Markierungen mit Unterstrich und Hypertext Kolorieren (normalerweise blau) angezeigt. Der Befehl, der ausgeführt wird, in beiden Fällen ist der "Cmd"-Element. Es ähnelt dem "Href" Mitglied der <a> Markup im HTML-Format:

<link cmd="dps @$csp @$csp+0x80">Stack</link>
<exec cmd="dps @$csp @$csp+0x80">Stack</exec>

Der Unterschied zwischen <link> und <exec> kann schwer zu erkennen sein. Tatsächlich, dauerte es einiges an Forschung, um am Ende zu erhalten. Der Unterschied besteht nur überwachbar im Befehl (STRG + N) Browserfenster, nicht das Ausgabefenster (Alt + 1). Beide Arten von Fenster, die Ausgabe des <link> oder <exec> Link wird im zugehörigen Ausgabefenster angezeigt. Der Unterschied ist, was passiert, an der Eingabeaufforderung den Befehl jedes Fensters.

Im Fenster Ausgabe ändert nicht der Inhalt von der Eingabeaufforderung. Wenn es einige nicht ausgeführter Text vorhanden ist, bleibt unverändert.

Im Befehl Browserfenster, wenn ein <link> wird aufgerufen, der Befehl wird zur Liste der Befehle hinzugefügt und der Eingabeaufforderung den Befehl festgelegt ist. Aber wenn ein <exec> wird aufgerufen, der Befehl ist nicht der Liste hinzugefügt und die Eingabeaufforderung wird nicht geändert. Ändern Sie nicht den Befehlsverlauf, ist es möglich, eine Folge von Hyperlinks zu stellen, die den Benutzer über einen Entscheidungsprozess führen können. Das am häufigsten verwendete Beispiel hierfür ist die Anzeige der Hilfe. Die Navigation auf Hilfe eignet sich ideal für Hyperlinking, aber es empfiehlt sich nicht, um die Navigation zu protokollieren.

Benutzereinstellung

Wie wissen Sie, wenn der Benutzer selbst DML anzeigen möchte? In einigen Szenarien können Daten Probleme auftreten, wenn die Ausgabe in Text konvertiert wird es durch die Aktion von Text in eine Protokolldatei (.logopen) speichern oder kopieren und Einfügen aus im Ausgabefenster werden. Daten können aufgrund von DML-Abkürzung verloren, oder die Daten sind möglicherweise aufgrund von die Unfähigkeit, führen Sie die Navigation über die Text-Version der Ausgabe überflüssig.

Ebenso ist es belastend DML-Ausgabe generiert, sollte dieser Aufwand vermieden werden, wenn bekannt ist, dass diese Ausgabe Inhalt konvertiert werden. Langwierige Operationen meistens auf Speicher-Scans und Symbol-Auflösung.

Ebenso könnten Benutzer nur bevorzugen nicht DML in ihrer Ausgabe.

Dabei Abkürzung <link>-basierte, würde nur erhalten Sie "-Objekt finden Sie unter 0x0C876C32" ausgegeben und verpassen die wichtige Information (der Datentyp der Adresse):

Objekt finden Sie unter < link Cmd = "dt Login!CSession 0x0C876C32 "> 0x0C876C32 </link>

Die richtige Vorgehensweise dieses ist eine Bedingung, die die Abkürzung wird vermieden, wenn DML nicht aktiviert ist. Hier ist ein Beispiel, wie diese behoben werden konnte:

if (DML)
  Object found at <link cmd="dt login!CSession 0x0C876C32">0x0C876C32</link>
else
  Object found at 0x0C876C32 (login!CSession)

Die .prefer_dml-Einstellung ist am nächsten Sie eine Benutzereinstellung zugreifen können (damit Sie diese bedingte Entscheidung über abgekürzten oder überflüssige Ausgabe erstellen können). Die Einstellung wird gesteuert, ob der Debugger DML-erweiterte Versionen der integrierten Befehle und Operationen führt standardmäßig verwendet. Obwohl es ausdrücklich vorgesehen ist nicht angeben, ob DML Global (innerhalb des Extensions) verwendet werden soll, ist es ein guter Ersatz.

Der einzige Nachteil von diese Voreinstellung ist, dass die Einstellung deaktiviert standardmäßig, und die meisten debugging Ingenieure nicht kennen, der Befehl .prefer_dml vorhanden ist.

Beachten Sie, dass eine Erweiterung nicht Debugmoduls, um Code zu erkennen, die Einstellung ".prefer_dml" oder die "Möglichkeiten" ("Möglichkeit" in Kürze werde ich) haben. Debugmoduls wird nicht die DML-Ausgabe für Sie auf Grundlage dieser Einstellung entfernen; Wenn der Debugger DML-fähig ist, wird es in DML unabhängig ausgeben.

Um die aktuelle Einstellung für ".prefer_dml" zu erhalten, müssen Sie ein QueryInterface für die übergebene IDebugClient-Schnittstelle für die IDebugControl-Schnittstelle verfügen. Dann verwenden Sie die GetEngineOptions-Funktion, um die aktuelle DEBUG_ENGOPT_XXX-Bitmaske zu erhalten. Wenn das Bit DEBUG_ENGOPT_PREFER_DML festgelegt ist, wird die .prefer_dml aktiviert. Abbildung 7 hat eine Beispielimplementierung von einer Funktion (Voreinstellung).

Abbildung 7 PreferDML-Implementierung

BOOL PreferDML(PDEBUG_CLIENT pDebugClient)
{
  BOOL bPreferDML = FALSE;
  IDebugControl* pDebugControl;
  if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugControl), 
    (void **)& pDebugControl)))
  {
    ULONG ulOptions = 0;
    if (SUCCEEDED(pDebugControl->GetEngineOptions(&ulOptions)))
    {
      bPreferDML = (ulOptions & DEBUG_ENGOPT_PREFER_DML);
    }
  pDebugControl->Release();
  }
  return bPreferDML;
}

Sie können denken, dass Sie nicht, rufen Sie die GetEngineOptions-Funktion in jedem Befehl, um die Einstellung zu bestimmen möchten. Können wir der Änderung werden benachrichtigt? Nachdem alle ändert es wahrscheinlich sehr häufig sich nicht. Ja, Sie können vorteilhafter sein, jedoch einen Haken.

Sie jedoch können eine IDebugEventCallbacks-Implementierung über IDebugClient::SetEventCallbacks registrieren. In der Implementierung registrieren Sie ein Interesse an der DEBUG_EVENT_CHANGE_ENGINE_STATE-Benachrichtigung. Wenn IDebugControl::SetEngineOptions aufgerufen wird, ruft der Debugger IDebugEventCallbacks::ChangeEngineState mit der DEBUG_CES_ENGINE_OPTIONS-Bit im Flags-Parameter festgelegt. Der Argument-Parameter enthält eine Bitmaske DEBUG_ENGOPT_XXX wie GetEngineOptions zurückgibt.

Die anzugebende Fangmenge ist, dass die Rückruffunktion nur ein Ereignis für ein IDebugClient Objekt gleichzeitig registriert werden kann. Wenn zwei (oder mehr) Erweiterungen für Ereignisrückrufe (einschließlich wichtigere Benachrichtigungen wie z. B. Modul laden/entladen, Thread starten oder beenden, starten oder Beenden des Prozesses und Ausnahmen) registrieren, wird Personen nicht entgehen. Und wenn Sie das übergebene IDebugClient-Objekt ändern, werden einer Person der Debugger!

Wenn Sie den IDebugEventCallbacks-Rückruf implementieren möchten, müssen Sie Ihre eigenen IDebugClient-Objekt über IDebugClient::CreateClient zu machen. Anschließend ordnen Sie den Rückruf dieses (neuen) IDebugClient-Objekts und verantwortlich für die Lebensdauer der IDebugClient geworden.

Aus Gründen der Einfachheit ist Sie besser aufrufen GetEngineOptions jedes Mal, wenn Sie den DEBUG_ENGOPT_PREFER_DML-Wert zu ermitteln müssen. Wie bereits erwähnt, sollten Sie rufen Sie QueryInterface für die übergebene IDebugClient-Schnittstelle für die IDebugControl-Schnittstelle, und rufen Sie GetEngineOptions, um sicherzustellen, dass Sie die aktuelle (und richtige) (Voreinstellung) haben.

Möglichkeit des Debugclients

Wie wissen Sie, wenn der Debugger sogar DML unterstützt?

Wenn der Debugger keine DML unterstützt, können Daten verloren gehen, überflüssig werden, oder der Aufwand belastend, ähnlich wie die Benutzereinstellung handeln. Wie bereits erwähnt, ist NTSD ein nur-Text-Debugger, und wenn DML darauf ausgegeben wird, Debugmoduls Inhaltskonvertierung DML aus der Ausgabe entfernen.

Wenn auf dem Clientcomputer Fähigkeit erhalten möchten, müssen Sie ein QueryInterface für die übergebene IDebugClient-Schnittstelle für die IDebugAdvanced2-Schnittstelle zu tun. Anschließend verwenden Sie die Funktion der Anforderung mit der DEBUG_REQUEST_CURRENT_OUTPUT_CALLBACKS_ARE_DML_AWARE-Anforderungstyp. HRESULT enthält S_OK zurück, wenn mindestens ein Ausgabe-Rückruf DML-kompatibel ist, andernfalls gibt S_FALSE zurück. To reiterate, the flag doesn’t mean that all callbacks are aware; it means that at least one is.

In scheinbar nur-Text-Umgebungen (wie NTSD) können Sie immer noch in die Probleme bei der bedingten Ausgabe erhalten. Wenn eine Erweiterung einen Ausgabe-Callback registriert hat, der DML-aware ist (durch Zurückgeben von DEBUG_OUTCBI_DML oder DEBUG_OUTCBI_ANY_FORMAT aus IDebugOutputCallbacks2::GetInterestMask) innerhalb von NTSD, wird es die Anforderung Funktion S_OK zurückgegeben wird. Diese Erweiterungen sind zum Glück sehr selten. Wenn sie vorhanden sind, sie sollten werden überprüft den Status der DEBUG_REQUEST_CURRENT_OUTPUT_CALLBACKS_ARE_DML_AWARE und ihrer Fähigkeit entsprechend festlegen (vor, ihre Äußerung ihrer DML-Fähigkeit). Überprüfen Sie den nächsten Teil dieser Reihe Weitere Informationen zu DML-aware-Rückrufe.

Abbildung 8 hat eine Beispielimplementierung einer Fähigkeit-Funktion.

Abbildung 8 AbilityDML-Implementierung

BOOL AbilityDML(PDEBUG_CLIENT pDebugClient)
{
  BOOL bAbilityDML = FALSE;
  IDebugAdvanced2* pDebugAdvanced2;
  if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugAdvanced2), 
    (void **)& pDebugAdvanced2)))
  {
    HRESULT hr = 0;
    if (SUCCEEDED(hr = pDebugAdvanced2->Request(
      DEBUG_REQUEST_CURRENT_OUTPUT_CALLBACKS_ARE_DML_AWARE, 
      NULL, 0, NULL, 0, NULL)))
    {
      if (hr == S_OK) bAbilityDML = TRUE;
    }
    pDebugAdvanced2->Release();
  }
  return bAbilityDML;
}

Beachten Sie, dass die DEBUG_REQUEST_CURRENT_OUTPUT_CALLBACKS_ARE_DML_AWARE-Anforderungstyp und IDebugOutputCallbacks2-Schnittstelle noch in der MSDN-Bibliothek dokumentiert werden nicht.

Lassen Sie potenziellen Defiziten, denken Sie daran, ist die beste Möglichkeit bevorzugt und Client-Fähigkeit von Benutzern verarbeiten:

if (PreferDML(IDebugClient) && AbilityDML(IDebugClient))
  Object found at <link cmd="dt login!CSession 0x0C876C32">0x0C876C32</link>
else
  Object found at 0x0C876C32 (login!CSession)

Die! Ifdml-Implementierung (in Abbildung 9) zeigt die Funktionen PreferDML und AbilityDML in Aktion, so dass bedingte DML-Ausgabe generiert wird. Beachten Sie, dass in der Mehrzahl der Fälle, besteht keine Notwendigkeit eine bedingte Anweisung wie dieser haben; Sie können problemlos auf die Debugger-Engine-Inhaltskonvertierung verlassen.

Abbildung 9 !ifdml Implementierung

HRESULT CALLBACK 
ifdml(PDEBUG_CLIENT pDebugClient, PCSTR args)
{
  UNREFERENCED_PARAMETER(args);

  PDEBUG_CONTROL pDebugControl;
  if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugControl), 
    (void **)&pDebugControl)))
  {
    // A condition is usually not required;
    // Rely on content conversion when there isn't 
    // any abbreviation or superfluous content
    if (PreferDML(pDebugClient) && AbilityDML(pDebugClient))
    {
      pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, 
        DEBUG_OUTPUT_NORMAL, "<b>Hello</b> <i>DML</i> <u>World!</u>\n");
    }
    else
    {
      pDebugControl->ControlledOutput(
        DEBUG_OUTCTL_AMBIENT_TEXT, DEBUG_OUTPUT_NORMAL, 
        "Hello TEXT World!
\n");
    }
    pDebugControl->Release();
  }
  return S_OK;
}

Unter Verwendung des test_windbg.cmd-Testskripts WinDbg, die Ausgabe des geladen! Ifdml ist:

0: 000 > .prefer_dml 0
DML-Versionen der Befehle deaktiviert standardmäßig
0: 000 > ! Ifdml
TEXT Hallo!

0: 000 > .prefer_dml 1
DML-Versionen in der Standardeinstellung auf Befehle
0: 000 > ! Ifdml
Hello DML World!

Unter Verwendung des test_ntsd.cmd-Testskripts NTSD, die Ausgabe des geladen! Ifdml ist:

0: 000 > .prefer_dml 0
DML-Versionen der Befehle deaktiviert standardmäßig
0: 000 > ! Ifdml
TEXT Hallo!
 
0: 000 > .prefer_dml 1
DML-Versionen in der Standardeinstellung auf Befehle
0: 000 > ! Ifdml
TEXT Hallo!

Kontrollierte Ausgabe

Wenn DML ausgegeben werden soll, müssen Sie die IDebugControl::ControlledOutput-Funktion verwenden:

HRESULT ControlledOutput(
  [in]  ULONG OutputControl,
  [in]  ULONG Mask,
  [in]  PCSTR Format,
         ...
);

Der Unterschied zwischen ControlledOutput und Ausgabe ist der OutputControl-Parameter. Dieser Parameter basiert auf der DEBUG_OUTCTL_XXX-Konstanten. Es gibt zwei Teile an diesen Parameter: die niederwertigen Bits den Umfang der Ausgabe und der höheren Bit darstellen die Optionen. Es ist etwas höher, die DML ermöglicht.

Ein – und nur ein – von der DEBUG_OUTCTL_XXX müssen bereichsbasierte Konstanten niederwertigen Bits verwendet werden. Der Wert leitet, in denen die Ausgabe ist, zu gelangen. Dies kann für alle Debugger-Clients (DEBUG_OUTCTL_ALL_CLIENTS), nur im Zusammenhang mit der IDebugControl-Schnittstelle (DEBUG_OUTCTL_THIS_CLIENT), alle anderen Clients (DEBUG_OUTCTL_ALL_OTHER_CLIENTS), nie an alle (DEBUG_OUTCTL_IGNORE) oder nur die Protokolldatei (DEBUG_OUTCTL_LOG_ONLY) IDebugClient sein.

Die höheren Bits entsprechen einer Bitmaske und auch in der DEBUG_OUTCTL_XXX-Konstanten definiert sind. Es gibt Konstanten an, dass Text oder DML-basierte Ausgabe (DEBUG_OUTCTL_DML), wenn die Ausgabe nicht protokollierte (DEBUG_OUTCTL_NOT_LOGGED) und gibt an, ob ein Client-Ausgabe-Maske ist (DEBUG_OUTCTL_OVERRIDE_MASK) ausgezeichnet.

Ausgabe-Steuerung

In den Beispielen habe ich den ControlledOutput-Parameter auf DEBUG_OUTCTL_AMBIENT_DML festgelegt. Lesen die Dokumentation auf MSDN, man kann sagen, dass ich auch DEBUG_OUTCTL_ALL_CLIENTS verwendet haben, konnte | DEBUG_OUTCTL_DML. Dies jedoch wouldn't die IDebugControl Ausgabe Steuerelement bevorzugt berücksichtigt werden.

Wenn der Befehl für die Erweiterung von IDebugControl::Execute aufgerufen wurde, sollte der OutputControl-Parameter der Execute-Aufruf für alle verwandten Ausgabe verwendet werden. IDebugControl::Output tut dies grundsätzlich, aber bei der Verwendung von IDebugControl::ControlledOutput ist die Verantwortung für die Kenntnis des OutputControl-Wert des Aufrufers. Das Problem ist, dass es keine Möglichkeit, tatsächlich den aktuellen Ausgabewert für die Steuerung von der IDebugControl-Schnittstelle abzurufen (oder eine beliebige andere Schnittstelle). Ist nicht alles verloren, jedoch; Es gibt spezielle "ambient" DEBUG_OUTCTL_XXX-Konstanten, behandeln das Umschalten des DEBUG_OUTCTL_DML-Bits. Wenn Sie eine ambient-Konstante verwenden, die aktuelle Ausgabe-Steuerung wird berücksichtigt, und das DEBUG_OUTCTL_DML-Bit wird entsprechend festgelegt.

Anstatt übergeben eine der unteren Konstanten mit der höheren DEBUG_OUTCTL_DML-Konstante, übergeben Sie einfach DEBUG_OUTCTL_AMBIENT_DML DML-Ausgabe zu aktivieren, oder DEBUG_OUTCTL_AMBIENT_TEXT So deaktivieren Sie die DML-Ausgabe:

pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, ...);

Der Maskenparameter

Ein anderer Parameter, für den ich in den Beispielen festgelegt wurden, haben ist der Mask-Parameter. Sie sollten eine entsprechende DEBUG_OUTPUT_XXX-Konstante, die basierend auf den Text, der ausgegeben wird die Maske festlegen. Beachten Sie, dass der Mask-Parameter auf die DEBUG_OUTPUT_XXX-Konstanten basiert; Dies ist nicht zu verwechseln mit der DEBUG_OUTCTL_XXX-Konstanten sein.

Die am häufigsten verwendeten Werte, die Sie verwenden würden, sind DEBUG_OUTPUT_NORMAL für normale (Allgemein)-Ausgabe, DEBUG_OUTPUT_WARNING für die Ausgabe der Warnung und DEBUG_OUTPUT_ERROR für die Fehlerausgabe. Verwenden Sie DEBUG_OUTPUT_EXTENSION_WARNING, wenn die Erweiterung ein Problem hat.

Die DEBUG_OUTPUT_XXX Ausgabe Flags ähneln Stdout und Stderr mit Konsolenausgabe verwendet. Jede Ausgabe-Flag ist eine einzelne Ausgabekanal. Es liegt an den Empfänger (Rückrufe) zu entscheiden, welche der folgenden Kanäle Gehör an, wie sie kombiniert werden, sind (wenn es alle) und wie sie sind, angezeigt werden soll. Beispielsweise zeigt WinDbg standardmäßig alle Ausgabe-Flags, mit Ausnahme der DEBUG_OUTPUT_VERBOSE-Ausgabe-Flag im Ausgabefenster. Sie können dieses Verhalten über Ansicht umschalten | Ausführliche Ausgabe (Strg + Alt + V).

Die! Maskdml-Implementierung (finden Sie unter Abbildung 10) gibt eine Beschreibung, die mit dem Flag zugeordnete Ausgabe formatiert.

Abbildung 10 !maskdml Implementierung

HRESULT CALLBACK 
maskdml(PDEBUG_CLIENT pDebugClient, PCSTR args)
{
  UNREFERENCED_PARAMETER(args);

  PDEBUG_CONTROL pDebugControl;
  if (SUCCEEDED(pDebugClient->QueryInterface(__uuidof(IDebugControl), 
    (void **)&pDebugControl)))
  {
    pDebugControl->ControlledOutput(
      DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_NORMAL, 
      "<b>DEBUG_OUTPUT_NORMAL</b> - Normal output.
\n");
    pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, 
      DEBUG_OUTPUT_ERROR, "<b>DEBUG_OUTPUT_ERROR</b> - Error output.
\n");
    pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, 
      DEBUG_OUTPUT_WARNING, "<b>DEBUG_OUTPUT_WARNING</b> - Warnings.
\n");
    pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, 
      DEBUG_OUTPUT_VERBOSE, "<b>DEBUG_OUTPUT_VERBOSE</b> 
      - Additional output.
\n");
    pDebugControl->ControlledOutput(
      DEBUG_OUTCTL_AMBIENT_DML, DEBUG_OUTPUT_PROMPT, 
      "<b>DEBUG_OUTPUT_PROMPT</b> - Prompt output.
\n");
    pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, 
      DEBUG_OUTPUT_PROMPT_REGISTERS, "<b>DEBUG_OUTPUT_PROMPT_REGISTERS</b> 
      - Register dump before prompt.
\n");
    pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, 
      DEBUG_OUTPUT_EXTENSION_WARNING, 
      "<b>DEBUG_OUTPUT_EXTENSION_WARNING</b> 
      - Warnings specific to extension operation.
\n");
    pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, 
      DEBUG_OUTPUT_DEBUGGEE, "<b>DEBUG_OUTPUT_DEBUGGEE</b> 
      - Debug output from the target (for example, OutputDebugString or  
      DbgPrint).
\n");
    pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML,  
      DEBUG_OUTPUT_DEBUGGEE_PROMPT, "<b>DEBUG_OUTPUT_DEBUGGEE_PROMPT</b> 
      - Debug input expected by the target (for example, DbgPrompt).
\n");
    pDebugControl->ControlledOutput(DEBUG_OUTCTL_AMBIENT_DML, 
      DEBUG_OUTPUT_SYMBOLS, "<b>DEBUG_OUTPUT_SYMBOLS</b> 
      - Symbol messages (for example, !sym noisy).
\n");
    pDebugControl->Release();
  }
  return S_OK;
}

Wenn Sie ausführliche Ausgabe, wechseln nachdem der Befehl ausgeführt wurde, werden die ausgelassene DEBUG_OUTPUT_VERBOSE Ausgabe wird nicht angezeigt. die Ausgabe geht verloren.

WinDbg unterstützt die farbliche Kennzeichnung jedes Ausgabe-Flags. In der Ansicht | Dialogfeld "Optionen" können Sie eine Farbe für Vorder- und Hintergrundfarben für jede Ausgabe-Flag angeben. Die Farbeinstellungen werden im Arbeitsbereich gespeichert. Wenn sie global festlegen möchten, starten Sie WinDbg, alle Arbeitsbereiche löschen, legen die Farben (und andere Einstellung, die Sie möchten), und speichern Sie den Arbeitsbereich. Ich mag die Vordergrundfarbe des Fehlers auf Rot, Warnung, Grün, Blau Verbose, Erweiterung Warnung festgelegt in Lila und Symbole in Grau. Der Standard-Arbeitsbereich werden die Vorlage für alle zukünftigen Debugsitzungen.

Abbildung 11 zeigt die Ausgabe des! Maskdml ohne (oben) und mit (unten) ausführliche aktiviert.

!maskdml mit Farbschema

Abbildung 11 !maskdml with Color Scheme

Break!

Es ist einfach, Verlängerungskabel mit DML zu verbessern. Und mit ein wenig Infrastrukturcode, ist es auch einfach die Benutzereinstellung abwickeln. Es ist auf jeden Fall lohnt einige zusätzliche Zeit Ausgabe korrekt generiert. Insbesondere streben danach, immer beide Text und DML-basierte Ausgabe bereit, wenn die Ausgabe abgekürzten oder überflüssig ist, und leiten Sie diese Ausgabe entsprechend.

Im nächsten Artikel über die Debugger-Modul-API werde ich noch tiefer in die Beziehung einzusteigen, die mit dem Debugger eine Debuggererweiterung haben kann. Ich erhalten Sie eine Übersicht über die Architektur des Debugger-Clients und Debugger-Rückrufe. Auf diese Weise erhalten wir alles Wesentliche der DEBUG_OUTPUT_XXX und DEBUG_OUTCTL_XXX-Konstanten sein.

Ich werde diese Grundlage verwenden, um eine Kapselung von Debuggererweiterung Sohn der Strike oder SOS, implementieren. Ich verbessern die SOS-Ausgabe mit DML und veranschaulichen, wie die integrierten Debugger-Befehle und andere Erweiterungen zum Abrufen von Informationen von Ihren Erweiterungen benötigt genutzt werden können.

Wenn Sie gerade debuggen und mehr erfahren möchten, interessieren, sollten Sie den Blog "Erweiterte Windows-Debuggen und Problembehandlung" (NTDebugging) unter blogs.msdn.com/b/ntdebugging—Es gibt eine Vielzahl von Schulungen und Case Study-Artikel zu lesen.

Microsoft sucht immer nach talentierten debugging-Ingenieure. If you’re interested in joining the team, the job title to search for is “Escalation Engineer” at Microsoft Careers (careers.microsoft.com).

Andrew Richards ist Microsoft senior Escalation Engineer für Exchange Server. Er begeistert sich für Support-Tools und ist ständig Debugger erstellen, Erweiterungen und Anwendungen, die die Auftrag von Supporttechnikern zu vereinfachen.

Dank an den folgenden technischen Experten für die Überprüfung dieses Artikels: Drew Bliss