VC-2008: Neues, Breaking changes…

Hier ein kleiner Auszug aus der Liste der Änderungen und Neuigkeiten.
Die Links verweisen in die MSDN wo man alles nachlesen kann.

What’s New in Visual C++ 2008
Da ist nicht sooo viel:

  • Eingeschränkte (read only) Unterstützung des Class Designers.
  • Vista style Guidlines für Dialoge
  • Vista Common Controls in der MFC
  • Die interessanten Sachen wie MFCNext und TR1 kommen noch

Breaking Changes

  • Nachdem in Visual Studio 2005 der Support von Windows  95 entfiel, hat Microsoft nun einen Schlussstrich unter gezogen.
    Im Klartext: Windows 95, Windows 98, Windows ME, und Windows NT werden als Zielplattformen nicht mehr unterstützt.
  • Hier fällt auf, dass die ATL nun zwingend Abhängig von der CRT wird. Wer früher gerne ATL_MIN_CRT verwendet hat um „gar keine“ CRT zu verwenden, der wird feststellen, dass seine Module etwas wachsen.
  • /Wp64 ist deprecated (na endlich)…

Anmerkung:
Wie so oft werden hier die Sachen veröffentlicht, wo man bei Microsoft weiß, dass man Breaking Changes durchgeführt hat. Es gibt oft genug weitere Breaking Changes die man oft erst am eigenen Leib erfahren muss, siehe Attributed ATL.

VC-2005 Features der CRT für Unicode Unterstützung

Die CRT der VC-2005 hat eine perfekte Unterstützung für Unicode Dateien im UTF-8 und UTF-16 Little Endian Format.

Wollte man bisher Unicode Dateien lesen, so mussten diese mit _wfopen(…,L“rb“) geöffnet werden und entsprechende Leseoperationen mussten folgen. Um Unicode und ANSI Dateien zu unterscheiden schrieb man selbst entsprechenden Code, der nach einer BOM (Byte Order Mark) schaut. Beim Schreiben hatte man auch selbst darauf zu achten die BOM entsprechend zu setzen.

Ziemlich unbemerkt (auch von mir 🙂 ), hat die CRT hier eine entscheidene Erweiterung erfahren. Mit dem folgenden Code lässt sich gezielt jede Datei entsprechend öffnen und lesen, sofern Sie über eine korrekte BOM verfügt, bzw. auch eine Datei zum Schreiben öffnen die mit der entsprechenden BOM versehen wird.

FILE *pFile = _tfopen(_T(„myfile.txt“),_T(„rt, ccs=UNICODE“));

Ist die Datei eine ANSI Datei ohne BOM, wird sie entsprechend geöffnet. Unicode Dateien im Typ UTF-8 und UTF-16 little endian werden an der entsprechenden BOM erkannt und entsprechend gelesen.
Über ccs=UTF-8 bzw. ccs=UTF-16LE lässt sich auch gezielt eine entsprechende Unicode Datei ohne BOM öffnen. Eine vorhandene BOM überschreibt allerdings die beim Öffnen angegebenen Formatierung. Fein!

Leider hinkt die MFC diesen wirklich tollen Funktionen der CRT hinterher. Weder die MFC  8.0 aus VS-2005 noch die neue Orcas Version (Stand Beta1) verfügt über eine neue Version der CStdioFile, die diese Funktionen der CRT abbildet.

Glücklicherweise gibt es einen entsprechenden CStdioFile(FILE *) Konstruktor. Dadurch ist es möglich, einfach eine Datei mit _tfopen zu öffnen und den Stream einfach an ein CStdioFile Objekt zu koppeln.
Einziger Schönheitsfehler: CStdioFile::Close muss explizit in diesem Fall aufgerufen werden, d. h. Close wird nicht durch den Destruktor aufgerufen.
Für den MFC Kenner: m_bCloseOnDelete ist FALSE und leider protected. Dieses Flag verhindert, dass Close auch durch den Destruktor aufgerufen wird.

MSDN Dokumentation zu _tfopen/fopen/_wfopen

Mein erster Codeproject Artikel…

Nun habe ich es endlich mal geschafft und meinen ersten kleinen Artikel auf Codeproject geschrieben:

http://www.codeproject.com/cpp/PrivateAssemblyProjects.asp

 Er schließt sich nahtlos an über alles das was ich hier achon über die Manifest-Hölle geschrieben haben…

_MFC_NOFORCE_MANIFEST und _ATL_NOFORCE_MANIFEST

In meinem Blog habe ich bereits über Libraries und die Verwendug von _CRT_NOFORCE_MANIFEST geschrieben (siehe Link unten).

Wenn man nun eine Library erzeugt, die die MFC oder die ATL benutzt, sollte man sich auch noch der beiden Defines _MFC_NOFORCE_MANIFEST und _ATL_NOFORCE_MANIFEST bewusst sein. Diese beiden Defines verhindern, dass durch die Verwendung der ATL bzw. MFC Include-Dateien #pragma comment(linker,”/manifestdependency:..”) Statements erzeugt werden.

Werden diese Defines konsequent verwendet, dann hat der Benutzer der Library die volle Kontrolle welche CRT, MFC bzw. ATL Version angebunden wird.

Warum man sich mit diesen Defines beim erzeugen einer Library auseinenadersetzen sollte kann man in diesem Artikel nachlesen: Warum man seine Libraries mit _CRT_NOFORCE_MANIFEST erzeugen sollte!

❗ BTW: Durch diese Defines kann man allerdings nicht verhindern, dass überhaupt Manifest-Einträge erzeugt werden. Selbst wenn man sein Programm mit den entsprechenden Defines kompiliert. Die Objektdateien haben dann zwar keine Manifest-Einträge, aber spätestens in dem Moment, in dem man das Programm linkt werden aus der CRT, MFC bzw. ATL Libraries Objektdateien gezogen die wieder entsprechende #pragma comment(linker,”/manifestdependency:..”) Einträge haben. Entsprechend bekommt das Manifest Tool dann auch Futter. Dazu mehr in einem späteren Artikel 🙂

_UNICODE versus UNICODE und so manches Eigentümliche

Wann nimmt man eigentlich was?
Jedesmal wenn ich Code ansehe finde ich mal einen

#ifdef _UNICODE

dann mal wieder einen

#ifdef UNICODE

Wann eigentlich was?
Die Frage wurde schon oft beantwortet (siehe The Old New Thing : TEXT vs. _TEXT vs. _T, and UNICODE vs. _UNICODE).

In Kurzform:
UNICODE wird in den Windows Header-Dateien verwendet.
_UNICODE in der C-Runtime (CRT).

Interessanterweise gibt es nur den Define _MBCS aber nicht den Define MBCS. Anhand der Namensgebung kann man sehen, dass _MBCS aus/für die CRT nur Einfluss hat. Die Windows API sagt einfach, wenn nicht Unicode dann ist es MBCS!

Daraus ergeben sich einige interessante Abhängigkeiten: 

_T ist abhängig vom Define _UNICODE genauso wie _TEXT (also der CRT)
TEXT ist wiederum abhängig vom Define UNICODE.

Jetzt wird es aber ganz lustig:
Die Datei tchar.h gehört zur CRT. Und TCHAR ist eigentlich kein Define aus tchar.h und der CRT. Dort wird nämlich eigentlich _TCHAR definiert und sollte TCHAR schon definiert sein (durch die windows.h), dann wird TCHAR nicht mehr umdefiniert.

Ist also nur UNICODE definiert und nicht _UNICODE, dann muss der folgende Code einen Compiler Fehler auslösen, wenn windows.h vor der tchar.h inkludiert wird. Erwartungsgemäß meldet der folgende Code einen Compiler-Fehler, obwohl dieser perfekt und korrekt aussieht:

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
int _tmain(int argc, _TCHAR* argv[])
{
 const TCHAR szText[] = „“;
  // Must fail if UNICODE is defined and not _UNICODE, and windows.h included first
 _tcslen(szText);
 return 0;
}

In VC6 gab es noch keine Projekteinstellung für Unicode. Man musste also beide Defines setzen, als Präprozessor Definition. Andernfalls hatte man eine lustige Code Mischung und lief schnell mal in ein Problem wie oben.

Der Schalter in VC-2005 und VC-2003 für

  • „Use Unicode Character Set“ setzt beide Defines _UNICODE und UNICODE
  • „Use Multi-Byte Character Set“ setzt nur _MBCS.
  • „Not set“ setzt Erwartungsgemäß keinen der Defines und man kann das obige Beispiel und den Fehler ausprobieren.

Projektintern verwende ich grundsätzlich immer intern nur den define UNICODE um zu prüfen welche Art von Projekt hier läuft. Und ich achte darauf, dass die windows.h bzw. die afxwin.h Datei immer als erstes inkludiert werden. Dann ist TCHAR entsprechend konform für die Windows API gesetzt. Die CRT ist für mich meistens nur Beiwerk und ich vermeide Ihre Verwendung eigentlich wo ich kann.

An kritischen Stellen baue ich auch gerne den folgenden Test ein um ganz auf Nummer sicher zu gehen:

#if defined(UNICODE) ^ defined(_UNICODE)
#error Inconsitent UNICODE and _UNICODE definition
#endif

Mit dem XOR-Operator wird hier ein Fehler ausgelöst wenn nur einer der beiden Defines gesetzt ist.

Siehe auch die MSDN INFO: UNICODE and _UNICODE Needed to Compile for Unicode

Selbstgemachter Manifest-Ärger…

In dem C++.de Forum kam dieser Thread Sehr spezielles Problem – (DLL + Manifest) auf!

Man muss den Thread nicht ganz lesen, ich will hier eine kurze Übersicht geben, wie man ein Projekt verbiegen kann, dass es nur noch Ärger macht, den man nur schwer (oder nach langem Suchen) in den Griff bekommt.

Ursprüngliches Problem:
Es ging mal wieder darum eine EXE so zu schreiben, dass Sie nicht von einer VC-2005 Runtime Library Installation (CRT+MFC) abhängig ist. Es ging um ein spezielles Plugin (also eine DLL).

Die Empfehlung war, wie so oft bei Manifest Problemen:
Linke sowohl CRT als auch MFC statisch.

Begründung:
Die DLL benötigt keine weiteren DLLs oder Third Party Libs. Ist also mehr oder weniger standalone. Also warum noch zusätzliche Abhängigkeiten schaffen. Auch wenn dies etwas mehr Hauptspeicher kostet, wenn andere Applikationen die 8.0 Runtime-Libs auch gleichzeitig verwenden können.
Es erübrigt die Installation der VCRedist_x86.exe oder auch andere, komplexere Tricks wie die applikationslokale Installation der Runtime-Libs.

Folgeproblem:
Sowohl für die MFC als auch für die CRT wurde nun statisches Linken im Projekt eingestellt. Aber dennoch wurde weiterhin ein Manifest erzeugt, das die CRT anforderte.
Das erzeugte doch nun einiges Rätselraten bei mir. 😕
Auch die Projektdatei, die mir zugesandt wurde brachte auf den ersten Blick keinen Aufschluss über das Problem.
Weitere Prüfung mit DUMPBIN ergab, dass die Objekt Dateien alle mit einem Manifest Eintrag kompiliert wurden, oder in anderen Worten, das im Sourcecode ein #pragma comment manifest Eintrag drin steht.

Lösung:
Einze kurze Recherche in der crtdefs.h ergab, dass die #pragma Einträge für die Manifeste nur erzeugt werden wenn, _DLL definiert wird. Die Doku sagt klar:

  1. Das ist eine interne, vordefinierte Präprozessor Variable
  2. Diese ist nur bei den Kompileroptionen /MD und /MDd definiert

Derjenige, der dieses Projekt erzeugt hat, hat auch die Präprozessor Variable _DLL vordefiniert. Das Resultat war natürlich, dass Manifest-Einträge erzeugt wurden und weiterhin die CRT-DLLs genutzt wurden.

Merke: Pfusche nie mit internen Nachrichten, Präprozessor-Variablen rum, die einen nichts angehen. Oft genug sind schnelle Workarrounds die man mit so etwas erreicht ein Schuss ins eigene Knie. 🙂

Ach ja! Man hätte übrigens noch weiter Pfuschen können und _CRT_NOFORCE_MANIFEST definieren können :mrgreen: ! Dann hätten wir den einen Pfusch mit einem anderen behoben!

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.

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)

Manifest-Hölle „…MSVCR80.dll nicht gefunden…“

Mir ist wieder einmal ein Projekt untergekommen, das in der Debug Version nicht starten wollte. Ursache diese Fehlermeldung:
„Die Anwendung konnte nicht gestartet werden, weil MSVCR80.dll nicht gefunden wurde. Neuinstallation der Anwendung könnte das Problem beheben.“

Der Grund für diesen Fehler war in diesem Fall ein Manifest, dass selbst geschrieben wurde und nicht durch den Manifest Compiler erzeugt wurde.

Der Reihe nach:

  • Es wurde ein Projekt gebaut, das eine externe Library benutzte
  • Diese externe Library wurde mit dem selben Compiler erzeugt. Sowohl als Debug, als auch als auch Release Version. (ZLib)
  • Alle Projekte verwenden dynamische Bindung an die CRT.
  • Der Entwickler machte nun aber einen kleinen Fehler. Im Debug Build instruierte er den Linker so, dass die Release Version der Library verwendet wurde. BTW: Ein Grund mehr immer #pragma comment(lib,…) zu verwenden, dann kann man in den Projekteinstellungen keinen Mist machen.
  • Wie ich schon erwähnte wurde nun das Manifest mit der Hand erzeugt um die entsprechenden Abhänigkeiten und auch das Trustinfo Manifest korrekt zusammenzubauen, bzw. weil nicht mit MT.EXE aus dem Vista SDK verwendet wurde (siehe mein Blog).
    Die Release CRT wurde natürlich nicht mit angegeben (obwohl verwendet), aber dessen war sich natürlich keiner im Team bewusst.

Nun kam es zu dem Effekt, dass dieses Projekt im Debug Modus nicht starten wollte, weil die MSVCR80.DLL nicht gefunden wird. Ja und das stimmt, denn diese DLL kann nicht gefunden werden, da Sie nicht in den entsprechenden Manifesten definiert wurde.

Wie findet man so einen Fehler?
Herausbekommen habe ich das durch die Verwendung von DEPENDS.EXE. Damit konnte ich erstmal ermitteln wer, wen, wann laden möchte und wo das Laden letzten Endes fehlschlägt. 
Dann habe ich die entsprechenden Module (DLLs und EXE Dateien) in den Resource-Editor geladen. Dort habe ich die entsprechenden Manifeste kontrolliert.

Nachtrag 22.03.2007 (als Kommentar wird es evtl. übersehen)
Ich habe aktuell auch in einem anderen Fall ähnliches herausbekommen. Hier wurde auch eine alte ZLib-Release Version (und andere) in ein Debug Projekt eingebaut. Nur waren diese Libraries im Projekt nicht mit VS-2005 kompiliert worden, denn sonst wären entsprechende Manifest Einträge eingeflossen. Soweit ich das Projekt sehen konnte wurde _CRT_NOFORCE_MANIFEST auch nicht verwendet, was die Erzeugung der Manifest Einträge verhindert hätte.
Das Resultat auch hier. Eine MSVCRT80.DLL wird vom Linker verlangt, aber es existiert kein Manifest dazu. Die entsprechende gleiche Fehlermeldung auch hier.
Ein Rebuild der entsprechenden Libraries und des Projektes lies das Programm dann zwar starten, aber dennoch wurden in diesem Falle gleichzeitig Debug-CRT und Release-CRT verwendet, was sicher nicht im Sinne der Programmierer war.

Wir lernen daraus wieder einmal: Achte streng auf die Trennung von Release und Debug Libraries. 😉

Gründe für den R6034 bei der VC 8.0 DLL CRT

Warum lässt sich die neue VC 8.0 CRT eigentlich nicht ohne Manifest verwenden?
Schauen wir uns also mal an was passiert wenn eine Applikation oder eine DLL geladen wird, die die VC 8.0 CRT als DLL verwendet:

Die DllMain der CRT wird aufgerufen. Bei der C-Runtime DLL ist dies die Funktion _CRTDLL_INIT die sofort die Funktion __CRTDLL_INIT aufruft.

Diese Funktion sorgt nun der Reihe nach dafür, dass:

  • die aktuelle Windows Version bestimmt wird
  • der Heap initialisiert wird
  • die TLS Bereiche für multithreading angelegt werden
  • das Environment und die Befehlszeile bestimmt werden
  • die MBCS Umwandlungstabellen angelegt werden
  • alles was mit atexit, floating-point math etc. initialisiert wird
  • und… jetzt wird es spannend… die Funktion _check_manifest aufgerufen wird.

In der Funktion _check_manifest wird der Code ausgeführt der zum Runtime Fehler R6034 „The application has attempted to load the runtime library incorrectly. Contact support for more information“ führen kann, nämlich genau dann, wenn diese Funktion FALSE zurück gibt.

Was macht diese Funktion nun?
Im Source-Code der CRT ist dies wunderbar dokumentiert:

  1. if (pre-fusion OS)
      return TRUE; [no need to check]

    Das bedeutet nichts anderes, als das die nachfolgenden Tests nur auf XP/Vista/2003 und folgenden OS durchgeführt werden. Also im Klartext unter Windows 2000 kann dieser Runtime Error nicht auftreten.
  2. if dll is being loaded by instrumented-mscoree.dll.
      return TRUE;
    OK. Ein Speziallfall. Im Kontext der .NET CLR und des Profilers ist auch ales erlaubt. Also wenn sich MSCoree.dll (Microsoft .NET Runtime Execution Engine) und PGORT80.dll (Profile Guided Optimization Instrumentation Runtime) im Speicher befinden ist auch alles paletti.
  3. if (dll is loaded from system32)
      return FALSE;
    Dies ist interessant. In keinem Fall darf diese DLL im Windows\System32 stehen! Oder wenn Sie von dort geladen wird fielgt einem der R6045 um die Ohren.
  4. if (!(loaded through a manifest))
      return FALSE;

    Hier wird über die Funktion FindActCtxSectionStringW geprüft ob diese DLL über ein Manifest geladen wird. ActCtx ist die Abkürzung für Activation Context.
    Wurde kein solcher Activation Context gefunden (also kein Manifest), dass für diese DLL verantwortlich ist, dann heißt das auch R6045.
  5. if (loaded from %SystemRoot%\WinSxS)
      return TRUE; [loaded from the WinSxS cache]

    Wird die DLL aus den Side by Side Verzeichnissen geladen ist schon alles gut.
    Aber auch das geht nur mit gültigem Manifest.
  6. if (manifest is in the same folder as the dll)
      return TRUE;
    Auch das ist interessant. Das Manifest muss zwingend im selben Verzeichnis wie diese DLL liegen, denn sonst sind wir fertig
  7. return FALSE; [loaded with another manifest]
    und nun schlägt das Laden fehl, da das Manifest offensichtlich nicht im DLL Verzeichnis liegt. Auch hier ein R6045.

Zurück noch mal zum Fall 2. Was macht dieser Test „if dll is being loaded by instrumented-mscoree.dll“? Er macht nichts anderes als zu prüfen ob die beiden DLLs im Speicher sind.

BTW: Man könnte nun ganz frech zwei leere DLLs mit diesen Namen erzeugen und diese in ein Projekt implizit laden lassen… und siehe da, die Applikation benötigt auf einmal kein Manifest mehr…
Aber warum sich das ganze verbietet ist auch klar. DLLs mit den oben genannten Namen zu erzeugen, kann ganz schön verwirrend und problematisch werden.
Und warum zwei leere DLLs in einen Prozess laden, wenn es reicht ein Manifest zu ergänzen? 😉