Feature Request: Always ask the developer before applying a service pack or a security fix to Visual Studio that need changed C++ runtime DLLs ATL/MFC/CRT

I know this is a German blog, but for reaching a wider range of developer this article is written in English 🙂

In the past security patches to Visual Studio were automatically installed on the machines of developers. This might have a great impact of to the shipment of the software that is created with this new pachted Visual-Studio version and it might cause incompatibilities with previous created modules.

And we all suffered under the problems that came with this patches and I don’t want to know how much time and money was wasted here.

Also I am aware of the risk that is caused when security fixes are not applied. But the last decision must be allowed to a developer if a fix is applied or not.

To avoid this I have a feature request that such security fixes to Visual Studio (any Version: VS-2005, VS-2008, VS-2010) is only applied to the developers machine if he is asked to do this!

Please use your vote and your words to comment this feature request ❗
Here is the link:

Always ask the developer before applying a security fix or service pack to Visual Studio that need changed the C++ runtime DLLs ATL/MFC/CRT

BUG: Black Patchday for all OS from XP and later 3. – MFC 8.0 (VC-2005) or MFC 9.0 (VC-2008) linked dynamically to the MFC may not find the MFC language DLLs after installation of the security packs dated April 12th 2011

This is the English translation of the already published German article:
BUG: Schwarzer Patchday für alle OS XP und später 3. – MFC 8.0 (VC-2005) oder MFC 9.0 (VC-2008) die dynamisch gelinkt wurden finden die MFC Sprach-DLLs evtl. nicht mehr nach Installation der Sicherheitspatches vom 12.04.2011

Affected are:

  • All programs created with MFC 8.0 and MFC 9.0 that link dynamically to the MFC DLLs .
  • All operating systems from Windows XP and later. 32bit as 64bit
  • Al programs that do not use an application local installation (program directory, see note at the bottom of the article). So all programs that use and depend on WinSxS and VCRedist_x86.exe ( VCRedist_x64.exe).
  • All programs that are localized and use the MFC90xxx.DLL or. MFC80xxx.DLL language-DLLs and the OS system language is not set to English.

It is affected due to the security fixes offered April 12th, 2011:

For VS-2005 SP1 http://support.microsoft.com/kb/2465367 and http://support.microsoft.com/kb/2467175
For VS-2008 SP1 http://support.microsoft.com/kb/2465361 and http://support.microsoft.com/kb/2467174

Failure description:

The MFC language DLLs (satellite DLLs) are not loaded any longer. Parts of the application appear in English and not the selected language from the OS.

Background:

To prevent loading of wrong satellite DLLs (Binary Planting), an internal function in appcore.cpp named _AfxLoadLangDLL was changed. It checks if an activation context is active or not and if the DLLs should be loaded using this context. If there is an activation context active it is safe to load the satellite DLLs(MFCDEUxxx.DLL etc.) without defining a full path. If no activation context is active the path of the current application is used to load and find the satellite DLLs. The DLLs are loaded with a call to LoadLibrary.

The code used looks like this (empty lines removed):

...
TCHAR *pszFilename = ::PathFindFileName(szLangDLL);
ACTCTX_SECTION_KEYED_DATA data;
if (FindActCtxSectionString(
    FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX,
    NULL,
    ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
    pszFilename,
    &data) )
{
    // Load using the dll name only...
    hInstance = ::LoadLibraryEx(pszFilename, NULL, 0);
}
else
{
    // Load using the full path...
    hInstance = ::LoadLibraryEx(szLangDLL, NULL, 0);
}
...

The code looks OK.  And it is conform to the documentation of FindActCtxSectionString where the last parameter is defined as __out.

BOOL FindActCtxSectionString(
  __in   DWORD dwFlags,
  __in   const GUID *lpExtensionGuid,
  __in   ULONG ulSectionId,
  __in   LPCTSTR lpStringToFind,
  __out  PACTCTX_SECTION_KEYED_DATA ReturnedData
);

But the documentation of ACTCTX_SECTION_KEYED_DATA tells a different story:

Callers should initialize the ACTCTX_SECTION_KEYED_DATA structure as such:
„ACTCTX_SECTION_KEYED_DATA askd = { sizeof(askd) };“
which initializes all members to zero/null except the size field which is set correctly.

(BTW: In my eyes a documentation failure)

So what we see is that the code misses this: data.cbSize isn’t initialized
Now we have 3 possible scenarios what can happen with a  randomly initialized data.cbSize field:

  1. data.cbSize is larger than sizeof(ACTCTX_SECTION_KEYED_DATA):
    In this case the activation context is correctly detected. The program executes normal.  With an activation context no full path is needed. The MFC90xxx.DLL will be loaded from the WinSxS (Side by Side) or found over the common search path.
  2. data.cbSize is less than  sizeof(ACTCTX_SECTION_KEYED_DATA):
    In this case FindActCtxSectionString returns with an error. The DLL is now loaded with a full path name constructed from the application directory to prevent Binary Planting. Butthe problemis that with a normal installation the searched files are all in WinSxS, and the application directory has no such data. The DLL is not loaded.
    If the application local assemblies are used and placed in sub directories they aren’t found either.
  3. A future problem.
    If an OS will use a larger ACTCTX_SECTION_KEYED_DATA and data.cbSize has a greater value than the corresponding sizeof(…):
    We have a buffer-overrun!

I always recommend to use private and application local assemblies for the CRT and MFC DLLs. And to install all this files local to the application.
Years ago I wrote an article for this scenario that was published on CodeProject and a hotfix for VS-2008 is also available
Create projects easily with private MFC, ATL and CRT assemblies
Hotfix für UseMSPrivateAssemblies.h und VC-2008

What to do?

Uninstall all of the mentioned security fixes with the specified article IDs.
Runtime-2005: KB2467175, Runtime-2008: KB2467174
VS-2007 SP1: KB2465367, VS-2008 SP1: KB2465361).

Further notes:

The affected C/C++ Runtimes of Visual Studio have the following version numbers:
– VC-2005 8.0.50727.5592 (KB2467175)
– VC-2008 9.0.30729.5570 (KB2467174)

My comment to tis issue:
It was easier to live with the DLL-hell. 🙁

Many thanks to my Co-MVP Mike Ryan who helped me to discover this problems with the latest security patches:!:

What Do I mean with „application local“?
Some people ship the MFC files in the application directory. In such a case this DLLs are not loaded if a newer version can be found in the WinSxS directory. This is not application local for me!
So if the manifest file in the program directory still have a publicKey entry, the local files will be used  in case of the here described bug. Even if the activation context was not detected, so the local files are a kind of fallback and help prevent get around the problem.
My articles describe how to make your application really application local in removingthe publicKey tokens from the manifest files. Such programs will never fail on such broken security patches. (Just read my article at Codeproject). (Thanks for Co-MVP David Ching who asked me for a clarification)

BUG: Schwarzer Patchday für alle OS XP und später 3. – MFC 8.0 (VC-2005) oder MFC 9.0 (VC-2008) die dynamisch gelinkt wurden finden die MFC Sprach-DLLs evtl. nicht mehr nach Installation der Sicherheitspatches vom 12.04.2011

Betroffen sind:

  • Alle Programme die mit MFC 8.0 oder MFC 9.0 erzeugt wurden und dynamisch an die MFC DLLs gelinkt sind.
  • Alle Betriebssysteme ab Windows XP aufwärts. 32bit wie 64bit
  • Alle Programme, die nicht die MFC und CRT DLLs applikationsnah (d.h. im Programmverzeichnis, siehe dazu auch die Fußnote in meinem Artikel) installiert haben. Also alle Programme die WinSxS benutzen und die VCRedist_x86.exe ( VCRedist_x64.exe)  mit ausliefern.
  • Alle Programme, die lokalisiert sind und die MFC90xxx.DLL bzw. MFC80xxx.DLL Sprach-DLLs verwenden und das OS nicht auf Englisch eingestellt ist

Betrifft die folgenden Fixes vom 12.04.2011:

Für VS-2005 SP1 http://support.microsoft.com/kb/2465367 und http://support.microsoft.com/kb/2467175
Für VS-2008 SP1 http://support.microsoft.com/kb/2465361 und http://support.microsoft.com/kb/2467174

Effekt:

Die MFC Statelite DLLs werden nicht geladen. Teile der Anwendung erscheinen in englischer Sprache.

Hintergrund:

Um das Laden von falschen Satelite DLLs zu verhindern (Binary Planting), wurde intern in appcore.cpp in der Funktion _AfxLoadLangDLL geprüft, ob die DLLs in aus einem Activation Context geladen werden oder nicht. Sollte ein Activation Context vorhanden sein, dann kann man gefahrlos die Sprach DLLs (MFCDEUxxx.DLL etc.) ohne Pfadnamen laden. Ist kein Activation Context vorhanden wird der Pfad der Anwendung verwendet und LoadLibrary mit vollem Pfadnamen durchgeführt.

Der Code der dazu verwendet wird sieht so aus (Leerzeilen entfernt):

...
TCHAR *pszFilename = ::PathFindFileName(szLangDLL);
ACTCTX_SECTION_KEYED_DATA data;
if (FindActCtxSectionString(
    FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX,
    NULL,
    ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
    pszFilename,
    &data) )
{
    // Load using the dll name only...
    hInstance = ::LoadLibraryEx(pszFilename, NULL, 0);
}
else
{
    // Load using the full path...
    hInstance = ::LoadLibraryEx(szLangDLL, NULL, 0);
}
...

Eigentlich sieht der Code prima aus. Und er verträgt sich auch mit der Doku von FindActCtxSectionString dort wird der letzte Parameter als __out definiert.

BOOL FindActCtxSectionString(
  __in   DWORD dwFlags,
  __in   const GUID *lpExtensionGuid,
  __in   ULONG ulSectionId,
  __in   LPCTSTR lpStringToFind,
  __out  PACTCTX_SECTION_KEYED_DATA ReturnedData
);

Aber die Doku zu ACTCTX_SECTION_KEYED_DATA sagt was anderes:

Callers should initialize the ACTCTX_SECTION_KEYED_DATA structure as such:
„ACTCTX_SECTION_KEYED_DATA askd = { sizeof(askd) };“
which initializes all members to zero/null except the size field which is set correctly.

(BTW: Auch ein krasser Doku-Bug in meinen Augen)

Und jetzt sieht man was dem Code fehlt: data.cbSize wird nicht gesetzt
Daraus ergeben sich nun drei Varianten, da data.cbSize nun zufälligen (nicht initialisierten) Inhalt hat:

  1. data.cbSize ist größer als  sizeof(ACTCTX_SECTION_KEYED_DATA):
    In diesem Fall wird korrekt ermittelt ob ein Activation Context vorhanden ist. Das Programm läuft normal. Mit Activation Context ist kein voller Pfadname nötig. Die MFC90xxx.DLL wird evtl. aus dem WinSxS (Side by Side) geladen, oder in einem der Suchpfade gefunden.
  2. data.cbSize ist kleiner als  sizeof(ACTCTX_SECTION_KEYED_DATA):
    In diesem Fall liefert FindActCtxSectionString einen Fehler und nun wird es spannend. Die DLL wird nun versucht mit dem vollen Pfadnamen zu laden um Binary Planting zu verhindern. Das Problem ist aber dass bei korrekter Installation im WinSxS, dass im Applikationsverzeichnis keine dieser Daten liegen. Die DLL wird nicht gefunden.
    Sollten die private applikationsnahe Assemblies in einem Unterverzeichnis installiert sein, werden diese auch nicht gefunden.
  3. Für die Zukunft.
    Ein zukünftiges OS vergrößert ACTCTX_SECTION_KEYED_DATA und data.cbSize hat zufälligen Inhalt und ist größer als sizeof(…):
    Ein Buffer-Overrun!

Ich empfehle nicht ohne Grund seit VS-2005 private Assemblies zu verwenden, und die MFC Dateien in das Anwendungsverzeichnis zu kopieren. Dazu habe ich auf Code-Projekt einen entsprechenden Artikel geschrieben und ein Hotfix für VS-2008 existiert auch ❗
Create projects easily with private MFC, ATL and CRT assemblies
Hotfix für UseMSPrivateAssemblies.h und VC-2008

Was ist zu tun?

Deinstallation aller hier erwähnten Sicherheitspatches mit den entsprechenden Arikelnummern:
Runtime-2005: KB2467175, Runtime-2008: KB2467174
VS-2007 SP1: KB2465367, VS-2008 SP1: KB2465361).

Weitere Anmerkungen:

Die betroffenen C/C++ Runtimes des Visual Studio haben die folgenden Versionsnummern
– VC-2005 8.0.50727.5592 (KB2467175)
– VC-2008 9.0.30729.5570 (KB2467174)

Mein Kommentar dazu:
Das Leben in der DLL-Hölle war fast angenehmer als das hier. Ohne Worte 🙁

Herzlichen Dank auch an meinem Mit-MVP Mike Ryan, der mit mir zusammen auf diese gesamte Problematik gestoßen ist ❗

Was meine ich mit „application local“?
Einige Entwickler installieren die MFC runtme Dateien im Applikationsverzeichnis. In diesem Fall werden diese DLLs nicht verwendet wenn eine neuere Version der DLLs im WinSxS Verzeichnis liegen. Das ist für mich keine applikationsnahe Instalation! Diese Manifeste im Programmverzeichnis haben immer noch einen publicKey Eintrag. Aber durch die Existenz der lokalen Dateien wird dieses hier beschriebene Problem umgangen, weil die lokalen Dateien eine Art Fallback bilden.
Meine Artikel beschriben wie man eine Anwendung wirklich applikationslokal macht und damit unabhängig von solchen „kaputten“ Security Patches. Dazu muss der publicKey Token aus den Manifesten entfernt werden. (Lesen Sie meinen Artikel aufCodeproject).
(Danke an Co-MVP David Ching der mich um Kläurung gebeten hat)

BUG: Schwarzer Patchday für Windows 2000 2.- MFC 8.0 (VC-2005) und MFC 9.0 (VC-2008) Static Libraries erzeugen auch inkompatiblen Code für Windows 2000 durch KB2465367 bzw. KB2465361

Wer VS-2005 SP1 oder VS-2008 SP1 installiert hatte und bei dem auch die entsprechenden Patches von gestrigen Tag (12.04.2011) durchlaufen wurden, der hat nun auch veränderte statische Libraries.

Sollte man nun also EXEs oder DLLs mit den neuen Libararies statisch linken, dann sind diese genausowenig lauffähig unter Windows 2000. wie auch die EXEs und DLLs die gegen die MFC 8.0 bzw. MFC 9.0 DLLs gelinkt werden

Das Ganze ist hier aufgelistet:
für VS-2005 SP1 http://support.microsoft.com/kb/2465367
für VS-2008 SP1 http://support.microsoft.com/kb/2465361

Die LIBs sind aufgeführt und auch diese verwenden auch die Funktion FindActCtxSectionStringA, die natürlich nicht unter Windows 2000 vorhanden ist.

Siehe auch:
http://blog.m-ri.de/index.php/2011/04/13/bug-schwarzer-patchday-fur-windows-2000-mfc-8-0-vc-2005-und-mfc-9-0-vc-2008-dlls-sind-nicht-mehr-lauffahig-nach-installation-von-kb2467175-bzw-kb2467175/

PS: Ich kann nur raten die entsprechenden Patches zu deinstallieren sofern man noch für Windows 2000 entwickelt und warten bis neue Securitypatches vorhanden sind.

Nachtrag:
Die betroffenen C/C++ Runtimes des Visual Studio haben die folgenden Versionsnummern
– VC-2005 8.0.50727.5592 (KB2467175)
– VC-2008 9.0.30729.5570 (KB2467174)

BUG: Schwarzer Patchday für Windows 2000 – MFC 8.0 (VC-2005) und MFC 9.0 (VC-2008) DLLs sind nicht mehr lauffähig nach Installation von KB2467174 bzw. KB2467175

Ein wirklich übler Patchday von Microsoft für ale Windows 2000 Nutzer.
In den Sicherheitsfixes für Visual Studio 2005 und 2008 wurden auch die Runtime Module der MFC 8.0 und MFC 9.0 „gefixed“.
Siehe http://support.microsoft.com/kb/2467175 für VS-2005
Siehe http://support.microsoft.com/kb/2467174 für VS-2008

Es wurde ein Fix eingebaut, den wir schon in VS-2010 gesehen haben, der vermeiden soll, dass Satelite DLLs nur noch aus dem Verzeichnis geladen werden, in dem auch die MFC DLLs liegen
(gegen ‘Binary Planting’, d.h. unterschieben gefakter DLLs aus anderen Verzeichnissen).

Leider wurde dabei die Funktion FindActCtxSectionStringA mit eingebaut und verwendet. Eine Funktion die aber erst ab Windows XP vorhanden ist. Sofern man also eine Anwendung unter Windows 2000 hat, die die MFC80.DLL, MFC80U.DLL, MFC90.DLL oder MFC90U.DLL aus dem WINDOWS\SYSTEM32 Verzeichnis verwendet, dann hat man seit dem dem Patchday von gestern ein massives Problem. Die DLLs können nicht mehr geladen werden. 😯

Einziger Rat ist hier, die neuen Dateien löschen und die ungepatchten Versionen installieren und das am besten direkt in den Programmverzeichnissen ❗
Alte Runtimes für VS-2005 SP1 http://www.microsoft.com/downloads/en/details.aspx?FamilyID=200b2fd9-ae1a-4a14-984d-389c36f85647
Alte Runtimes für VS-2008 SP1 http://www.microsoft.com/downloads/en/details.aspx?FamilyID=a5c84275-3b97-4ab7-a40d-3802b2af5fc2
Alle Versionen mit den VCRedist_x86.exe Versionen und dem Datum vom 13.04.2010 sind für Windows 2000 tabu.
Das heißt auch, dass man diese DLLs nicht in seinem eigenen Setup für Windows 2000 ausrollen sollte.

Nicht betroffen sind Applikationen, die diese DLLs applikationsnah, d.h. im Programmverzeichnis installieren.
Eine Methode, die ich seit Jahren empfehle!

Nur noch einmal zur Klarstellung: Der Sicherheitspatch ist nur problematisch für Windows 2000 Rechner ❗

PS: Was bin ich froh, dass wir Windows 2000 schon lange nicht mehr unterstützen :mrgreen:

PPS: Bei solch einem Fehler frage ich mich ob hier irgend jemand überhaupt etwas getestet hat, außer das die Installation durchgeführt wird.

Nachtrag:
Die betroffenen C/C++ Runtimes des Visual Studio haben die folgenden Versionsnummern
– VC-2005 8.0.50727.5592 (KB2467175)
– VC-2008 9.0.30729.5570 (KB2467174)

Workaround:
Workaround für Patchday Bug vom 12.04.2011: Wenn unter Windows 2000 der Einsprungpunkt FindActCtxSectionStringA nicht gefunden wird

virtuelle Funktionen und const sind immer wieder eine Freude… oder wie C++0x einen Fehler verhindert hätte…

Wieder mal hat sich in unserer Software ein kleiner und ganz mieser Fehler eingeschlichen, obwohl der Entwickler „eigentlich“ an alles gedacht hatte.

class A
{
...
    virtual void Doit() const;
...
};

class DerivedA : public A
{
...
    virtual void Doit(); 
...
};

void Foo()
{
...
    A *pA = Something();
...
    pA->Doit();
...
}

Sieht alles gut aus. Leider fehlt das kleine nette Wort const beim Überscheiben der Funktion Doit und daraus folgt, dass DerivedA::Doit niemals aufgerufen wird.

Obwohl wir VC-2010 verwenden, hat der neue C++0x Standard noch keinen Einzug in unseren Köpfen gefunden.
Das kleine Wort override in DerivedA hätte diesen Fehler verhindert.

class DerivedA : public A
{
...
    virtual void Doit() override;
// This line results in:

// error C3668: 'DerivedA::Doit' :
// method with override specifier 'override' did
// not override any base class methods
...
};

Es wird wirklich Zeit, dass C++0x auch wirklich genutzt wird, aber wie alles Neue muss man es sich besonders am Anfang immer wieder bewusst machen, damit es einem direkt zur Angewohnheit wird.
override ist sogar schon in VC-2008 und dem nativen Compiler implementiert. Allerdings wundert es mich, dass hier nicht dann auch gleich das Schlüsselwort new eingeführt wurde.
Aber das man unter dem gleichen Namen wirklich einen neuen Slot in der vtable aufmacht ist wirklich eher selten. Ich persönlich hatte dafür bisher noch keine Verwendung. 

Leider ist C++0x  nicht komplett in der C++ Doku in der MSDN hervorgehoben. Man kann nicht mal erkennen, dass sealed und abstract MS-Extensions sind, und override im Standard verankert ist. Schade…

Damit man sich etwas besser einen halbwegs kompletten Überblick verschaffen kann habe ich hier ein paar Links zusammengestellt.

Siehe auch:

Minidumps ganz einfach

Manchmal, wenn man ein kleines Programm entwickelt mag es als Overkill erscheinen extra Code für Minidumps einzubauen.
Was aber, wenn man doch einen Fehler aufspüren möchte und ein Minidump ad hoc ganz praktisch wäre?

Unter Vista und Windows 7 ist es ganz einfach in den WER Einstellungen Einträge vorzunehmen, mit denen man mit nur ein paar Registry Einträgen sofort zu Minidumps kommt.

Nachfolgend die Registry Einträge, die einen Fulldump im Verzeichnis %LOCALAPPDATA%\CrashDumps erzeugen.

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps]
"DumpType"=dword:00000001
"DumpCount"=dword:00000010
"DumpFolder"=hex(2):25,00,4c,00,4f,00,43,00,41,00,4c,00,41,00,50,00,50,00,44,\
00,41,00,54,00,41,00,25,00,5c,00,43,00,72,00,61,00,73,00,68,00,44,00,75,00,\
6d,00,70,00,73,00,00,00

Eine vollständige Liste der Einstellungen findet sich in der MSDN Doku:
http://msdn.microsoft.com/en-us/library/bb787181(VS.85).aspx

PS:
Ich benutze diese Einstellungen aktuell auch einem Bug in VisualStudio 2010 auf die Spur zu kommen, damit ich Crashdumps regelmässig an http://connect.microsoft.com übertragen kann.

PPS: (14.01.2011 nach Hinweis von André)
Wie man auch der Doku ennehmen kann ist fpr dieses Funktion Vista SP1, Windows 2008 Server oder Windows 7 notwendig. Vista RTM hat diese Funktion nicht.

Nette Falle im SQL-Server: Der Kompatibilitätsgrad

Nach einem der letzten Updates unserer Software meldete uns ein Kunde einen SQL Fehler, der bei einer bestimmten Operation auftrat. Er setzt den MS-SQL Server 2008 ein.

OK, meine Testumgebung hat drei Server von SQL 2000, über 2005 bis 2008 R2. Keine der Testumgebungen brachte bei der entsprechenden gleichen Operation einen Fehler 😕 Gut oder besser schlecht… Der Kunde bekommt nun eine Fehlermeldung und auch wenn Kunden meistens ja nicht recht haben wenn sie Fehler melden 😀 schaute ich mir dennoch alle SQL Befehle etwas genauer an, die meine Software da auslöste.
In dem entsprechenden Teil meiner wurde nach Benutzerangaben ein relativ komplexer Query durch einen Abfragegenerator zusammengebaut. Darunter fand sich auch der folgende Subquery, als Teil der gesamten Abfrage:

SELECT a.[Id] FROM [tblXYZ] AS a
  WHERE
    (((a..[IdParent] IS NULL
       AND a..[Id] NOT IN
         (SELECT [IdXYZ]
            FROM [tblSomething]
              WHERE [IdParent] IS NOT NULL))))

Unschwer zu sehen werden hier mit dem Alias a zusammen irgendwie zwei Punkte verwendet. Bleibt die Frage warum in meiner Umgebung nun kein Fehler passiert und beim Kunden ein nun Syntax Fehler ausgelöst wird.

Nach einigem Suchen fand ich die Ursache im Kompatibilitätsgrad, den man im Managementstudio unter Datenbank -> Datenbankname -> Eigenschaften -> Optionen je Datenbank separat einstellen kann. Dort sind folgende Einstellungen möglich.

SQL Server 2000 (80)
SQL Server 2005 (90)
SQL Server 2010 (100)

In meiner Testumgebung verwende ich eine Datenbank, die seit den ersten Anfängen unserer Software immer weiter als Testumgebung mit vielen Testdaten dient. Sie wurde erstmals auf einem SQL Server 2000 angelegt. Dann auf einen 2005er und schließlich auf einen SQL Server 2008 R2 umgezogen. Netterweise – oder besser dummerweise – hat sich der SQL Server bei jeder Umstellung die ehemalige Kompatibilität gemerkt. Und man staunt nicht schlecht: Auf einem SQL Server 2000 ist es kein Fehler zwischen Alias und Spaltennamen zwei Punkte zu schreiben. Bei einem SQL Server 2005 oder später ist das sehr wohl ein Syntaxfehler.
Der Fehler lag also doch bei uns – was ja wirklich selten vorkommt 😀 – und wurde trotz genauer Tests nicht entdeckt.

Man merke sich: SQL Server Syntax ist trotz gleicher SQL Server Version eben doch lange nicht das selbe.
Wer also Software auf einem SQL Server testet sollte tunlichst darauf achten welchen Kompatibilitätsgrad er benutzt ❗

Mal einen ganz anderen Blick auf seinen Code werfen mit CppDepend

Vor einiger Zeit habe ich versucht mit einem anderen Werkzeug mal den eigenen Code zu analysieren.
Ich habe CppDepend entdeckt.  http://www.cppdepend.com/

Gerade bei großen Projekten kann man schnell den Überblick verlieren und es ist schwierig die abhängigen Klassen und Objekte in Ihren Verbindungen zu sehen oder mögliche Designprobleme nachträglich festzustellen. Oder den richtigen Ansatzpunkt für Reviews zu finden. Man hat manchmal das Gefühl, dass etwas nicht stimmt, aber man weiß oft nicht genau was.

CppDepend kann helfen schlecht konstruierte Klassen zu finden, Fehler in den Namenskonfentionen oder der Dokumentation und gezielter auf notwendige Reviews hinzuweisen.
Durch eine eigene Abfragesprache ist es extrem einfach große unübersichtliche Funktionen zu finden. Klassen mit zyklischen Abhängigkeiten und extreme Vererbungstiefen aufzuspüren. Besonders einfach wird es zum Beispiel auch Namenskonventionen zu überprüfen. Die eigene Codebase wird durch die Abfragesprache analysierbar.

Ich empfand die graphischen Darstellungen der eigenen Codebasis und die Ergebnisse der Abfragen in diesen Grafiken als extrem anschaulich und nützlich. Weitaus mehr als manche tabellarische Analyse meines Codes.

Einen guten Einblick was hier möglich ist liefern die Case-Studies. Wie zum Beispiel die Analyse der MFC 8.0 (VC-2005) http://www.cppdepend.com/MFC.aspx. Man kann die MFC sicherlich nicht als Glanzstück des OOP bezeichnen, umso mehr ist es mal interessant mit diesem Tool einen Blick auf die MFC zu werfen. Wem das nicht anschaulich genug ist kann sollte sich auch die anderen Case-Studies mal ansehen. Oder noch besser die Software 2 Wochen testen.

Ein gutes Tool, aber leider nicht ganz billig, aber in jedem Fall mal einen Blick wert.

Crash durch unsachgemäßen Umgang mit PreTranslateMessage

Neulich bei einem Codereview, lief mir Code in den beiden nachfolgenden Formen über meinen Monitor:

BOOL CMyDialog::PreTranslateMessage(MSG *pMsg)
{
    // Translate Messages (ON_KEYDOWN -> ON_COMMAND)
    TranslateAccelerator(m_hWnd,m_hAccel,pMsg);
    // Let the ToolTip process this message.
    m_tooltip.RelayEvent(pMsg);
    return __super::PreTranslateMessage(pMsg);
}

– bzw.  –

BOOL CMyWnd::PreTranslateMessage(MSG *pMsg)
{
  BOOL bResult = __super::PreTranslateMessage(pMsg);
  DoSomething();
  return bResult;
}

Sieht OK aus, aber hier tritt ein grundsätzliches Problem auf:

In beiden Funktionen wird evtl. eine Nachricht behandelt. Allerdings wird in diesem Fall nicht umgehend die Funktion verlassen. Durch das Behandeln der Nachricht kann nämlich das Fenster/Objekt, zu dem PreTranslateMessage gehört, bereits zerstört sein. In diesem Fall kehrt PreTranslateMessage zurück und der this Zeiger ist bereits ungültig.

Es ist also imminent wichtig in dem Moment in dem erkannt wird, dass die Nachricht behandelt wurde, auch umgehend die Funktion mit TRUE zu verlassen und keine weitere Memberfunktion oder gar Membervariable mehr zu nutzen. Beides könnte zu einem üblen Crash führen.

Der korrekte Code sähe also so aus:

BOOL CMyDialog::PreTranslateMessage(MSG *pMsg)
{
  // Translate Messages (ON_KEYDOWN -> ON_COMMAND)
  if (TranslateAccelerator(m_hWnd,m_hAccel,pMsg))
    return TRUE;
  // Let the ToolTip process this message.
  m_tooltip.RelayEvent(pMsg);
  return __super::PreTranslateMessage(pMsg);
}

– oder –

BOOL CMyWnd::PreTranslateMessage(MSG *pMsg)
{
  if (__super::PreTranslateMessage(pMsg))
    return TRUE;
  DoSomething();
  return FALSE;
}

PS: Beide Codeteile wurden durch Crashdumps aus WinQual gefunden. Regelmäßig, alle 2 Monate schaue ich mir Dumps an, von den Top-Crashes, die dort verzeichnet sind, und mache entsprechende Code-Reviews.
Der erste Code, stammte aus einem speziellen nicht modalen Dialog, der durch Drücken bestimmter Tastenkombinationen geschlossen und zerstört wurde.
Der zweite Code stammte aus einem Popup-Fenster, dass auch durch Mausaktivitäten oder Tastendrücke zerstört wurde.
Ich kann jedem nur raten WinQual auch zu nutzen, es dient der Qualtitätssicherung und man findet viele kleine Bugs, die manchen User ärgern, die aber nie sonst gemeldet würden.