VS-Ausgabe und Fehlerliste lesen
Der Inhalt des Build-Ausgabefensters und die Error-List lassen sich über das DTE2-Interface programmatisch auslesen. Das ist nützlich für automatisierte Build-Berichte.
Voraussetzungen
- Eine DTE-Verbindung ist aktiv.
C#-Beispiel
using EnvDTE;
using EnvDTE80;
DTE2 dte2 = (DTE2)dte;
// Read build output window
OutputWindow outputWindow = dte2.ToolWindows.OutputWindow;
foreach (OutputWindowPane pane in outputWindow.OutputWindowPanes)
{
if (pane.Name == "Build")
{
TextDocument textDoc = pane.TextDocument;
TextSelection sel = textDoc.Selection;
sel.StartOfDocument(false);
sel.EndOfDocument(true);
string buildOutput = sel.Text;
Console.WriteLine(buildOutput);
break;
}
}
// Read error list
ErrorList errorList = dte2.ToolWindows.ErrorList;
ErrorItems errors = errorList.ErrorItems;
for (int i = 1; i <= errors.Count; i++)
{
ErrorItem item = errors.Item(i);
Console.WriteLine($"[{item.ErrorLevel}] {item.Description} ({item.FileName}:{item.Line})");
}
Das Fehlerlisten-Fenster zeigt Statusmeldungen für verschiedene Funktionen der Visual-Studio-Entwicklungsumgebung und damit auch der TwinCAT-Entwicklungsumgebung an. Jedes ErrorItem-Objekt hat folgende Eigenschaften:
Eigenschaft | Beschreibung |
|---|---|
| Beschreibt die Fehlermeldung. |
| Dateiname, in dem der Fehler aufgetreten ist. |
| Zeile, in der der Fehler aufgetreten ist. |
| Spalte, in der der Fehler aufgetreten ist. |
| Projekt, in dem der Fehler aufgetreten ist. |
![]() |
|
Fehlerliste in PowerShell auslesen
Da PowerShell COM-Objekte über Late Binding anspricht, kann der in C# verwendete direkte Cast von DTE zu DTE2 hier nicht übernommen werden. Dabei gehen Typinformationen verloren, was einen zuverlässigen Cast verhindert. Die empfohlene Lösung kompiliert die C#-Logik zur Laufzeit mit Add-Type direkt in PowerShell und führt sie aus. So wird weiterhin Early Binding gegen die Interop-Assemblies genutzt.
Voraussetzungen
PowerShell 5.x oder 7.x und TwinCAT XAE müssen installiert sein.
Problem und Lösung
Der C#-Weg in PowerShell schlägt fehl, weil PowerShell beim DTE2-Late-Binding die ErrorItems-Collection nicht korrekt auflöst:
# FUNKTIONIERT NICHT (Late Binding Fehler):
# $errors = $dte2.ToolWindows.ErrorList.ErrorItems
Lösung PowerShell 5:
# Locate the Visual Studio installation path using vswhere.exe,
# which ships with every VS installation since 2017
# only useable when VisualStudio is installed
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$installPath = & $vswhere -latest -property installationPath
# For TcXaeShell version 4026+
$installPath = 'C:\Program Files\Beckhoff\TcXaeShell'
# Locate the Microsoft.VisualStudio.Interop assembly dynamically,
# since its exact path varies depending on the VS version and edition
$interopDll = Get-ChildItem "$installPath" -Recurse -Filter "Microsoft.VisualStudio.Interop.dll" | Select-Object -First 1
# Load assemblies via Reflection instead of Add-Type -Path,
# because LoadFrom is more tolerant of missing transitive dependencies
$asmEnvDTE = [System.Reflection.Assembly]::LoadFrom("$installPath\Common7\IDE\PublicAssemblies\EnvDTE.dll")
$asmEnvDTE80 = [System.Reflection.Assembly]::LoadFrom("$installPath\Common7\IDE\PublicAssemblies\EnvDTE80.dll")
$asmInterop = [System.Reflection.Assembly]::LoadFrom($interopDll.FullName)
# Compile a C# helper class at runtime using the loaded assemblies as references.
# This is necessary because PowerShell uses COM late binding, which loses type information
# and prevents a direct cast from DTE to DTE2. C# uses early binding and resolves
# the cast correctly via the 'as' operator and QueryInterface under the hood.
Add-Type -ReferencedAssemblies @(
$asmEnvDTE.Location,
$asmEnvDTE80.Location,
$asmInterop.Location
) -TypeDefinition @"
using System;
using System.Collections.Generic;
using EnvDTE;
using EnvDTE80;
public class ErrorListReader
{
public static List<string[]> GetErrors(object dteObject)
{
var results = new List<string[]>();
// Cast the generic COM object to DTE2 using early binding.
// This is the key step that fails in PowerShell but works correctly in C#.
DTE2 dte2 = dteObject as DTE2;
if (dte2 == null) return results;
// Access the ErrorList via ToolWindows, which is only available on DTE2
ErrorList errorList = dte2.ToolWindows.ErrorList;
ErrorItems items = errorList.ErrorItems;
// Note: COM collections are 1-indexed, not 0-indexed
for (int i = 1; i <= items.Count; i++)
{
ErrorItem item = items.Item(i);
results.Add(new string[] {
item.ErrorLevel.ToString(), // 1 = Error, 2 = Warning, 3 = Message
item.Description,
item.FileName,
item.Line.ToString(),
item.Project
});
}
return results;
}
}
"@
# Get a reference to the running TcXaeShell instance via the Running Object Table (ROT)
$dte = [System.Runtime.InteropServices.Marshal]::GetActiveObject("TcXaeShell.DTE.17.0")
# Call the C# method, passing the raw COM object
$errors = [ErrorListReader]::GetErrors($dte)
# Format and display the results as a PowerShell table
if ($errors.Count -eq 0) {
Write-Host "No errors found."
} else {
$errors | ForEach-Object {
[PSCustomObject]@{
Level = $_[0]
Description = $_[1]
File = $_[2]
Line = $_[3]
Project = $_[4]
}
} | Format-Table -AutoSize
}
![]() | Das Late-Binding-Problem betrifft nur das direkte Auslesen der |
Fehlerliste leeren
Die Fehlerliste kann über den DTE-ExecuteCommand-Mechanismus automatisch geleert werden:
C#:
dte.ExecuteCommand("OtherContextMenus.ErrorList.Clear");PowerShell:
$dte.ExecuteCommand("OtherContextMenus.ErrorList.Clear")