VS-Tipps & Tricks: Benötigt man eigentlich noch DEF Dateien?

Das was eine DEF Datei tut, benötigt man oft genug noch. Stellen wir die Frage mal etwas anders:
Kann man den Inhalt einer DEF Datei auch wo anders unterbringen?

Ja man kann sich eine DEF Datei sparen. Auch der Linker kennt entsprechende Optionen auf der Befehlszeile, die das gleiche tun, was eben eine DEF Datei macht.

Wer schon mal ATL Support zu einem DLL Projekt hinzugefügt hat, oder eine ATL DLL mit VS-2005/2008 angelegt hat, der wird feststellen, dass es gar keine DEF Datei mehr gibt, aber dennoch Funktionen wie DllCanUnloadNow exportiert werden.

Schaut man sich den Code an, der erzeugt wird, dann sieht man einen netten Block von pragmas.

#pragma comment(linker, 
        "/EXPORT:DllCanUnloadNow=_DllCanUnloadNow@0,PRIVATE") 
#pragma comment(linker, 
        "/EXPORT:DllGetClassObject=_DllGetClassObject@12,PRIVATE") 
#pragma comment(linker, 
        "/EXPORT:DllRegisterServer=_DllRegisterServer@0,PRIVATE") 
#pragma comment(linker, 
        "/EXPORT:DllUnregisterServer=_DllUnregisterServer@0,PRIVATE")

Die Ähnlichkeit zur DEF Datei ist frappierend, was aber auch wieder nicht wundert.
Nett ist auch, dass man sich die declspec(dllexport) Spielereien sparen kann. Alles macht hier einfach der Linker ❗

Das eigentliche Problem an dieser Syntax ist, dass man auch den intern gemangelten Namen kennen muss. Es enthebt den Programmierer nicht die Funktion auch entsprechend korrekt zu deklarieren.

#pragma comment(linker, 
        "/EXPORT:ExportedFunction=_ExportedFunction@0") 
extern "C" BOOL __stdcall ExportedFunction() 
...

Der einzige kleine Trick steckt hinter der Frage: Wie kommt man an den gemangelten Namen der Funktion?
Aber auch das ist nicht schwer. Funktion gewünscht deklarieren und definieren, z.B.:

__declspec(dllexport) BOOL __stdcall ExportedFunction2(const char *) 
{ 
 return FALSE; 
}

Dann mit Depends, den exportierten Namen abgreifen (rechte Maustaste Copy Function Name).
Dann einfach die Funktion final übernehmen.

#pragma comment(linker, 
        "/EXPORT:ExportedFunction2=?ExportedFunction2@@YGHPBD@Z") 
BOOL __stdcall ExportedFunction2(const char *) 
{ 
 return FALSE; 
}

Netter Effekt: Auf diesem Weg kann man auch eine Funktion leicht unter zwei Namen exportieren…

VS-Tipps & Tricks: Einfaches Navigieren mit Strg+-

Vielleicht ist einigen schon aufgefallen, dass es die Schalter Navigate Backward und Navigate Forward im Standard-Toolbar von Visual Studio gibt. Diese Funktionen werden über die Hotkeys Strg+- (Bindestrich) und Strg+Umschalt+- ausgelöst.

Bei dieser Funktion werden Go-Back-Markierungen in einer Liste vermerkt. Hierbei wird nicht jede Cursorbewegung aufgezeichnet, sondern Bewegungen, die über eine größere Distanz erfolgen.

D.h. man kann mit Strg+- sofort an die Position zurückspringen, die man soeben verlassen hat. Die Umkehrfunktion wird wie gewohnt mit der Umschalt-Taste ausgelöst, Umschalt+Strg+-.

Solche Go-Back-Marken werden in den folgenden Fällen gesetzt.

  • Öffnet man eine neue Datei, dann wird die Position in der aktuellen Datei, als Go-Back-Marke gespeichert. Das Wechseln in eine andere Datei (Strg+Tab) setzt keine Go-Back-Marke (was ich verwunderlich finde).
  • Jede Löschoperation nach einer Cursorbewegung setzt eine Go-Back-Marke.
  • Eine Textsuche (Strg+F) setzt eine Go-Back-Marke an der Fundstelle.
  • Inkrementelle Suche (ob vorwärts oder rückwärts ist egal), trägt eine Go-Back-Marke in die Liste ein und gleichfalls, wenn die inkrementelle Suche beendet wird.
  • Verwendet man GotoLine (Strg+G) oder bewegt den Cursor mehr als 10 Zeilen von der aktuellen Position weg, wird eine Go-Back-Marke an der neuen Position gesetzt. Dies gilt auch wenn dies durch eine Suche ausgelöst wird, die mehr als 10 Zeilen weiterspringt. In diesem Fall wird auch die Startposition als Go-Back-Marke gespeichert.
  • Jeder Klick mit der linken bzw. rechten Maustaste platziert eine Go-Back-Marke an der alten Cursor-Position. Weitere Mausklicks ohne Cursor-Operationen zwischen drin platziert keine neue Go-Back-Marke.
  • Jeder Step-Into beim Debuggen löst auch setzt auch eine Go-Back-Marke.

Sehr nett ist auch die Möglichkeit den gesamten Text von der aktuellen Position bis zurück zur letzten Go-Back-Marke zu selektieren. Dies erfolgt über den Hotkey Strg++ (Pluszeichen).

Ausgesprochen nützlich finde ich diese Funktion auch beim debuggen. Man kann sofort an die Stelle zurückspringen von der man soeben kam, ohne das Call-Stack-Fenster zu verwenden.

BTW: Diese Funktionen habe ich erst durch das versehentliche Auslösen der Kombination Strg++ vor längerer Zeit entdeckt 🙂

VS-Tipps & Tricks: Einstellungen für Visual Studio mehreren Arbeitsplätzen gleich halten

Das man Einstellungen von Visual Studio mittlerweile einfach und intelligent ex- und importieren lassen ist ja mittlerweile bekannt. Man findet diese Funktion direkt im Tools Menü. In meinem Blog fand dies auch schon Erwähnung.

Weitaus versteckter in den Einstellungen von Visual Studio findet man unter
Tools -> Environment -> Import and Export Settings
einen Schalter der das heißt: Use team settings file

Hier ist ein Feature verborgen, dass es wirklich einfach macht auch in einem Team identische Projekt und Visual Studio Einstellungen auf jedem Arbeitsplatz einzustellen. Und das mit wirklich geringem administrativen Aufwand.
Gerade die Include und Library Pfade für gemeinsame Bibliotheken und deren Reihenfolge lassen sich direkt vorbelegen. Und sie werden bei jedem Neustart von Visual Studio auf den vorbestimmten Wert zurückgestellt.
Vorbei die Suche warum in aller Welt irgendwelche falschen SDK Header und Libraries verwendet werden. Warum ein Entwickler immer noch eine uralte Libary eines Drittanbieters verwendet und in seinen Modulen ein Bug auftaucht, den alle schon überwunden glaubten.
Voraussetzungen dafür ist natürlich, dass die einzelnen Rechner der Entwickler natürlich ein ähnliches Verzeichnis Layout verwenden, bzw. die gemeinsamen Bibliotheken im Netz liegen.

Im Prinzip lässt sich jede Einstellung von VS auf diesem Wege beeinflussen.

VS-Tipps & Tricks: Tracepoints die zweite…

In meinen ersten Artikel zu Tracepoints habe ich ja auch erwähnt, dass man sehr einfach auch Variablen ausgeben kann. Das schöne ist, dass dies sofort auch für Iteratoren und manche andere Klasse funktioniert, ohne dass man spezielle Member angeben muss. Man setzt einfach die Variable in geschweifte Klammern. Und das dürfen auch komplexe Ausdrücke sein, wie im Debug-Watch-Fenster.

So führt die folgende Tracepoint Definition in einem meiner Programme:

$FUNCTION, {iCountry,x} {s_countries[iCountry]}

zu der folgenden Ausgabe:

wmain(int, wchar_t * *, wchar_t * *), 1 {m_szISO3=0x00487e48 „AUT“ m_szISO2=0x00487e44 „AT“ m_szCountryCode=0x00487e40 „43“ }
wmain(int, wchar_t * *, wchar_t * *), 2 {m_szISO3=0x00487e3c „AUS“ m_szISO2=0x00487e38 „AU“ m_szCountryCode=0x00487e34 „61“ }

Ein leider undokumentiertes Feature von Tracepoints ist jedoch, dass innerhalb der geschweiften Klammern auch die bekannten Formatierungen aus dem Debug-Watch-Fenster verwendet werden können. Wie hier zum Beispiel um die Ausgabe des Intergers in hexadezimal zu ändern:

$FUNCTION, {iCountry,x} {s_countries[iCountry]}

wmain(int, wchar_t * *, wchar_t * *), 0x00000001 {m_szISO3=0x00487e48 „AUT“ m_szISO2=0x00487e44 „AT“ m_szCountryCode=0x00487e40 „43“ }
wmain(int, wchar_t * *, wchar_t * *), 0x00000002 {m_szISO3=0x00487e3c „AUS“ m_szISO2=0x00487e38 „AU“ m_szCountryCode=0x00487e34 „61“ }

Besonders ist das interessant, wenn Fensterprozeduren tracen möchte ohne das Trace-Tool oder Spy++ verwenden will und man die Formatspezifkation wm verwendet und alle Fensternachrichten im Klartext lesen kann. Oder man verwendet hr und sieht auch einen HRESULT nicht mehr nur als kryptische Zahl.

VS-Tipps & Tricks: Anpassen von Kontextmenüs

Manchmal hätte man gerne eigene Befehle in einem Kontextmenü, oder man möchte evtl. einen speziellen Befehl aus einem Kontextmenü verschieben oder entfernen. Dies geht, allerdings nur mit einem kleinen Trick, denn die Kontextmenüs befinden sich normalerweise ja nicht in der Anzeige:

  • Mit der rechten Maustaste klickt man auf irgend einen Toolbar.
  • Man wählt Customize aus dem Kontextmenü.
  • Im Karteireiter Toolbars wählt man Context Menus aus. Es erscheint ein neuer Toolbar mit allen Kontextmenüs.
  • Nun kann man das entsprechende Menü durch anklicken öffnen (also z.B. Project and Solution Context Menu).
  • Nun kann man einfach die entsprechenden Anpassungen durchführen. 

Wenn man den Haken in dem Customize Dialog bei Context Menus drin lässt werden automatisch die Kontextmenüs angezeigt, wenn man den Customize Dialog öffnet.

VS-Tipps & Tricks: Synchronisation und Navigation mit dem Solution Explorer

Die Navigation über den Solution Explorer ist der übliche weg andere Sourcedateien zu öffnen. Außer man nutzt VA-X da geht noch mehr 😉 

Der Solution Explorer wird über die Hotkeys Strg+Alt+L, geöffnet oder man verwendet das View Menü. Bei mir ist der Solution Explorer (außer im Debug Modus), am recten Rand gedockt, teilt sich das Fenster aber mit dem Resource View (Strg+Umschalt+E) und Class View (Strg+Umschalt+C).

Unter dem Menü Tools -> Options -> Projects and Solution findest man die Checkbox „Track item in Solutiuon Explorer„. Mit dieser Option wird die aktuelle Datei, die man gerade bearbeitet auch im Solution Explorer angezeigt und selektiert. Da ich die Dateien in Unterordnern der Solution auch nach Themen gruppiere kann man relativ schnell thematisch nahe Datein öffnen.  Dies kommt meine Arbeitsweise sehr nahe und ich verwende diese Einstellung immer.

Dieses Verhalten lässt sich auch direkt über den Befehl View.TrackActivityinSolutionExplorer ein- bzw. ausschalten. Dieser Befehl hat allerdings keinen Hotkey.  Das geht natürlich auch schnell über das Command Window (Strg+Alt+A)

PS: Bzgl. Unterordner. Ich verwende meistens nur Ordner im Solution Explorer, keine Ordner in der Verzeichnisstruktur.

VS-Tipps & Tricks: Insert Tracepoint, der nette Helfer beim Debuggen

Breakpoints kennt jeder, aber Tracepoints!?!

Den Menüpunkt Insert Tracepoint findet man im Kontext Menü des Editors.
Wer kennt das nicht? Man ist mitten in einer Debug-Session, aber man müsste jetzt eine Variable beobachten, wie sie sich entwickelt. Manchmal ist es einfach ungünstig einen Breakpoint zu stetzen, weil der Timing, Focus und manches andere ändert. Zudem, Breakpoints sind lahm und die F5-Taste will man auch icht kaputt machen. Ein TRACE Statement wäre super. Aber jetzt den Code ändern? Evtl. klappt das Edit&Continue nicht oder man kann es nicht einrichten, weil man eine Release Version debuggt.

Hier ist ein Tracepoint ideal. Er leistet das, was ein TRACE Statement auch leistet, nur ohne Code Änderung. Einfach Insert Tracepoint auswählen und angeben was man gerne sehen möchte.
z.B.: $FUNCTION bReset={bResetClipRegion} bEmpty={bEmptyClipRect}

Es stehen einige Makros zur Verfügung, die direkt im Dialog erklärt werden. Gigantisch nützlich sind die Variablen $FUNCTION , $CALLER, und $CALLSTACK. Das geht auch mit Variablen, die sich im aktuellen Kontext befinden, wie in meinem Beispiel zu sehen. Einfach die Variablen in geschweifte Klammern setzten und das war es schon.

Die Ausgabe erfolgt umgehend in der Debug-Ausgabe:
RedirectEraseBkgndToParent(CWnd *, CDC *) bReset=true bEmpty=true
RedirectEraseBkgndToParent(CWnd *, CDC *) bReset=true bEmpty=true
RedirectEraseBkgndToParent(CWnd *, CDC *) bReset=true bEmpty=true

Absolut super ist, dass man dazu noch nicht mal einen Breakpoint setzen muss. D.h. man kann während der Laufzeit einfach so einen Tracepoint setzen ohne große Eingriffe in die Software.

Und ganz ohne Probleme lässt sich auch ein Breakpoint in einen Tracepoint umwandeln und umgekehrt. Bei den Eigenschaften eines Breakpoints im Kontextmenü kann man die Eigenschaft When hit… entsprechend bearbeiten. Nimmt man den Haken bei Continue Execution heraus, dann hat man einen normalen Breakpoint. Setzt man den Haken bei Continue Execution, so macht man aus einem Breakpoint einen Tracepoint, man muss nur noch eine entsprechende Nachricht nach Wunsch angeben, die in der Debug Ausgabe angezeigt werden soll.

❗ Beachten muss man jedoch, dass die Performance schlechter ist als bei einem eingebauten TRACE. Denn ein Breakpoint wird ausgelöst und der Debugger übernimmt kurzfristig die Kontrolle.

VS-Tipps & Tricks: Edit.GotoBrace (Strg+´) kann noch mehr

Mit Strg+´ kann man ja bekannterweise die nächste passende schließende bzw. geöffnete Klammer finden. Mit Umschalt+Strg+´ kann man diesen Bereich auch direkt markieren. Diese Funktion ist nicht unbedingt eine Neuigkeit.

Viele wissen jedoch nicht, dass man auch passende #if, #ifdef, #ifndef, #else, #elif, #endif Direktiven damit anspringen kann. Man muss dazu das Caret einfach nur auf die entsprechende Direktive platzieren und mit Strg+´ springt man zum nächsten #else, #endif oder wieder an den Kopf des ersten #if Blockes.

Auch in diesem Fall kann man auf einfache Weise mit Umschalt+Strg+´ den entsprechenden Codeblock markieren.

VS-Tipps & Tricks: Die Combobox im Property Fenster des Dialog Editors

Unscheinbar aber sehr effektiv zum Ändern von großen Dialogen ist die Combobox im Eigenschaftsfenster.
In den meisten Fällen steht in dieser Combobox nur der Name des Objektes, das ausgewählt ist und eine weitere Auswahl ist dann oft hier nicht möglich.

Editiert man jedoch einen Dialog, dann kann man mit dieser Combobox, sehr effektiv und schnell Controls gezielt auswählen. Besonders wenn man Controls übereinander legt weil diese ein oder ausgeblendet werden wird es schwierig diese Controls gezielt mit der Maus zu markieren.
Mit der Combobox kein Problem.
Die Reihenfolge in der Combobox ist die alphabetisch sortiert nach den Namen der Control IDs. Auch das erleichtert die Auswahl, wenn man ein Control direkt anhand seiner ID finden will. Hat man den Dialog nicht designed und will ein Control identifizieren, dass durch einen bestimmten Code gesteuert wird, ist das mit dieser Combobox ein Klax.

Auch wenn man die Maus nicht verwenden will kann man mit dieser Combobox schnell navigieren:
Alt+Eingabetaste, wählt das Eigenschaftsfenster, mit 3 x Tab oder 2 x Umschalt+Tab kommt man in die Combobox, die man dann mit den Cursor-Tasten, bzw. Alt+Pfeilrunter bedienen kann. Mit der Escape-Taste kommt man zurück in die Editoroberfläche.

VS Tipps & Tricks: Kontextmenü für Refactor Befehle in VA-X ab Build 1557

Die Refactoring Befehle in VA-X sind ja schon länger bekannt und sind wirklich ein prima Feature (Build 1534 seit 09.2006).

Nur war es manchmal etwas trickreich an diese Befehle zu kommen. Meistens habe ich die Maus verwendet und über dem Symbol platziert und gewartet, bis das entsprechende Dropdown Symbol aus VA-X erschien. Intuitiv und schnell war das nicht.

Seit dem Build 1557 (052007) finden sich die Refactoring Befehle direkt als erster Menüpunkt im normalen Kontextmenü und sind damit auch über die Tastatur direkt mit der Kontextmenütaste (oder Umschalt+F10) zu erreichen. Ein Grund mehr sich diese Funktionen wirklich mal anzusehen. Sie sind wirklich einen Tipp wert!
Meine Favoriten im Refactoring Menü sind immer wieder Find References und Rename.

Und für alle die meinen ich habe was vergessen oder übersehen: 😉

  • Es gab zwar seit dem Build 1540 einen entsprechenden Befehl den man einem Hotkey zuordnen konnte, aber allzu viele Hotkeys passen in meinen Kopf auch nicht rein.
  • Ja und man kam an diese Befehle auch über Umschalt+Kontextmenütaste erhalten. Nur ist das komplette VA-X Menü leider etwas unaufgeräumt und groß.