VS-Tipps & Tricks: Kommentare intelligent und einfach umbrechen

 Wer programmiert, der dokumentiert auch. Denke ich zumindest 😀

Ich zumindest habe teilweise Kommentare, die sich über 10 bis zu 100 Zeilen erstrecken. Die sind nah am Code und erklären, oft was die Hintergründe für das gewählte Design und Vorgehen sind.

Leider ist aber der Editor vom Visual Studio kein Word. D.h. absatzweisen Umbruch kennt das Ding nicht und manuell solche Texte inkl. Einrückungen und Listen mit Bindestrichen oder 1., 2., 3. zu formatieren ist etwas was gar keinen Spaß macht. Zudem sind die Kommentar Zeichen // oder * eher lästig als hilfreich beim schreiben.
Und auch VAX muss hier mal passen. Aber! Netterweise gibt e auch andere Addins.

Ich habe vor langer Zeit schon den Comment Reflower  entdeckt. Mit dem ist das Ganze ein Klacks.
Aus dem nachfolgenden Text:

// Dies ist ein Kommentar, den man nicht wirklich hier schreiben
// müsste und der
// eigentlich nichts bedeutet außer
// die Funktionen von einem netten Addin zu zeigen.
// Das
//  1. wirklich Arbeit erspart
//  2. total simpel zu bedienen ist
//  3. für alle VS-Versionen von 2005 über 2008 bis 2010 verfügbar ist.
// Was bei der Formatierung heraus kommt lässt sich sehen.

Wird in Null-Komma-Nichts der folgende Text:

// Dies ist ein Kommentar, den man nicht wirklich hier
// schreiben müsste und der eigentlich nichts bedeutet
// außer die Funktionen von einem netten Addin zu
// zeigen. Das
//  1. wirklich Arbeit erspart
//  2. total simpel zu bedienen ist
//  3. für alle VS-Versionen von 2005 über 2008 bis
//     2010 verfügbar ist.
// Was bei der Formatierung heraus kommt lässt sich
// sehen.

Wer noch mehr Beispiele sehen will, was das Tool leistet findet hier auf der Sourceforge Seite ein Vorher Nacher Pärchen.

Das Addin existiert für alles Visual Studio Versionen ab 2005.
Es kann hier heruntergeladen werden:

PS: Es empfiehlt sich ein Blick auf die Blockdefnition in den Einstellungen. Dort ist oft ein Leerzeichen hinter dem * bzw. // eingetragen. Leider lässt sich in meiner Version hier RegEx nicht einschalten. Wer also ein <tab>-Zeichen hinter dem * oder // hat wird sich wundern wenn das Addin keinen Kommentar findet. Ich habe das Leerzeichen einfach entfernt…

PPS: Für alle nicht C++ Entwickler. Das Tool funktioniert auch für VB und C# ❗

VS-Tipps & Tricks: Springe zur nächsten Klammer funktioniert auch für #if, #elif, #else und #endif

Wer sich schon durch die Windows Header gekämpft hat um herauszufinden warum welche Definition einer Struktur oder Funktion in irgend einer Windows Version so oder gar nicht vorhanden ist, der weiß auch wie einem #if, #elif, #else und #endif das Leben schwer machen können, was die Orientierung betrifft.

Netterweise hilft einem eine Funktion, die man nur von Blöcken und verschachtelten Funktionen her kennt Strg+´ (Edit.GotoBrace). Wichtig! Man darf nicht auf der Variable oder Bedingung stehen, sondern muss auf dem Schlüsselwort stehen.

Wenn man auf einer Präprozessor Direktive kann man mit den Tasten die einem zur passenden Klammer bringt zur nachfolgenden Direktive. Und mit dem Festhalten der Umschalttaste kann man den entsprechenden Block auch markieren.

AfxMessageBox versus CWnd::MessageBox

Jeder MFC Entwickler kennt AfxMessageBox. In der Klasse CWnd finden wir auch die Memberfunktion CWnd::MessageBox.
Mancher Entwickler wird sich nun fragen: Wann nehme ich denn nun was?

Kurze Antwort: Meide CWnd::MessageBox und nutze immer AfxMessageBox

Lange Antwort:
CWnd::MessageBox ist einfach nur ein direkter Wrapper für Windows API Funktion MessageBox.
Der Grund warum man AfxMessageBox verwenden sollte liegt darin was AfxMessageBox einfach noch mehr leistet:

  1. AfxMessageBox benutzt die virtuelle Funktion CWinApp::DoMessageBox. Damit kann man zentral eine andere Behandlung für Fehlermeldungen einbauen.
  2. AfxMessageBox sorgt dafür, dass OLE Controls benachrichtigt werden, dass Ihre nicht modalen Dialoge nun deaktiviert werden müssen. Es wäre ja ziemlich heftig, wenn man solche Dialoge trotz aktiver MessageBox noch benutzen könnte. (siehe CWinApp::DoEnableModeless und Implementierung von CWnd::GetSafeOwner)
  3. CWnd::MessageBox benutzt das aktuelle Fensterhandle aus dem es aufgerufen wird als Parent-Handle für die API Funktion MessageBox. Die wenigsten Entwickler kennen, lesen oder beachten den netten Zusatz in der MessageBox Doku:
    If you create a message box while a dialog box is present, use the handle of the dialog box as the hWnd parameter. The hWnd parameter should not identify a child window, such as a control in a dialog box.
    Es ist also gerade zulässig CWnd::MessageBox aufzurufen, denn oft genug haben wir es mit Child-Windows zu tun.
    Netterweise beachtet AfxMessageBox genau das. Es ermittelt das aktuelle aktive Top-Level Fenster und setzt es intern für den Aufruf von MessageBox.
    Das wird auch ganz besonders wichtig, wenn man evtl. mehrere UI Threads hat. AfxMessageBox verwendet automatisch den richtigen. CWnd::MessageBox verwendet den aktuellen Thread aber evtl. als Fenster das Fenster-Handle aus einem anderen Threads.
  4. CWnd:MessageBox hat keine Implementierung für die F1-Taste und die Hilfeimplementierung der MFC.
  5. Ich muss mich nicht um den Titel der MessageBox kümmern, denn AfxMessageBox benutzt den hinterlegten Applikationsnamen (CWinApp::m_pszAppName) der in der MFC hinterlegt und definiert wurde.
  6. Netterweise setzt AfxMessageBox passende Icons wenn nur die Reaktionsform definiert wird, also kein Icon. (MB_OK und MB_OKCANCEL benutzt MB_ICONEXCLAMATION, MB_YESNO und MB_YESNOCANCEL benutzt MB_ICONQUESTION).

HTH

VS-Tipps & Tricks: Format Specifier in den Debugger Fenstern

Beim Debuggen Variablen im Watch-Window oder im Quick-View anzeigen zu lassen ist gängige Praxis und jeder etwas fortgeschrittene Entwickler wird diese Funktionen des Visual-Studios nutzen.

Üblicherweise wählt der Debugger eine Darstellungsform, die für die Variable geeignet ist. Besonders für STL Datentypen hat sich hier einiges getan seit VC-2005.

Dennoch kann man dem Debugger für manche Datentypen noch einen Format Specifier mitgeben, der einem die Arbeit beim Debuggen extrem erleichert.
Format Specifier erlauben es eine Variable entsprechend Ihrer Verwendung zu interpretieren. Typisch hier wäre eine Windows Nachricht. Als Integer sagt einem 0x0129 nicht viel, aber WM_NCCREATE einiges. Wenn man hinter die Variable nMsg im Watch-Fenster einfach aus nMsg,wm erweitert erhält man sofort die Nachricht als symbolischen Wert angezeigt.

Ich will hier nicht alle aber wenigstens ein paar sehr nützliche und weniger bekannte Format Specifier aufzählen:

! – Raw format
hr – HRESULT in Klartext
su -Unicode
s8 – UTF8
wm – Windowsnachricht
wc – Fensterstil
<n> – Anzahl der Arrayelemente

Am schönsten sieht man die Wirkung an dem folgenden Code und den nachfolgenden Bildern der Watch-Windows:

int g_ai[] =
{
  4711,
  815,
  1234
};

int _tmain(int argc, _TCHAR* argv[])
{
  std::list lst;
  lst.push_back(1);
  lst.push_back(2);
  DWORD dwHResult = 2147943623;
  void *szUnicode = L"Unicode ÄÖÜäöü";
  char *szUTF8Code = "Umlaute AE=\xc3\x84 OE=\xc3\x96 UE=\xc3\x9c.";
  UINT winmsg = 125;
  DWORD winstyle = 0xA6730000;
  int *pi = g_ai;

  DebugBreak();
  return 0;
}

Hier das Ganze die Daten im Watchwindow ohne Formatspecifier:

Watch1

Hier das Ganze mit:

Watch2

Weitere Links dazu:
http://msdn.microsoft.com/en-us/library/75w45ekt.aspx
http://blogs.msdn.com/vcblog/archive/2006/08/04/689026.aspx

CMapStringTo… HashKey Implementierung in VC-2003/5/8 ist auch fragwürdig

 Hash-Algorithmen haben es mir irgendwie angetan. 1984 hatte ich das erste mal mit einem miesen Hashverfahren zu tun, das einfach versagte wenn es nur um Ziffern ging. Das ganze betraf das Betriebssystem OASIS (später TheOS) und dessen Index Dateiorganisation. Ich entwickelte hierfür einen Patch in Assembler, der damals exakt in den x-Bytes der Kernels in Z80 Assembler passen musste.
Das Problem bei Strings die nur aus Ziffern bestehen ist das die Veränderung, die immer nur die unteren 4 Bits betrifft und die höheren 4 Bits immer konstant sind mit 3 belegt sind.

Als ich gelesen habe,  dass in VS-2010 die CMap Implementierung nun auch für Strings den selben Algorithmus aus der STL erhalten, habe ich mir auch den mal genauer angesehen. (siehe Beitrag Die MFC erhält mit VC-2010 jetzt eine neue HashKey Implementierung)
Der sieht so aus:

inline UINT CMapStringToString::HashKey(LPCTSTR key) const
{
  UINT nHash = 0;
  if (key == NULL)
  {
    AfxThrowInvalidArgException();
  }
 
  while (*key)
    nHash = (nHash<<5) + nHash + *key++;
  return nHash;
}

Und auch in diesem Fall muss ich sagen, dass der Algorithmus nur scheinbar intelligent ist. Wenn man sich aber die Abstände die hier erzeugt werden etwas genauer anschaut kommt man schnell auf eine Wertekombination, in der der Algorithmus versagt.
Wie sehr er versagt zeigt das folgende Beispiel, in dem ich einfach einen String verwende der aus 5 Ziffern besteht und immer den Abstand 11 verwendet.

CMapStringToPtr myMap;
for (int i=0; i<1000; i+=11)
{
  CString strKey;
  strKey.Format(_T("%05d"),i);
  myMap[strKey] = NULL;
}

Das erschreckende Ergebnis der Verteilung ist bei einem Unicode wie auch MBCS Projekt:

TMyMap<class CMapStringToPtr>::DumpMap
Bucket  0 =  0
Bucket  1 =  0
Bucket  2 =  0
Bucket  3 =  0
Bucket  4 =  0
Bucket  5 =  0
Bucket  6 =  0
Bucket  7 =  0
Bucket  8 = 36
Bucket  9 =  0
Bucket 10 =  0
Bucket 11 =  0
Bucket 12 =  0
Bucket 13 =  0
Bucket 14 = 55
Bucket 15 =  0
Bucket 16 =  0

Wie gut, dass auch die String Variante von HashKey überarbeitet wird.

BTW: Da die internen Strukturen protected sind muss man zu einem kleinen Trick greifen um die Verteilung ausgeben zu können. Ich habe dazu ein template verwendet, dass für alle MFC Maps funktioniert.

template<class TBase> class TMyMap : public TBase
{
public:
  void DumpMap()
  {
    TRACE(__FUNCTION__ "\n");
    if (m_pHashTable)
    {
      for (size_t i=0; i<m_nHashTableSize; ++i)
      {
        int iCount = 0;
        for (CAssoc *p = m_pHashTable[i]; p; p=p->pNext)
          ++iCount;
        TRACE("Bucket %2d = %4d\n", i, iCount);
      }
    }
  }
};

Merke: Wer Hashes verwendet sollte sich über sein Hashverfahren wirklich gedanken machen.

BTW: Sollte es den Leser meines Blogs langsam langweilen weil ich mich permanent mit Hash-Algorithmen aufhalte, der sei getröstet: Dies war vorläufig mein letzter Beitrag zu CMap… :mrgreen:

Die MFC erhält mit VC-2010 jetzt eine neue HashKey Implementierung

Ich hatte die schlechte Hashkey Implementierung ja bereits in VC-2005 als Bug gemeldet (siehe Die HashKey Implementierung in der MFC in VC-2005 und VC-2008). In VC-2008 geschah dies bzgl. wieder nichts. Aber was lange währt wird manchmal gut 😉

Die neue Implementierung wird aus der aktuellen STL übernommen. Diese Implementierung sorgt für Integer und Pointer für eine sehr gute zufällige Verteilung. Und weitere gute Nachricht dazu, auch die Implementierung für Strings wird aus der STL übernommen.

Zitat:

Hello Martin,

Thanks for the report. This issue has been fixed in MFC for Visual Studio 2010. MFC now uses the STL hash functions directly when possible, or uses a new algorithm copied from STL (in the case of strings).

Pat Brenner
Visual C++ Libraries Development
Von Microsoft am 20.07.2009 um 12:15 bereitgestellt

Siehe https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=468860

Tipps & Tricks: Warum nicht mal ein anderer Font, wie z.B. Consolas

Eigentlich bin ich sehr konservativ was meine Visual-Studio Einstellungen betrifft. Aber irgendwie hat mich heute mal die Experimentierlaune überkommen, weil ich durch Zufall über einige Color-Schemes gestolpert bin und im C++ Forum eine Diskussion lief ob „hell auf dunkel“ oder „dunkel auf hell“ besser ist.

Am Ende des Experimentierens, habe ich das Farbschema gelassen wie es ist. Ich habe ja auch VA-X, den untentbehrlichen Helfer des Programmieres,  und auch da ist bis auf das Highlighting alles Standard. Aber bei der Schriftart habe ich – für mich gefühlt – was besseres gefunden: „Consolas“.
Eindeutig für mich das schönere Schriftbild, klarer und etwas kompakter und kleiner als „Courier New“.

Beispiel „Courier New“
CourierNew

Beispiel „Consolas“
Consolas
Wer mag, der kann es gleich probieren. Auf meinen Vista Rechnern war diese Schriftart sofort vorhanden. Wer das Font-Paket nicht hat, der kann es herunterladen bei Microsoft Consolas Font Pack for Microsoft Visual Studio 2005 or 2008

Die HashKey Implementierung in der MFC in VC-2005 und VC-2008

Die Maps in der MFC werden ja auch zum Teil gerne verwendet. Auch wenn viele Programmierer eher auf die std::map bzw. std::hash_map verwenden.

Zwei interne Dinge sind jedoch vielen Entwicklern nicht klar.

  1. Die Standard-Maps der MFC werden mit 17 Hash-Buckets erzeugt. Man sollte sich also über die Anzahl der Elemente klar werden, die gespeichert werden sollen.
    Bei großen Datenmengen sollte man evtl. darüber nachdenken, die Anzahl der Buckets zu erhöhen.
  2. Ist die Hash Funktion zu beachten, die hier verwendet wird. Denn deren Effektivität entscheidet ja, wie die Einträge auf die Buckets verteilt werden.

Zu diesem zweiten Punkt macht man eine erstaunliche Entdeckung, wenn man sich ansieht, was Microsoft für eine Funktion vorgesehen hat um den Hask-Key zu erzeugen. Diese Funktion ist in der MFC für primitive Werte vordefiniert, als primitive Werte sehe ich alle numerischen Werte, Zeiger und Handle an.
Man findet eine Implementierung als Template-Funktion in afxtempl.h:

template<class ARG_KEY>
AFX_INLINE UINT AFXAPI HashKey(ARG_KEY key)
{
 // default identity hash - works for most primitive values
 return (DWORD)(((DWORD_PTR)key)>>4);
}

Das erstaunliche ist für mich, dass hier die untersten 4 Bits einfach abgeschnitten werden. Für mich hätte für Handles, Zeiger und Integer Werte einfach ein cast gelangt.

Nehmen wir jetzt mal an, wir haben eine einfache Datenmenge, die die Integer 1-100 auf eine Struktur abbilden. Man wird die erstaunliche Entdeckung machen, dass alle Werte nur in den Buckets 0-6 abgespeichert werden. Die Buckets 7 bis 16 werden nicht verwendet.

Der Nachteil ist offensichtlich. Man nutzt das Datenrauschen auf den untersten 4 Bits nicht.
Das macht sich sogar bei Fensterhandles bemerkbar. Bekanntlich sind das immer gerade Werte, aber auch auf den Bits 1-3 finden wir hier Informationen. Auch hier werden nützliche „zufällige“ Informationen nicht genutzt. Dadurch werden mehr Kollisionen in Kauf genommen als notwendig wären.

Ich möchte nicht unerwähnt lassen, dass die MFC Maps wirklich eine Berechtigung haben, weil sie grundsätzlich einen Pool-Allocator verwenden. Häufige Allokationen und Löschungen werden weitaus schneller behandelt, als durch die std::map, oder std::hash_map mit normalen Allocator.

Ich habe diesen Bug (oder miese Verhalten) bereits in der Beta für VC-2008 gemeldet. Es wurde aber nicht mehr geändert. Nun habe ich es an die Produktgruppe erneut  für VC-10 eingereicht.
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100772
Nachtrag 22.06.2009: Da der Bug von Microsoft nicht neu geöffnet wird habe ich einen neuen für VS-2010 Beta 1 angelegt.
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=468860
Wer Lust hat, der kann ja abstimmen.

BTW: Die HaskKey Funktion in afxtempl.h ist für andere Maps (z.B. CMapPtrToPtr) direkt in der Klasse definiert. Dieses Template wird nur vom CMap-Template verwendet.

VS Tipps & Tricks: Export/Import von Einstellungen zwischen VisualStudio 2005 und 2008

In einem meiner ersten Tipps habe ich das Thema Import und Export von Einstellungen schon behandelt schon behandelt: VS-Tipps & Tricks: Einstellungen für Visual Studio mehreren Arbeitsplätzen gleich halten.

Besonders wichtig ist das für mich immer mit den Tastatureinstellungen, die immer anpasse.

Was ich aber jetzt mit positivem Erstaunen feststellen konnte ist folgendes:
Man kann Einstellungen auch aus VS-2005 nach VS-2008 übertragen und umgekehrt ❗

Die XML Struktur des Exportformates ist nicht versioniert. Man kann direkt Einstellungen aus VS-2005 austauschen. Ideal wenn man Legacy Projekte noch behandeln muss und beide Systeme parallel fährt.

MSVSMON: Visual Studio Remote Debugging Monitor will not be able to debug applications because…

Da schreibe ich einen netten Artikel über Remote Debugging und schon geht es doch nicht so einfach wie ich schreibe.

Manch ein Entwickler wird von der folgenden Nachricht begrüßt, wenn er MSVSMON.EXE startet:

Visual Studio Remote Debugging Monitor will not be able to debug applications because the ‚Network access: Sharing and security model for local accounts‘ local security policy option is set to ‚Guest only – local users authenticate as Guest‘. This can be configured with the ‚Local Security Settings‘ administration tool.
Do you want to continue?    (Use /noguestonlywarn to suppress this warning)

Nun auch keine Tragik. Dann kommen wir aber um einen Neustart nach Änderungen der Lokalen Richtlinien nicht herum. Aber man muss erst mal wissen was mit dieser Meldung gemeint ist, wenn man ein deutsches OS benutzt:

  1. Systemsteuerung starten.
  2. Leistung und Wartung -> Verwaltung -> Lokale Sicherheitsrichtlinie
  3. In den Lokalen Sichereinstellungen auf
    Lokale Richlinien -> Sicherheitsoptionen
  4. Dort dann Netzwerkzugriff: Modell für gemeinsame Nutzung und Sicherheitsmodell für lokale Knoten auswählen.
  5. Umstellen auf: Klassisch – lokale Benutzer authentifizieren sich als sie selbst
  6. Reboot

HTH
Siehe auch:
How to: Set Up Remote Debugging
How to debug on computers that are running Windows XP and that are in the same Workgroup in Visual Studio .NET or in Visual Studio 2005