vcredist_x86.exe VC-2005 SP1 gibt es jetzt auch als separaten Download

Visual C++ 2005 Express Edition Nutzer mussten bislang in die Röhre sehen.
vcredist_x86.exe in der SP1 Version gab es nicht als separaten Download.
D.h. sie konnten den SP1 für VC++ 2005 Express Edition installieren und nutzen, aber Ihre Programme nicht mit der neuen SP1 CRT ausliefern.

Das ist nun vorbei:
http://www.microsoft.com/downloads/details.aspx?FamilyID=200B2FD9-AE1A-4A14-984D-389C36F85647&displaylang=en

Alle Nutzer des Visual Studios 2005 finden die Merge Module in Ihrem Verzeichnis C:\Programme\Gemeinsame Dateien\Merge Modules
bzw. hier als Executable
C:\Programme\Microsoft Visual Studio 8\SDK\v2.0\BootStrapper\Packages\vcredist_x86\vcredist_x86.exe

BTW: Die neue Datei benötigt auch nur die 2.0 Version des MSI und nicht die 3.0 Version die die RTM Version!
Weitere Infos und Links zu den 64bit Versionen gibt es auch auf Nikolar Dudars Blog.

Bug in CPropertyPage und „Certified for Vista“

Der von mir gemeldete Bug Bug in der MFC71.DLL bzgl. CPropertySheet/CPropertyPage führt dazu, dass ein Programm, dass den genannten Voraussetzungen entspricht, bei den Test für „Certified for Vista“ durchfällt!

Der Application Verifier führt bei den Basic Test einen Page Boundary Check durch. Dabei werden alle Allokationen so durchgeführt, dass sie immer am Ende einer Memory Page erfolgen. Jeder Zugriff über die Grenze hinweg führt dann zu einem Crash.

Im realen Leben ist der Crash wirklich selten, aber mit dem Application Verifier kracht es sofort. Das Problem ist, dass dieser Basic Test Bestandteil von „Certified for Vista“ ist.

Die Folge man rasselt mit 100% Sicherheit durch!

Bug Report hier:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=270493

Dämlicherweise wurde auch dieser Bug von Microsoft als „Gelöst (Nicht reproduzierbar)“ markiert. Da kann man wirklich nurmit dem Kopf schütteln.

Und ein kleines Demo findet ihr hier:
Testprogramm für CPropertyPage Bug in MFC 7.1

Probleme bei der CTime Serialisierung mit MFC 7.1 und MFC 8.0

Der operator<< und operator>> in MFC 7.1 serialisiert grundsätzlich nur 32bit Werte des CTime. Das wurde offensichtlich gemacht um rückwärtskompatibel zu sein zu time_t obwohl mittlerweile ja intern __time64_t verwendet wird.
Gleichzeitig wurde Serialize64 eingeführt.
Man muss also in VS2003 mit MFC 7.1 Serialize64 verwenden um alle 64bit zu speichern, also den komplettem Wertebereich von CTime.

In VS2005 mit der MFC 8.0 ging man jetzt einen Schritt weiter. Die Operatoren operator<< und operator>> sollten auch 64bit verarbeiten können. Man wollte 64bit speichern und 32bit Daten beim Einlesen erkennen wenn es eben aus alten Dateien stammt und nicht mit der neuen MFC geschrieben wurde.

Dazu wird bei Speicherung von 64bit CTime über den operator<< zuerst ein DWORD gespeichert mit dem Wert cTime64Mark (der Wert ist INT_MIN+10). Danach der 64bit Wert.
Beim Lesen wird zuerst ein DWORD gelesen. Ist dieses nicht cTime64Mark (also INT_MIN+10), dann wird dieser Wert als 32bit CTime Wert gelesen.
Ansonsten werden die 64bit in den CTime Wert gelesen.

operator>> bläht also auch die Ausgabe um weitere 32bit auf. Ein 64bit CTime Wert wird in der MFC 8.0 als 32+64bit Wert gespeichert, also 4Bytes mehr.

Speicherst man mit MFC 7.1 und dem operator<<, dann kannst man die Daten in MFC 7.1 und MFC 8.0 mit dem operator>> lesen, denn der Wert wird wegen des fehlenden Leadin-Bytes als 32bit Wert in der MFC 8.0 erkannt.
Speicherst man aber mit MFC 8.0 und dem operator<< dann ist das für MFC 8.0 kompatibel. Und kannst es in MFC 8.0 mit dem operator>> lesen. Allerdings speichert – wie schon gesagt – MFC 8.0 zuerst ein DWORD (das Leadin für 64bit) und dann den 64bit Wert.
Das versteht nun MFC 7.1 gar nicht, denn das liest immer nur ein DWORD mit dem operator>>.

❗ Ein Problem liegt also im Schreiben unter MFC 8.0 mit dem operator<< und im Lesen der Daten mit einem Programm das die MFC 7.1 verwendet und den operator>>!

❓ Was kann man machen um komplett kompatibel zu sein?

  • Würde in beiden Programmen Serialize64 verwendet gibt es kein Problem!
    Wichtig Serialize64 verwendet kein Leadin weder unter MFC 7.1 noch unter MFC 8.0!
  • Kompatibel zu beiden Versionen von MFC 7.1 und MFC 8.0 ist man auch wenn in der MFC 7.1 und in der MFC 8.0 immer nur ein DWORD gespeichert wird (das kann ich aber wirklich nicht mehr empfehelen). Unter MFC 8.0 muss das manuell gemacht werden.
  • Wenn man also Code-kompatibel (nicht binärkomaptibel) für alle MFC Versionen ab 7.0 bleiben will sollte man immer Serialize64 verwenden was einem auch ein kompakteres Format beim Speichern der Daten garantiert.

Eigentümlich ist, dass dies nicht als Breaking Change dokumentiert ist.
Oder ich habe es (wie so manches) einfach überlesen, aber hier steht es nicht:
ATL and MFC Changes: ATL 7.1 and MFC 7.1
ATL and MFC Changes: ATL 7.0 and MFC 7.0

PS: Ich habe CTime Aufgrund der vielen Unzulänglichkeiten nie verwendet. Mein Favorit ist seit eh und je COleDateTime bzw. DATE und die dazugehörige COleDateTimeSpan Klasse. Diese ist wirklich einfach in der Kalkulation und war durch das Design nie von einem begrenzten Wertebereich betroffen.

Geschafft: Mein Software-Baby ist „Certified for Windows Vista“

Obwohl ich im Urlaub bin hat mich diese wirklich nette Nachricht von VeriTest erreicht:

Congratulations! AG-VIP SQL has successfully completed certification testing at VeriTest for the Microsoft “Certified for Windows Vista” logo program.

PS: Baby ist gut. AG-VIP SQL ist mittlerweile ein mittlerer Bolide… 😀

VS Tipps & Tricks: Blockmodus aus VC6 in VS.NET 2003 und VS 2005

Viele haben den Blockmodus (Box-Mode) in VC6 über die Tastatur benutzt. Diesen Modus erreichte man über die Tasten Strg+Umschalt+F8. Damit war es einfach Spalten und rechteckige Textbereiche zu markieren und zu bearbeiten.
Diese Operation konnte auch durch das Festhalten der Alt-Taste ausgelöst werden, wenn man mit der Maus den Cursor über einen Bereich zog.

Spätere Visual Studio Versionen haben dafür keinen direkten Hotkey mehr zwischen Box-Mode und Stream-Mode zu wechseln. Das Ganze wurde vereinheitlicht.
Ein Block wird markiert indem die Umschalt+Alt-Taste festgehalten werden und entsprechende Navigationstasten bewegt werden.
Eigentlich logisch und sogar dokumentiert, aber wer liest denn schon die Doku! 😛

http://msdn2.microsoft.com/en-us/library/aa301672(vs.71).aspx
http://msdn2.microsoft.com/en-us/library/729s2dhh(VS.80).aspx

Patch für CPropertyPage Bug der MFC71.DLL/MFC71U.DLL

Wer keinen neuen Build bezüglich des dieses Problemes Bug in der MFC71.DLL bzgl. CPropertySheet/CPropertyPage machen kann oder auch nicht seinen Sourcecode nach entsprechenden Konstruktoren durchsuchen kann und auf die ausgelieferten Versionen von Microsoft angewiesen ist, kann auch einen kleinen Patch durchführen.

MFC71.DLL aus SP1 Version 7.10.6030
Größe 1.060.864 Bytes, Dienstag, 11. Juli 2006, 18:43:32
Patch der Adressen
0x0008E9F0 von 0x89 0x38 -> 0x90 0x90
0x0008EA09 von 0x89 0x38 -> 0x90 0x90

MFC71U.DLL aus SP1 Version 7.10.6030
Größe 1.056.768 Bytes, Dienstag, 11. Juli 2006, 19:02:30
Patch der Adressen
0x000A1ACC von 0x89 0x38 -> 0x90 0x90
0x000A1AE5 von 0x89 0x38 -> 0x90 0x90

Gesucht werden kann auch nach der Sequenz 0x8B 0x46 0x74 0x89 0x38. Diese Sequenz kommt in der MFC.DLL genau zweimal vor.

Es entspricht dem folgenden Code:

8B 46 74 mov eax,dword ptr [esi+74h]
89 38 .. mov dword ptr [eax],edi

Weggepatch wird in diesem Falle einfach das Laden der Adresse (zweites mov Statement) und durch zwei NOP ersetzt.
Wer die entsprechende PDB Datei hat sieht im Disassembly hier das Statement
m_psp.dwSize = sizeof(m_psp);
Der Bug befindet sich in der Datei dlgprop.cpp in den Zeilen 184 und 189!

Anwendung auf eigene Gefahr!

Bug in der MFC71.DLL bzgl. CPropertySheet/CPropertyPage

Ich habe ein Projekt, dass einen eigenen MFC Build verwendet. Dieser basiert zu 99,999% auf dem MFC71 SP1 Build aus VC-2003.NET. Das dies ein eigener Build ist spielt für den Bug allerdings keine Rolle, allerdings evtl. für die Lösung.

Zu den Rahmenbedingungen. Man baut eine EXE/DLL mit folgenden Eigenschaften:

  • _WIN32_IE< =0x0500.
  • Verwendet die DLL-Version der MFC (Use MFC in a Shared DLL)
  • Man verwendet CPropertyPage und CProprtySheet als Wizard.
  • Man verwendet Header-Titel und Header-Subtitel und damit einen der beiden nachfolgenden Konstruktoren:
    CPropertyPage(UINT nIDTemplate, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle = 0, DWORD dwSize = sizeof(PROPSHEETPAGE));
    CPropertyPage(LPCTSTR lpszTemplateName, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle = 0, DWORD dwSize = sizeof(PROPSHEETPAGE));

Die MFC71.DLL ist so gebaut, dass sie mit allen 3 Versionen der PROPSHEETPAGE Struktur klar kommen soll. Es gibt drei Versionen, die mit folgenden _WIN32_IE Definitionen unterschieden werden:

  • für WIN32_IE<0x0400, die aller erste Version die mit Windows 95 kam. Größe 40 Bytes.
  • für WIN32_IE>=0x0400 && _WIN32_WINNT<=0x0500, hier wurden Titel und Subtitel hinzugefügt (pszHeaderTitle, pszHeaderSubTitle). Größe 48 Bytes.
  • und zuletzt _WIN32_WINNT>=0x0501, hier kam noch ein Activation-Context dazu (hActCtx). Größe 52 Bytes.

Was passiert nun:

  • Einer der genannten Konstruktoren für die CPropertyPage wird aufgerufen.
  • Der letzte Parameter sizeof(PROPERTYPAGE) zeigt nun der DLL an welche Version das aufrufende Programm verwendet, damit die MFC71.DLL die entsprechenden Größen bereitstellt.
  • AllocPSP wird entsprechend aufgerufen und dynamisch diese PROPERTYPAGE allokiert.

Bis hierher ist alles prima, aber leider passiert es un, dass diese vordefinierte Größe in der Funktion CommonConstruct(LPCTSTR lpszTemplateName, UINT nIDCaption, UINT nIDHeaderTitle, UINT nIDHeaderSubTitle) überschrieben wird (Zeile 184+189 in dlgprop.cpp). Das passiert mit dem Statement:
m_psp.dwSize = sizeof(m_psp);
D.h. die PROPERTYPAGE Struktur ist mit weniger als 40 oder 48 Bytes allokiert. Im dwSize Member steht aber nun 52.
In der Funktion BuildPropPageArray() wird nun aus den einzelnen PROPERTYPAGE’s ein Array gebildet. Dabei werden jeweils dwSize-Bytes (also 52) aus den entsprechenden allokierten Bereichen kopiert. Aber Achtung, diese sind kleiner allokiert (40 oder 48 Bytes)
In der Debug Version ist das oft nicht tragisch, denn da kommen hinten dran noch einige Füllbytes. Auch in der Release Version kracht es nur manchmal. Genau nur dann, wenn einer der allokierten PROPERTYPAGE Strukturen genau auf mit einer allokierten Speicherseite endet und das 49ste Byte der Struktur eben nicht allokiert ist und die Speicherseite exakt hier endet.

Diese sehr üble Konstellation machte es mir extrem schwer diesen Fehler zu finden. Ich hasse es einfach optimierten Code zu debuggen 😉

Fix:

  • Keinen der obigen Konstruktoren verwenden und die Member m_strHeaderTitle und m_strHeaderSubTitle selbst füllen.
  • oder wie ich einen eigenen MFC Build erzeugen und verwenden. Die entsprechenden Code Zeilen 184+189 aus der dlgprop.cpp entfernen.

Hinweis:
Crashdumps zeigen meistens im Stacktrace die Funktionen BuildPropPageArray und memcpy!

BTW:
Dieser Bug betrifft sowohl die MFC71.DLL im RTM und im SP1.
In der MFC 8.0 des VS-2005 ist dieser Bug behoben.

Vista beendet Programme auch wenn WM_QUERYENDSESSION FALSE returniert

Jeder kennt die nette Nachricht WM_QUERYENDSESSION die einem mitteilt, dass das System herunter gefahren wird, oder der Benutzer sich abmeldet.

Und wir alle sind auch einfach gewöhnt FALSE zu returnieren wenn wir das nicht wollen und damit waren wir sicher das WM_ENDSESSION mit FALSE gesendet wird und das System nicht herunterfährt. Ja! Richtig gelesen waren!

Das hat sich mit Vista nun auch geändert. Wird im lParam Bit ENDSESSION_CLOSEAPP (0x1) gesetzt, dann spielt es keine Rolle wie das Programm es gerne hätte. Die Applikation soll und muss beendet werden. D.h. die Applikation muss auch mit dem Zustand zurecht kommen, dass die Daten nicht gesichert sind, bzw. eben mit dem Zustand, der uns veranlasst hat FALSE zu returnieren.
Dies war bisher ausgeschlossen. Deshalb sollte man sich über das Verhalten seines Programmes an dieser Stelle mal Gedanken machen.

Für MFC Anwender noch eine kleine Herausforderung denn OnQueryEndSession wird von der MFC ohne Parameter aufgerufen und ohne das Vista SDK ist natürlich ENDSESSION_CLOSEAPP nicht definiert

#ifndef ENDSESSION_CLOSEAPP
#define ENDSESSION_CLOSEAPP 0x1
#endif

BOOL CMainFrame::OnQueryEndSession()
{
// Need to get the lParam value for this message to
// determine the reason of the shutdown
LPARAM lParam = AfxGetCurrentMessage()->lParam;
if (lParam & ENDSESSION_CLOSEAPP)
{
  // We are forced to exit here, the user want to close the
  // application, even if we loose data
  OnForcedShutdown():
  return TRUE;
}
else
  // do the default
  return __super::OnQueryEndSession();
}

BTW: Eine normale MFC Applikation berücksichtigt dies nicht korrekt. Es wird der Status von allen Dokumenten geprüft und evtl. einen entsprechender Dialog angezeigt, wenn eines der Dokumente nicht gespeichert ist. Wird aber die Anfrage mit ENDSESSION_CLOSEAPP gesendet, dann muss die Antwort in 5 Sekunden erfolgen. Einen Dialog anzuzeigen bzw. Frage zu stellen wie es die Standardimplementierung macht, passt hier nicht!

Man sollte sich unbedingt hier die Vista Änderungen zu Gemüte führen und gegebenenfalls auch ShutdownBlockReasonCreate implementieren.

Weitere Infos zu den Änderungen findet sich in der aktuellen MSDN:

Anmerkung: Jeder der sich mit der Vista-Zertifizierung auseinandergesetzt hat wird erkannt haben, dass es sich bei dem hier beschriebenen Verhalten um den Test Case 30 handelt, bzw. die Requirements, Reliability 3.1!

Warum man seine Libraries mit _CRT_NOFORCE_MANIFEST erzeugen sollte

Seit VC 2005 wird die DLL-Version CRT ja grundsätzlich über ein Manifest geladen. Erzeuge ich Code, der die CRT verwendet, dann wird automatisch ein #pragma comment(linker,“/manifestdependency:..“) Eintrag erzeugt. Den entsprechenden Code dazu findet man in der _crtdefs.h Datei des entsprechenden Include-Pfades.

Dieser Manifest Eintrag wird später vom Manifest Tool (MT.EXE) gesammelt und im Manifest der Applikation oder der DLL verbaut.

Gesetzt den Fall ich habe Library erzeugt, die auch die DLL-Version der CRT verwendet, dann wird automatisch die entsprechende Version der CRT im Manifest hinterlegt. D.h. bei VS 2005 RTM die CRT Version 8.0.50727.42 und bei SP1 die CRT Version 8.0.50727.762

Wird nun für ein Projekt eine Library erzeugt mit einem VS 2005 RTM, und evtl. diese LIB lange nicht verändert und dann diese Library in einem Programm verwendet, dass mit VS 2005 SP1 erzeugt wird, dann werden beide CRT Versionen im Manifest eingetragen!
Der Lader ist zwar intelligent und lädt die letzte DLL (SP1). Aber undurchsichtig wird es in jedem Fall.
Vor allem wenn man wirklich mit evtl. einer älteren Version ausliefern möchte und SP1 auf der Ziel-Maschine nicht installiert ist.

Ich habe noch nicht genau herausbekommen wie entschieden wird welches der Manifesteintrag ist, der letzten Endes zählt. Scheinbar macht es auch nichts wenn, man die Version der CRT Versionen falsch angibt. Es wird dann die CRT mit der nächst höheren CRT geladen… (aber dazu muss ich noch etwas experimentieren)…

Dieses Verhalten ist gravierend unterschiedlich zu allen Vorgängerversionen von VC. Wie bisher hält Microsoft das Versprechen der binären Kompatibilität. Theoretisch müsste auch die mit RTM erzeugte LIB sofort funktionieren. Auch der Linker wird diese LIB sofort mit der entsprechenden MSVCR80(D).DLL verbinden. Nur hat er eben nicht das letzte Wort bei den Manifesten.

Man kann das ganze verhindern, indem man _CRT_NOFORCE_MANIFEST bevor die CRT Dateien inkludiert werden (am Besten in der stdafx.h oder in den Projekteinstellungen).
In diesem Fall wird kein #pragma, dass für den Manifest Eintrag verantwortlich ist, erzeugt.
Der Effekt ist dann wie gewollt, dass nur das eigentliche Projekt, dass dann das Executable erzeugt Einfluss auf die Ausgabe des Manifestes hat. Man kann also dann auch eine Lib-Datei mit VS-2005 SP1 erzeugen und mit einem VS-2005 RTM linken. Das Manifest wurde durch diese Lib dann nicht beeinflusst und so soll es IMHO sein.

Aber aufgepasst:
Seit VS-2005 muss in jedem Fall auf noch eine strenge Trennung der Release und Debug Versionen achten. Wenn nun kein Manifest mehr zum Beispiel in einer Release-Lib erzeugt wird, weil _CRT_NOFORCE_MANIFEST verwendet wird, nun aber diese Release Lib in ein Debug Projekt eingebunden wird.  Dann wird das Executable implizit mit der MSVCR80.DLL gebunden, aber diese DLL kann nur über ein Manifest geladen werden. Das ist aber nicht vorhanden. Resultat ist das was in diesem Beitrag beschrieben steht: Manifest-Hölle „…MSVCR80.dll nicht gefunden…“ (siehe Nachtrag)

VS Tipps & Tricks: Kann man Intellisense in VS-2005 ausschalten?

Immer wieder klagen User über Abstürze in VS-2005, die scheinbar mit dem Intellisense zusammengehören. Oder es wird von extrem langen Wartezeiten und zähem Verhalten der IDE berichtet. Die Frage ob man also Intellisense abschalten kann wundert nicht.

Ich persönlich kann diese Effekte nicht bestätigen bzw. habe es noch nicht erlebt.
<werbeblock>Ich nutze außerdem Visual-Assist von http://www.wholetomato.com , mit dem wohl besten Intellisense, das man sich vorstellen kann. Ein Blick lohnt sich in jedem Fall!
Vorsicht! Es macht süchtig. Sobald ich an einem Rechner sitze an dem es nicht installiert ist, fühle ich mich, als ob man mir 4 Finger amputiert hat.</werbeblock> 🙂

Aber zurück zur eigentlichen Frage:
Kann man Intellisense ganz ausschalten?

Ja es geht dazu muss man die folgende Datei löschen oder umbenennen:
<VS root path>\VC\vcpackages\feacp.dll