Kurz gesagt: Mein Profiler arbeitet mit einer temporären Kopie des VB6-Projekts, fügt dort automatisch Messpunkte ein und zeigt dir live, wo Laufzeit und Windows-Ressourcen hängen bleiben. Dein Originalcode bleibt dabei unangetastet.
Auf einen Blick
- Ganze Projekte und Projektgruppen einlesen
- Feingranulare Überwachungskonfiguration
- Automatische Instrumentierung im Detail
- Wenn der Profiler lügen würde: die Select-Case-Falle
- Präzise Zeitmessung mit Windows-Bordmitteln
- Live mitschauen, während das Programm läuft
- Hotspots: Wo geht die Zeit hin?
- Session-Übersicht: Auswerten auch nach dem Stopp
- Das KI-Datenpaket: Messwerte und Quellcode gemeinsam auswerten
- Eingebetteter Quellcode-Viewer
- Ressourcen im Blick, nicht nur Zeit
- Fehler nicht verpassen
- Persistenz & schneller Workflow

Das Problem
Visual Basic 6 hat bis heute keinen eingebauten Profiler. Wenn du in einer Anwendung den Zeitfresser suchst, bleibt ohne eigenes Werkzeug oft nur Handarbeit: Debug.Print Timer vor und nach verdächtigen Stellen. Für eine einzelne Methode reicht das, in einem großen Projekt wird es schnell mühsam.
Ich wollte deshalb ein externes Werkzeug, das ein bestehendes VB6-Projekt analysiert und Messpunkte einfügt, ohne die Originaldateien anzutasten. Der Profiler arbeitet dafür mit einer temporären Projektkopie.
Die Idee: Automatisch instrumentieren, ohne das Original anzufassen
Der VB6 Profiler ist eine eigenständige VB.NET-Anwendung, die ein bestehendes VB6-Projekt (oder eine ganze Projektgruppe) einliest, versteht, welche Methoden und Zeilen es enthält — und bei Bedarf eine temporäre Kopie davon anlegt, in die automatisch Zeitmess-Code eingefügt wird. Das Original-Projekt wird zu keinem Zeitpunkt verändert; alle Eingriffe passieren ausschließlich in einer Wegwerf-Kopie im Temp-Verzeichnis. Man wählt im Konfigurator einfach aus, was überwacht werden soll — ein ganzes Projekt, eine einzelne Datei, eine einzelne Methode oder sogar einzelne Zeilen darin — und der Profiler kümmert sich um den Rest: Kopie anlegen, Profiler-Code einfügen, Zeilen instrumentieren, starten.

Was der Profiler kann
Ganze Projekte und Projektgruppen einlesen
.vbp– und .vbg-Dateien werden vollständig geparst — inklusive aller Module, Formulare und Klassen, deren Methoden (Sub/Function/Property Get/Set/Let) und sogar der einzelnen Quellcodezeilen darin. Der Projektbaum gruppiert nach Dateityp (Formulare, Module, Klassenmodule, UserControls), damit man in großen Projekten nicht in einer flachen Liste versinkt. Gesucht wird über eine UND-verknüpfte Mehrzeilen-Suche mit Spaltenfiltern: jede Zeile ein Kriterium (Projekt, Datei, Methode oder direkt Quellcode-Inhalt), der Suchbereich wächst automatisch mit, und auf Wunsch werden nur bereits überwachte Elemente angezeigt.
Feingranulare Überwachungskonfiguration
Überwachung lässt sich exakt dosieren: ein komplettes Projekt, eine einzelne Datei, eine einzelne Methode — oder, wenn’s genau sein muss, zeilenbasiert bis auf einzelne Codezeilen herunter. Jeder Knoten im Baum hat eine eigene Checkbox. Wer eine ganze Verdächtigen-Liste hat (zum Beispiel aus einer vorherigen Analyse), fügt sie einfach als Text ins Kontextmenü ein — alle genannten Methoden werden auf einen Schlag angehakt, nicht gefundene Namen werden gemeldet.
Zusätzlich wählt man vor dem Start, welche Kennzahlen überhaupt erfasst werden: Die Dauer ist immer dabei, GDI-Handles, USER-Objekte und deren Peak-Werte sind einzeln zuschaltbar. Eine abgewählte Kennzahl wird beim Generieren des Messcodes gar nicht erst eingebaut und verursacht dann auch keinen API-Aufruf.
Die komplette Konfiguration wird pro Projekt in schlichten JSON-Dateien gespeichert — diffbar, von Hand reparierbar, beim nächsten Start ist alles wieder genau so eingestellt wie zuletzt.

Automatische Instrumentierung im Detail
Beim Start des Profilers passiert Folgendes automatisch, ohne manuellen Eingriff: Das komplette Projekt (bzw. die Projektgruppe) wird in einen Temp-Ordner kopiert, ein fertiges Profiler-Modul und eine Profiler-Klasse werden in jedes betroffene Projekt eingefügt, und in jeder überwachten Methode werden Start-/Ende-Aufrufe eingebaut — bei zeilenbasierter Überwachung vor jeder einzelnen markierten Zeile. Anschließend übernimmt VB6 selbst das Kompilieren/Ausführen dieser Kopie ganz normal, nur dass sie jetzt „mitschreibt“.
Wenn der Profiler lügen würde: die Select-Case-Falle
Die wichtigste Eigenschaft eines Instrumentierers ist nicht Geschwindigkeit, sondern Unsichtbarkeit: Das vermessene Programm muss sich exakt so verhalten wie ohne Messung. Wie schnell das kippen kann, zeigt eine Falle, in die die zeilenbasierte Instrumentierung anfangs getappt ist. Case-Zeilen wurden nach diesem Muster instrumentiert:
' vorher:
Case 5
' instrumentiert:
Case Not Profiler.StartZeile(4711, True), 5
Sieht harmlos aus: Not True ergibt False, und False matcht ja nichts … außer, der Select-Ausdruck ist selbst 0, False oder ein Leerstring. Dann matcht plötzlich der eingeschleuste Vergleichswert — und die Ausführung landet im falschen Zweig. Bewiesen habe ich das mit einem Mini-Testprojekt, das die Instrumentierung von Hand nachstellt (Select Case 0, Select Case False, Select Case "" — alle drei kippen). Die Konsequenz: Case-Zeilen mit Werteliste werden bewusst nicht instrumentiert — die erste Zeile im Rumpf des Zweigs übernimmt die Messung. Der Informationsverlust ist minimal, die Semantik bleibt garantiert unangetastet.
Präzise Zeitmessung mit Windows-Bordmitteln
Gemessen wird mit voller Genauigkeit und erst für die Anzeige gerundet. Der Overhead des Profilers wird automatisch herausgerechnet; rekursive Aufrufe zählen in Methodensummen nicht doppelt. Schleifen lassen sich jetzt genauer lesen: Neben der aufsummierten Zeilenzeit zeigt die Aufrufliste auch die Dauer eines einzelnen Durchlaufs. Bei Bedarf kann der Profiler GDI- und USER-Handles weiterhin je Zeile erfassen; standardmäßig geschieht das an Methodengrenzen, damit die Messpunkte schneller bleiben.
Live mitschauen, während das Programm läuft
Sobald der Profiler gestartet ist, laufen die instrumentierten Methodenaufrufe in Echtzeit ins Tool — mit Dauer, Aufrufzahl und einer klaren Unterscheidung zwischen „läuft noch“ und „abgeschlossen“. Über eine Schnellleiste blendet man ganze Spaltengruppen ein und aus (Dauer, Gruppierung, Aufrufe, Analyse, GDI, USER) und holt sich genau die Kennzahlen ins Bild, die gerade interessieren: prozentualer Anteil, Eigenzeit (ohne Kinder), Spannweite, Kinderzahl, Aufruftiefe, Durchschnittswerte, Peaks. Die zweizeiligen, nach Gruppen pastellig eingefärbten Spaltenköpfe halten das Ganze trotz Spaltenmenge lesbar.
Auch bei extremen Datenmengen bleibt das UI bedienbar: Große Aufruflisten werden asynchron geladen, und gerät die Auswertung bei sehr vielen Logs in Rückstand, zeigt die Statusleiste die aktuelle Auswerterate in Logs/s — man sieht, dass das Tool arbeitet, statt zu rätseln, ob es hängt.

Hotspots: Wo geht die Zeit hin?
Die wichtigste Frage beim Profiling ist selten „was ist alles passiert?“, sondern fast immer „wo steckt die Zeit?“. Die Hotspot-Ansicht beantwortet sie direkt: Gleichartige Aufrufe werden zusammengefasst und als Treemap (Fläche = Zeit) oder Balkenliste dargestellt, mit Summendauer, prozentualem Anteil und einstellbarer Gruppenanzahl von 5 bis 100. Statt sich durch einen Aufrufbaum zu hangeln, sieht man auf einen Blick, welche drei Methoden 90 % des Laufs ausmachen.

Session-Übersicht: Auswerten auch nach dem Stopp
Die Session-Übersicht (Taste F6) zeigt kumulierte Kennzahlen je Methode oder Zeile über die gesamte Profiling-Session — darunter Eigenzeit, Gesamtzeit, Aufrufe, Durchschnitt, längster Einzelaufruf und GDI-/USER-Werte. Suche, Dateifilter und Mindestanteil helfen beim Eingrenzen; die Live-Aktualisierung arbeitet nur bei Änderungen, alternativ aktualisiert F5. Nach dem Stoppen lassen sich die Ergebnisse weiter auswerten. Neben CSV kann die Session jetzt auch als KI-Datenpaket exportiert werden.

Das KI-Datenpaket: Messwerte und Quellcode gemeinsam auswerten
Das KI-Datenpaket bündelt ein Markdown-Briefing und eine SQLite-Datenbank mit den Profiling-Ergebnissen. Die Datenbank enthält jede einzelne Ausführung mit Aufrufbeziehungen, Laufzeiten und erfassten Handles sowie den vollständigen Original-Quelltext der beteiligten Dateien. Fertige Ansichten erschließen Zeilen- und Methoden-Hotspots, Schleifen, den Aufrufgraphen, annotierten Quellcode und weitere Zusammenhänge. Exportiert werden kann eine ganze Session, eine Aufrufliste oder eine Auswahl von Aufrufen samt Unteraufrufen. Ein Fortschrittsdialog begleitet den Export und zeigt die einzelnen Arbeitsschritte; bei Abbruch werden unvollständige Dateien entfernt. Das Briefing erklärt das Messmodell und Datenbankschema und enthält passende SQL-Abfragen für den direkten Einstieg in die Analyse.
Eingebetteter Quellcode-Viewer
Wählt man einen Aufruf aus, zeigt der Quellcode-Viewer sofort die passende Stelle im Code — wahlweise eingebettet im Hauptfenster, in einem eigenen, immer sichtbaren Extra-Fenster, oder ganz ausgeblendet, wenn gerade nur die reinen Zahlen interessieren. So lässt sich eine auffällig lange Dauer sofort mit dem tatsächlichen Code dahinter abgleichen, ohne zwischen VB6-IDE und Profiler hin- und herzuwechseln.

Ressourcen im Blick, nicht nur Zeit
Zusätzlich zur Laufzeit erfasst der Profiler auf Wunsch GDI-Handles, USER-Objekte und ihre Spitzenwerte. Standardmäßig werden die Werte an Methodenanfang und -ende abgefragt; mit der Option „Handles je Zeile“ geht die Messung bis auf Quellzeilenebene. So bleibt die schnelle Standardmessung schlank, während sich ein vermuteter Ressourcenanstieg gezielt genauer eingrenzen lässt. Nicht aktivierte Kennzahlen werden gar nicht erst in den erzeugten Messcode aufgenommen.
Fehler nicht verpassen
Unbehandelte Exceptions der profilierten Anwendung landen automatisch in einer eigenen Fehlerliste — inzwischen inklusive ThreadException und UnobservedTaskException, mit farbig dargestellter, vollständiger Exception-Kette samt Stacktrace. Die Liste überlebt Projektwechsel und Neustart, und alles wird zusätzlich als NDJSON-Log auf Platte gesichert. Praktisch, wenn ein Absturz genau während einer Profiling-Sitzung passiert und man sonst Gefahr liefe, ihn im Log-Rauschen zu übersehen.
Persistenz & schneller Workflow
Konfiguration in JSON, Fenster- und Splitterpositionen (auch verschachtelt) werden gespeichert und wiederhergestellt — beim nächsten Start ist alles wieder genau so wie zuletzt. Dazu ein auf Tastatur-Bedienung ausgelegter Workflow für den täglichen Einsatz:
| Taste | Aktion |
|---|---|
Strg+F | Fokus ins Suchfeld (Projekt/Datei) |
Strg+O | VB6-Projekt/Konfiguration auswählen |
F3 | Projekt im Konfigurator in VB6 öffnen |
F4 | Profiler starten/neu starten |
F5 | Instrumentierte Projektkopie in VB6 öffnen |
F6 | Session-Übersicht öffnen |
Esc | Profiler stoppen |
Entf | Methode von der Überwachung ausschließen |
Qualitätssicherung: der Hindernislauf
Die Select-Case-Falle hat eine Konsequenz hinterlassen: Der Profiler hat ein eigenes Folter-Testprojekt bekommen. „Hindernislauf“ ist ein absichtlich fieses VB6-Programm, das so ziemlich alles enthält, woran eine automatische Instrumentierung scheitern kann:
GoTovorwärts und rückwärts über tote Codeblöcke,GoSub/Return,On x GoSub-Verteiler- nummerierte Zeilen mit
Erl-Auswertung und allen dreiResume-Varianten (Resume,Resume Next,Resume <Ziel>) - Einzeiler-Ifs mit Doppelpunkt und
Else,IIfmit Seiteneffekten, Fortsetzungszeilen-Monster, nicht übersetzte#If-Zweige - die Select-Case-Semantik-Fallen (
Case 0,Case False,Case "") als Dauer-Regression - enge Schleifen mit 100.000 Durchläufen (Overhead-Messung),
Sleep, Busy-Wait undDoEvents-Warten — inklusive eines zuschaltbaren „Störfeuer-Timers“, der alle 250 ms mitten in laufende Messungen hineinfunkt - exponentielle und wechselseitige Rekursion (Stress für Aufruftiefe und Flush-Drosselung)
- Objektketten mit eigenem
Class_Initialize/Class_Terminate(kollidiert bewusst mit der Terminate-basierten Ende-Erkennung des Profilers) undCallByName-Aufrufe am Parser vorbei
Jeder Testlauf protokolliert sein Ergebnis; die erwarteten Werte stehen in der Testdokumentation. Stimmen Ergebnis und Soll überein, bleibt das Verhalten des VB6-Programms trotz Instrumentierung erhalten.
Genauer messen und große Aufruflisten flüssig auswerten
Der Profiler zieht seinen Overhead automatisch ab. Zeitwerte bleiben bis zur Anzeige ungerundet, rekursive Aufrufe zählen in Methodensummen nicht doppelt und Schleifen zeigen neben der Gesamtdauer auch die Dauer je Durchlauf. GDI- und USER-Handles werden standardmäßig an Methodengrenzen erfasst; eine Option schaltet die Abfrage je Zeile ein.
Auch große Aufruflisten mit Hunderttausenden Zeilen bleiben bedienbar: Der Profiler baut sie im Hintergrund auf und berechnet Quellcode- und Diagrammanzeigen nicht bei jeder Auswahl komplett neu. Ignorierte Aufrufe kannst du aus der Detailansicht ausblenden; die Session-Übersicht zählt sie weiterhin mit.
Die Instrumentierung erhält nummerierte ElseIf– und Do While-Zeilen, damit Erl und Resume <Nummer> weiter funktionieren. Kommentare mit einem Unterstrich am Ende ziehen keine nächste Codezeile mehr ein. Auch Prozedur-Attribute und Projektgruppen mit gleichnamigen Dateien werden korrekt behandelt.
Zehn VB6-Testprojekte prüfen feste Sollzeiten und fachliche Ergebnisse: Schleifen, Rekursion und Fehlerbehandlung gehören ebenso dazu wie Formulare, Klassen, Projektgruppen, 100.000 Aufrufe und ein Dauerlauf.
Unter der Haube
Für den laufenden Betrieb puffert der Profiler bis zu 10.000 Logeinträge und schreibt nur den belegten Teil auf die Platte. Am Ende der äußersten profilierten Methode wird sofort geschrieben; verschachtelte Methodenenden werden gesammelt, damit nicht für jeden Unteraufruf eine eigene Logdatei entsteht.
Die Verarbeitung der eingehenden Log-Daten läuft in getrennten Arbeitsschritten für Einlesen, Zuordnung und Auswertung. Zusätzlich werden umfangreiche Aufruflisten und ihre Filteransichten im Hintergrund vorbereitet, sodass die Oberfläche auch bei sehr großen Datenmengen bedienbar bleibt.
Für die Oberfläche kommen ObjectListView für die performanten Baum-/Listenansichten und FastColoredTextBox für die Quellcode-Anzeige mit Syntax-Highlighting zum Einsatz. Ein eigenes VB6-Datenmodell (Projekt → Datei → Methode → Zeile) bildet die komplette Struktur eines VB6-Projekts inklusive der historischen Windows-1252-Zeichenkodierung sauber ab — Umlaute in Bezeichnern wie „Rückgabe“ bleiben dabei korrekt erhalten. Die Konfiguration liegt in schlanken JSON-Dateien, ganz ohne Datenbank-Unterbau.
Fazit
Mit dem VB6 Profiler kannst du Methoden und einzelne Zeilen untersuchen, Ergebnisse über eine Session vergleichen und Messwerte samt Quellcode als KI-Datenpaket exportieren. Die zehn Testprojekte prüfen, dass die Instrumentierung das fachliche Verhalten deiner Anwendung erhält.