VS-Tipps & Tricks: Direkter Break in den Debugger bei einem ASSERT

ASSERTs in der MFC und in der CRT sind tolle Hilfsmittel, aber nicht selten verfälschen sie auch das Problem alleine dadurch, dass ein Fenster aufpoppt, wenn der ASSERT zuschlägt. Hat man nun einen Code, der in einem Tooltipp etwas Böses macht, dann wird der Tooltipp selbst aber schon wieder durch das erscheinen der ASSERT Meldung zerstört. Oder es wird ein neuer ASSERT ausgelöst. Der Callstack wird dadurch oft schwer zu lesen.
Besonders heikel kann dies auch noch werden wenn man mehrere Threads hat. Gleichfalls problematisch ist, dass in dem Moment in dem die ASSERT Box auftaucht nun auch wieder alle Timer weiterlaufen und sehr eigentümliche Seiteneffekte weiter auslösen können, dito. Probleme in WM_PAINT Handlern, denn auch die lösen evtl. schon wieder Aktionen aus, die Variablen verändern.

Nett ist am ASSERT-Dialog natürlich die Möglichkeit Ignorieren zu sagen und das Programm weiter laufen zu lassen. Ganz besonders wenn man Debug Versionen im Testfeld mit Anwendern testet.

Dennoch bin ich bei Debug-Versionen dazu übergegangen ASSERTs direkt  crashen zu lassen, bzw. direkt einen Debug-Break auszulösen. Das erleichtert das Lesen des Crashdumps bzw. hilft auch beim Debuggen, weil man direkt an der Stelle steht wo es hakt und alle Fenster und Variableninhalte exakt noch so sind, wie Sie es beim Auftreten des Problems waren (Tooltips, Popups, Menüs etc.).

Der Code um das zu erreichen ist relativ simpel. Man verwendet dazu _CrtSetReportHook2. In dem Hook sagt man einfach was man gerne hätte. Nämlich bei einem ASSERT oder ERROR keinen Dialog sondern einen Break (INT3).

#ifdef _DEBUG
int __cdecl DebugReportHook(int nReportType, char* , int* pnRet)
{
  // Stop if no debugger is loaded and do not assert, cause a crash
  // - returning TRUE indicates that we handled the problem, so no other hook
  //   needs to perform any action
  // - setting the target of *pnRet to TRUE indicates that the CRT should
  //   execute an INT3 and should crash or break into the debugger.
  return *pnRet = nReportType==_CRT_ASSERT ||
                  nReportType==_CRT_ERROR ?
                            TRUE : FALSE;
}
#endif

void SetBreakOnAssert(BOOL bBreakOnAssert/* =FALSE */)
{  
// Need to disable the ASSERT handler?
#ifdef _DEBUG  
  if (bBreakOnAssert)   
    _CrtSetReportHook2(_CRT_RPTHOOK_INSTALL, DebugReportHook); 
  else   
    _CrtSetReportHook2(_CRT_RPTHOOK_REMOVE, DebugReportHook);
#else
  UNUSED_ALWAYS(bBreakOnAssert);
#endif
}

Durch diese kleine Funktion SetBreakOnAssert kann man dieses Verhalten nun einfach ein- und ausschalten. Nähere Details stehen im Kommentar der Hook-Funktion.

50% Nachlass bis zum 31.08. bei Axialis auf den IconWorkshop

Der IconWorkshop von Axialis ist mein bevorzugter Editor für Bitmaps und Icons. Der IconWorkshop integriert sich in Vs-2005 und VS-2008 über ein Plugin.

Wer den IconWorkshop 6.5 noch nicht hat, der hat die Chance bis morgen den 31.08.2009 noch 50% Rabatt zu bekommen. Die Sommerrabatt Aktion gilt auch für alle anderen Axialis Produkte.

Alles weitere bei Axialis:
http://www.axialis.com/purchase/

Axialis

Da installiert man 64bit und schon fängt man an sich zu ärgern…

Ich hatte es befürchtet. Bisher bin ich mit allen meinen privaten PCs bei 32bit Betriebssystemen geblieben.

Nun habe ich Windows 7 und habe mich mutig für meinen netten Samsung Laptop für ein 64bit OS entschieden. Dann kann ich meine 4GB Speicher wenigstens auch komplett nutzen.

Und jetzt fange ich schon an mich zu ärgern, denn ich benutze gerne Groove 2007 (die Ordnersynchronisation), weil es einfach praktisch ist. Aber was sehen meine Augen beim einrichten des Grove Kontos auf dem Rechner:

Groove-Dateifreigabe-Arbeitsbereiche werden in 64-Bit-Betriebssystemen nicht unterstützt.

Nicht zu fassen… Überall wird ein 64bit Hype betrieben und viele Werkzeuge funktionieren darauf nicht.
Und schon fange ich mich zu ärgern 😐 Manchmal sollte man sich vorher klug machen bei dem was man will.

Probleme bei Installation des Sicherheitsupdates KB947319, Fehler Code A95

Am 11.08.2009 wurde das KB947319 Sicherheitsupdate für Office 2003 Web Components und Office XP Web Components in Office 2003  herausgegeben.
Dieses Updates lies sich bei mir auf einigen meiner Rechner (alle Vista) nicht installieren. Ich erhielt immer die Fehlermeldung A95.

Im Updateverlauf steht folgendes:

Sicherheitsupdate für Microsoft Office Web Components (KB947319)
Installationsdatum: ‎13.‎08.‎2009 10:29
Installationsstatus: Fehlgeschlagen
Fehlerdetails: Code A95

Ich habe danach versucht den Hotfix direkt herunter zu laden und die office2003-KB947319-FullFile-DEU.exe auszuführen. In diesem Fall erhielt ich die Fehlermeldungen:

Fehler 2709: Ein internen Fehler ist aufgetreten.
(Global_PIA_OWC11 )

und

Sicherheitsupdate für Office Web Components 2003 (KB947319)
Das Update konnte nicht angewendet werden.

Das eigentümliche ist allerdings, dass Office 2003 auf diesen Rechner selbst nicht installiert ist.
Da dieses Problem konsequent auf einer einer ganzen Reihe von Rechner auftrat habe ich den MS-Support bemüht. Jetzt nach einigen Tagen sieht es so aus, dass das ganze an einer Installation der MS-Access 2003 Runtime liegt.
Auch die MS-Access 2003 Runtime wurde als Ziel für diesen Patch eingetragen obwohl diese gar nicht betroffen ist.
Ich zitiere einfach mal den MS-Support:

Das Update KB947319 besteht aus 2 MSPs, OWC10.MSP und OWC11.MSP. Beide haben unter anderem die Access Runtime 2003 als Target eingetragen. Allerdings hat die Access Runtime 2003 gar keine OWC11, sondern nur die OWC10, insofern scheitert OWC11.MSP mit der Fehlermeldung:
Error 2709. An internal error has occurred. (Global_PIA_OWC11 )

Error 2709 heisst: „The specified Component name (‚[2]‘) not found in Component table“ was ja auch der Tatsache entspricht, denn die Komponente OWC11 ist ja tatsächlich nicht in Access Runtime 2003 enthalten.

Das Problem in diesem Fall ist, dass die Access Runtime 2003 in OWC11.MSP überhaupt als Target eingetragen ist. Das bedeutet für Sie zunächst einmal, dass trotz des Problems bei der Installation des Fehler im Moment kein  Sicherheitsrisiko besteht, durch den Umstand, dass OWC11.MSP in der Access Runtime 2003 nicht installiert werden kann, da es keine OWC11 gibt, die man updaten könnte.

Es geht momentan noch darum, dass KB947319 von der Patch Detection weiterhin als applicable angezeigt wird, solange der Patch für die OWC11.MSP bezüglich der Access Runtime 2003 einen Fehler zurückliefert.

Wir sind aktuell dabei, einen Regression Hotfix für diesen Patch zu beantragen.

Aktuell habe ich dieses Sicherheitsupdate ausgeblendet. Bis die Regression durch ist.

Nachtrag 07.09.2009:
Ich bitte davon abzusehen weiter nach Informationen bei mir persönlich nachzufragen. Ich werde über die Veröffentlichung eines öffentlichen Fixes zu gegebener Zeit berichten.
Man kann sich ohne Probleme selbst an den Microsoft Support wenden und seine Probleme bzgl. des KB947319 schildern. Die Probleme sind hinlänglich bekannt und sofern die Indikatoren übereinstimmen sollte man auch einen provesorischen Fix vom Microsoft Support erhalten können.

Mit Datum vom 07.09.2009 sperre ich weitere Kommentare.

Nachtrag 26.10.2009
Endgültiger Fix für die Probleme bei Installation des Sicherheitsupdates KB947319, Fehler Code A95

VS-Tipps&Tricks: Einfache Debug-Ausgabe mit TRACE auch in der Release Version

Wer wollte nicht schon immer mal gerne TRACE (Debug)-Ausgaben in seinem Release Programm haben ohne dafür überall OutputDebugString reinschreiben zu müssen.

Die nachfolgene kleine Klasse macht es möglich, den gewohnten Syntax des MFC TRACE Makros zu verwenden und direkt auf die Debugausgabe umzuleiten:

//    CTraceToOutputDebugString
//        Is a nice replacment class for TRACE
//        Easy to use with:
//            #undef TRACE
//            #define TRACE    CTraceToOutputDebugString()

class CTraceToOutputDebugString
{
public:
    // Non Unicode output helper
    void operator()(PCSTR pszFormat, ...)
    {
        va_list ptr;
        va_start(ptr, pszFormat);
        TraceV(pszFormat,ptr);
        va_end(ptr);
    }

    // Unicode output helper
    void operator()(PCWSTR pszFormat, ...)
    {
        va_list ptr;
        va_start(ptr, pszFormat);
        TraceV(pszFormat,ptr);
        va_end(ptr);
    }

private:
    // Non Unicode output helper
    void TraceV(PCSTR pszFormat, va_list args)
    {
        // Format the output buffer
        char szBuffer[1024];
        _vsnprintf(szBuffer, _countof(szBuffer), pszFormat, args);
        OutputDebugStringA(szBuffer);
    }

    // Unicode output helper
    void TraceV(PCWSTR pszFormat, va_list args)
    {
        wchar_t szBuffer[1024];
        _vsnwprintf(szBuffer, _countof(szBuffer), pszFormat, args);
        OutputDebugStringW(szBuffer);
    }
};

Durch den obenstehenden Code kann man auch in einer Release Version Trace Ausgaben erzeugen und z.B. mit DebugView.exe (Sysinternals) sichtbar machen, ohne evtl. weitere Anpassungen vornehmen zu müssen:

// Activate my special tracer
#undef TRACE
#define TRACE    CTraceToOutputDebugString()

void Foo()
{
     // Sometime usefull to see the output in a release version too
     TRACE(__FUNCTION__ " called at %d\n", GetTickCount());
}

Nach IE8 Installation öffnet ein Doppelklick auf einen Ordner im Explorer immer ein neues Fenster

Ich habe auf einem 2008er Server auf IE8 umgestellt.
Seitdem öffnete der Explorer (nicht IE) alle Ordner in einem neuen Fenster. Die Einstellungen unter Extras -> Orderoptionen -> Allgemein sind jedoch korrekt eingestellt.

Bei einigem hin und herspielen zeigte sich, dass im Kontext Menü korrekt Explorer fett dargestellt wird.
Wähle ich auf einem Order aus dem Kontextmenü Explorer aus, wird dieser Ordner im selben Fenster geöffnet.
Mache ich einen Doppelklick dann öffnet sich ein neues Fenster erzeugt, so wie wenn man im Kontextmenü den Befehl Öffnen auswählt.

Eine Recherche im Internet ergab, dass ich nicht alleine bin. Ich habe danach auch einige Beiträge zu diesem Problem bzgl. XP gefunden und einige Registry Einträge in HKCR (Drive/Folder/Directory) mit einem Vista System verglichen und die sind alle identisch. Alle Lösungen, die ich im Netz fand haben mir nicht geholfen.

Ich habe dann versucht einige Explorer DLLs neu registrieren. DLLs, die auch in anderen Artikeln zu Explorer Problemen erwähnt wurden. Die Lösung kam ziemlich schnell mit der erneuten Registrierung der actxprxy.dll.

Die Lösung sieht also wie folgt aus:
cmd Prompt als Administrator öffnen und den folgenden Befehl ausführen:

regsvr32 actxprxy.dll

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: