Seit gestern war es mir plötzlich nicht mehr möglich, meine Visual C++-Anwendung aus dem Visual Studio heraus zu debuggen. Das galt für beide Konfigurationen (Debug oder Release). Auch ein Bereinigen und das manuelle Entfernen aller nicht benötigten Dateien half nicht weiter. Das Abbrechen des Debug-Prozesses führte nach einiger Zeit immer wieder zu der folgenden Meldung:

Der Debugger konnte mindestens einen Prozess nicht beenden:

[(PID)] (Anwendung.exe) Das Anfügen des Debuggers an den Prozess ist noch nicht abgeschlossen, oder es wird gerade nicht die Art von Code ausgeführt, die für das Debugging ausgewählt wurde.

Möglicherweise befindet sich der Debugger in einem instabilen Zustand. Es wird empfohlen, alle Dateien zu speichern und das Programm zu beenden.

Startet man die Anwendung dagegen nicht aus dem Debugger und hängt den Prozess anschließend an den Debugger an, dann hat das Debuggen wieder wie gewohnt funktioniert. Ich habe das Mit dem Beispiel-Projekt VisualStudioDemo aus dem MSDN getestet:

Im Internet habe ich zu diesem Problem – abgesehen von der Neuinstallation des Visual Studios –keine wirklichen Lösungsvorschläge gefunden. Was mir in diesem Fall geholfen hat, war die Systemwiederherstellung. Die Systemwiederhestellung hatte ihren letzten Wiederherstellungspunkt in meinem Fall vor drei Tagen erstellt. Da dies definitiv ein Zeitpunkt war, zu dem noch alles funktioniert hat, habe ich diese Wiederherstullung einfach mal angeworfen. Und siehe da: Nach dem Neustartstart des System und der anschließenden Meldung, dass das System erfolgreich auf den Wiederherstellungspunkt zurückgesetzt wurde, hat alles wieder wie gewohnt funktioniert.

Wenn an einem Projekt der geladenen Projektmappe (Solution) im Visual Studio (VS) seit dem letzten erfolgreichen Erstellungsvorgang Veränderungen vorgenommen wurden, dann wird das vom VS erkannt und beim nächsten Erstellungsvorgang entweder automatisch oder nach Rückfrage berücksichtigt. Die entsprechenden Einstellungen, die festlegen, wie in diesem Fall vorgegangen werden soll, können im Optionen-Dialog des VS (über das Menü „Extras“ erreichbar) angepasst werden (vgl. Erstellen und Ausführen, Projekte und Projektmappen, Dialogfeld “Optionen”):

Hat man die Einstellung Zum Erstellen auffordern ausgewählt, so wird man fortan über veraltete Projektkonfigurationen folgendermaßen informiert:

In diesem Beispiel ist ein Projekt der gesamten Projektmappe veraltet.

Nun kann es in einigen Fällen aber eben doch vorkommen, dass dieser Dialog auch dann angezeigt wird, wenn an dem Projekt definitiv nichts verändert wurde. Das ist natürlich lästig, weil das Erstellen immer wieder wertvolle Zeit kostet und den eigentlichen Entwicklungsfluss mitunter stark beeinträchtigen kann. Daher wäre es sehr hilfreich, wenn man irgendwie herausfinden könnte, warum das VS der Meinung ist, dass ein neues Erstellen des Projektes erforderlich ist.

Bei einer Recherche im Internet bin ich dann auf den Artikel Enable C++ project system logging von Andrew L Arnott gestoßen, in dem beschrieben wird, wie man dem Problem auf die Spur kommen kann.

Schritt 1: System-Logging für c++-Projekte aktivieren

Zunächst muss das System-Logging für c++-Projekte aktiviert werden. Die Konfigurationsdatei devenv.exe.config aus dem Verzeichnis
%PROGRAMFILES%\Microsoft Visual Studio 10.0\Common7\IDE\
muss dazu um einige Zeilen ergänzt werden:

<?xml version ="1.0"?>
<configuration>
    <configSections>
        ...
    </configSections>
    <!-- Begin Neu -->
    <system.diagnostics>
        <switches>
            <add name="CPS" value="4" />
        </switches>
    </system.diagnostics>
    <!-- Ende Neu -->
    ...
</configuration>

Damit diese Änderung aktiv wird, muss VS neu gestartet werden.

Schritt 2: Log-Informationen abfangen

Nachdem der Logging-Mechanismus im ersten Schritt erfolgreich aktiviert wurde, muss festgelegt werden, wie die geloggten Informationen abgefangen werden können. Hierfür gibt es verschiedene Möglichkeiten. Eine Möglichkeit, die nachfolgend betrachtet wird, besteht darin, die Log-Einträge mit dem Werkzeug DebugView, das man kostenlos von der Microsoft-Internetseite herunterladen kann, abzufangen und darzustellen. DebugView kann man ohne aufwendige Installation in ein beliebiges Verzeichnis entpacken und ausführen. Startet man dann – bei laufendem DebugView – wieder das Debugging aus dem VS heraus, dann werden verschiedene Ausgaben des VS von DebugView abgefangen und direkt angezeigt. Sobald der bereits oben erwähnte Dialog mit der Information über das veraltete Projekt wieder erscheint, kann die Ausgabe des DebugView-Tools nach entsprechenden Informationen durchsucht werden. In dem hier beschrieben Fall weisen die Ausgaben darauf hin, dass über die Projektdatei auf 12 Header-Dateien verwiesen wird, die auf dem lokalen Rechner gar nicht (mehr) existieren:

Tatsächlich wurden diese Dateien erst vor kurzem entfernt. In der Projektdatei waren diese Dateien also nicht mehr zu finden. Eine weitere Suche über die Dateien (und deren Inhalte) in dem Verzeichnis des Projekts brachte dann aber einige weitere Fundstellen zu Tage. Infolgedessen wurde die gesamte Build-Ausgabe für das Projekts bereinigt, was dazu geführt hat, dass diese veralteten Dateien gelöscht wurden. Nach einem erneuten Erstellen des Projekts war das Problem dann behoben.

Links

Ein interessanter Artikel zum (zukünftigen) Umgang mit c++ bei Microsoft (von Marius Bancila): C++ Renaissance at Microsoft.

Im Microsoft SQLNCli team blog wurde am 29.08.2011 der Artikel Microsoft is Aligning with ODBC for Native Relational Data Access veröffentlicht. Microsoft weist darauf hin, dass ODBC zukünftig der Standard für die Entwicklung von Datenbank-Applikationen sein wird.

Einige der vielen Fragen, die sich nun insbesondere für Ole-DB-Entwickler stellen, werden in dieser FAQ beantwortet.

Nachdem der c++-Standard nun offiziell unter dem Namen c++11 verabschiedet worden ist, stellt sich natürlich die Frage, was wir von diesem Standard schon jetzt verwenden können. Der im Stdcxx Wiki veröffentlichte Artikel C++0xCompilerSupport zeigt, welcher Compiler welche c++11-Feutures bereits unterstützt.

Mit Windows XP führte Microsoft die sogenannte Side-by-Side-Technologie (SxS) ein, durch die es möglich wurde, DLLs in unterschiedlichen Versionen auf einem System zu verwenden. Hierdurch können Konflikte vermieden werden, wenn Anwendungen installiert sind, die unterschiedliche Versionen einer DLL benötigen.
Die entsprechenden DLLs werden im Verzeichnis WinSxS des Windows-Systemordners gespeichert. Um festzustellen, welche Anwendung auf welche Version einer DLL zugreift, kann man das von Microsoft bereitsgestellte Tool sxstrace.exe verwenden. Über die Eingabeaufforderung kann man vor dem Start der Anwendung die Datenaufzeichnung mit
  sxstrace Trace -logfile:test.log
beginnen. Das Tool schreibt alle Daten in die angegebene Logdatei. Nach dem Starten der Anwendung kann man die Aufzeichnung beenden und das Ergebnis über
  sxstrace Parse -logfile:test.log -outfile:test.txt
in eine Textdatei konvertieren.
Führt man diese Anweisung nicht als Administrator aus, dann kann es zu der Fehlermeldung
 sxstrace Trace -logfile:test.log
 StartTrace failed.  Error Message is:
 Unknown error.
kommen. In diesem Fall sollte man die Eingabeaufforderung als Adminstrator öffnen (d.h. mit der rechten Maustaste auf den Befehl “Eingabeaufforderung” klicken, um das Kontextmenü zu öffnen, um dort “Als Administrator ausführen” auswählen zu können):
Eingabeaufforderung als Administrator öffnen

Eingabeaufforderung als Administrator öffnen

Alternativ kann man die Eingabeaufforderung auch über den “Ausführen…”-Dialog öffnen, den man in diesem Fall mit der Tastenkombination [Windows-Taste]+R aufruft. In dem mit “Öffnen” beschrifteten Eingabefeld gibt man dann “cmd” ein und bestätigt diese Eingabe mit OK. Daraufhin sollte sich die Eingabeaufforderung mit Administratorrechten zeigen.
Detailliertere Informationen zu diesem Thema sind in diesem Artikel zu finden.

Die neuen (Fenster-) Klassen der MFC, die zum MFC Feature Pack gehören, bieten von Haus aus die Möglichkeit, die aktuellen Zustände der Fenster einer Anwendung (Größe, Position etc.) nach dem Beenden eines Programms in die Registry zu schreiben. Wird die Anwendung zu einem späteren Zeitpunkt wieder gestartet, dann können die alten Fensterzustände mit diesen Registry-Einträgen wiederhergestellt werden. Was aber tun, wenn die Registry hierfür nicht verwendet werden soll?

In den Methoden

  BOOL LoadState(LPCTSTR lpszProfileName, int nIndex, UINT uiID);
  BOOL SaveState(LPCTSTR lpszProfileName, int nIndex, UINT uiID);

der Klasse CBasePane nimmt die Serialisierung der Zustände ihren Lauf. Dabei ist ein Objekt vom Typ CSettingsStore für das Speichern der Daten (in die Registry) verantwortlich:

  CSettingsStoreSP regSP;
  CSettingsStore& reg = regSP.Create(FALSE, FALSE);

Wie das genau funktioniert, kann man an den (virtuellen) Read- und Write-Methoden der Klasse CSettingsStore (vgl. afxsettingsstore.h) sehen. Das Framework gestattet es aber auch, über ein CRuntimeClass-Objekt eigene CSettingsStore-Objekte zu verwenden. Definiert man sich also eine von CSettingsStore abgeleitete Klasse (z.B. CMySettingsStore), dann kann man die Kontrolle über das Speichern der Daten (z.B. in Dateien) übernehmen, indem man die Read- und Write-Methoden entsprechend überschreibt. Voraussetzung ist, dass die eigene Klasse zuvor noch mit

  CSettingsStoreSP::SetRuntimeClass( RUNTIME_CLASS( CMySettingsSore ) );

angemeldet wird.

Mit der Funktion „Externe Tools“ im Menü „Extras“ kann man die Entwicklungsumgebung mit externen System recht elegant verknüpfen. Setzt man beispielsweise ein webbasiertes Bug-Tracking-System (wie z.B. BugTracker.Net) ein, dann könnte die Verknüpfung folgendermaßen hergestellt werden. Zuerst öffnet man mit dem Befehl “Externe Tools” im Menü “Extras” den gleichnamigen Dialog:

Betätigt man nun die Schaltfläche “Hinzufügen”, so ist es möglich ein neues externes Tool anzulegen. In diesem konkreten Fall geben wir mal

  • Befehl: C:Program FilesInternet Exploreriexplore.exe.
  • Argumente: http://webtrack/edit_bug.aspx?id=$(CurText). Hierbei steht $(CurText) für den aktuell im Editor des Visual Studios markierten Text.

ein und schließen den Dialog wieder. Wenn man Änderungen und Neuerungen im Quellcode nun immer mit der entsprechenden Bug-ID kennzeichnet, dann kann man zukünftig, durch einfaches markieren der Bug-ID und anschließendem Aufruf des externen Tools direkt zur ausführlichen Beschreibung des Bugs im Bug-Tracking-System springen. Auf ähnliche Art und Weise können dann auch Dokumentationssysteme oder Wikis verknüpft werden.

Um das Ganze noch etwas komfortabler zu machen, können wir noch eine eigene Toolbar (und ein Tastenkürzel) für den Schnellzugriff auf unser neues Externes Tool einrichten. Dazu ruft man zuerst im Menü “Extras” den Befehl “Anpassen…” auf. Es öffnet sich ein Dialog, der über zwei Reiter verfügt. Unter dem reiter “Symbolleisten” richten wir eine neue Symbolleiste ein, die wir mal mit “Meine Tools” bezeichnen:

Anschließend wechseln wir auf den Reiter “Befehle” und wählen dort die soeben angelegte Symbolleiste “Meine Tools” aus. Mit der Schaltfläche “Befehl hinzufügen” fügen wir dann das Externe Tool mit der Nummer 7 hinzu, das in diesem Fall dem zuvor angelegten Aufruf des Bug-Tracking-Systems entspricht:

Wenn man nun alles richtig gemacht hat, dann erscheint im Visual Studio die neu angelegte Symbolleiste mit einer Schaltfläche.

18. May 2011 · Write a comment · Categories: MFC

Sowohl in Kontextmenüs (CMFCPopupMenu) als auch in “normalen Menüs” (CMFCMenuBar) gibt es für jeden Menüeintrag ein Objekt vom Typ CMFCToolBarMenuButton. Handelt es sich bei einem Menüeintrag um ein Untermenü (Submenü), dann kann man über die Methode

  const CObList& CMFCToolBarMenuButton::GetCommands() const;

auf die Menüeinträge des Untermenüs zugreifen. Mit dieser Information lässt sich dann recht einfach eine rekursive Funktion implementieren, die ab einem gegebenen Menüpunkt alle untergeordneten Menüpunkte systematisch durchläuft, z.B.:

	void IterateMenu( CMFCToolBarMenuButton* pButton )
	{
		if( pButton )
		{
			// Irgendetwas mit diesem Menüeintrag anstellen:
			pButton-> ...

			// Hat der Menüpunkt ein Untermenü?
			const CObList& lCmds = pButton->GetCommands();
			for( POSITION pos = lCmds.GetHeadPosition(); pos != NULL; )
			{
				IterateMenu( (CMFCToolBarMenuButton*) lCmds.GetNext( pos ) );
			}
		}
	}

Leider besitzen die beiden genannten Menüklassen keine gemeinsame Basisklasse, die ein einheitliches Iterieren über alle Elemente der ersten Ebene ermöglichen könnte. Allerdings ist diese Funktionalität durchaus an beiden Klassen vorhanden.

Für ein CMFCPopupMenu-Kontextmenü könnte diese Funktion dann folgendermaßen eingesetzt werden:

	CMFCPopupMenu* pMenu = ...
	for( int i = 0; i < pMFCPopupMenu->GetMenuItemCount(); i++ )
	{
		IterateMenu( (CMFCToolBarMenuButton*) pMenu->GetMenuItem( i ) );
	}

Im Fall eines Menüs vom Typ CMFCMenuBar könnte das dagegen so aussehen:

	CMFCMenuBar* pMenu = ...
	for( int i = 0; i < pMenu->GetCount(); i++ )
	{
		IterateMenu( (CMFCToolBarMenuButton*) pMenu->GetButton( i ) );
	}

Zum Auffinden von Memory-Leaks kann die globale Variable _crtBreakAlloc verwendet werden. Speicher wird in Blöcken zugewiesen, und jeder Block ist durch eine Nummer eindeutig identifizierbar. Wenn der Debugger ein Speicher-Leak meldet, dann wird diese Nummer immer in geschweiften Klammern – z.B. {314} – mitgeliefert:

Mit dieser Nummer kann man dann einiges anfangen! Zuerst setzt man einen Haltepunkt in die InitInstance-Methode. Dann wird der Debugger gestartet. Wenn der Debugger in dem Haltepunkt stoppt, gibt man folgendes in das Fenster “Überwachung” ein:

Führt man nun die Debug-Sitzung fort, dann hält der Debugger an der Stelle an, an der die o.g. Nummer zugewiesen wird:

Und nun ist der Call-Stack wieder unser bester Freund.

Man sieht, dass hier ein CString-Objekt mit new angelegt wird. Das ist natürlich mehr als verdächtig! Und sucht man nach der Stelle, wo diese Objekte wieder aufgeräumt werden, dann wird man nicht fündig. Das Objekt, das diesen Speicher-Leak verursacht ist damit gefunden.