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 🙂

Wie findet die MFC 8.0 eigentlich die MFC80lll.DLL Dateien?

Es ist vielleicht schon machen aufgefallen, dass auch die sprachabhängigen Ressourcen-Dateien der MFC als Side by Side Assemblies installiert werden durch VCREDIST_X86.EXE.
Schaut man sich an wie ein Programm erzeugt wird, dass die MFC80.DLL/MFC80U.DLL verwendet, dann weiß man wie Manifeste aussehen die für das entsprechende Programm erzeugt werden. Wir finden im Allgemeinen drei Dependency-Einträge für die MFC, die CRT und die ComCtl32 DLL.

Erstaunlicherweise wird kein Eintrag für die Sprachdateien erzeugt und scheinbar auch nicht benötigt. Denn wenn man das Programm im Debugger auf einem Deutschen XP oder Vista startet sieht man in der Debug-Ausgabe, dass die entsprechende Datei MFC80DEU.DLL aus dem Side by Side Storage geladen wird.

Ein Blick in die MFC80(U).DLL zeigt auch ein Manifest. Dieses passt genau auf die Sprachdateien. Allerdings ist dieses Manifest nicht aktiv, denn es hat keine „gültige“ ID (1000), die das Betriebsystem veranlassen würde es zu berücksichtigen. Zudem hätte dieses Manifest auch nur einen Effekt, wenn die Sprach-DLLs implitzit geladen würden.
Diese Dateien werden aber explizit geladen. Der Code der dafür verantwortlich ist wird zuallererst in der DllMain und der Initialisierung der MFC80-DLL und ein zweites mal in CWinApp::InitInstance aufgerufen.

Der entsprechende Code dort, lädt das Manifest aus der DLL, erzeugt einen Aktivierungs-Kontext und führt dann den entsprechenden AfxLoadLibrary Aufruf durch. Anschließend wird der Kontext wieder zerstört. Wird die Sprach-DLL nicht gefunden wird ein zweites Mal versucht die Datei nun aus dem Programmverzeichnis bzw. über die normalen Suchpfade zu laden.

Diese Methode über das eingebaute Manifest lässt sich nicht aushebeln. Verwendet man selbst eine Technik, die die MFC-DLLs als private Assemblies lädt, dann versucht die MFC dennoch die Sprach-DLLs Side by Side zu laden. Das Ganze ist etwas ärgerlich, wenn man private Assemblies pur verwenden möchte. Einziger Trick wäre hier CWinApp::LoadAppLangResourceDLL zu überschreiben, aber das verhindert nicht den ersten Aufruf aus der DllMain der MFC-DLL heraus.

❗ Wer sich das Ganze genauer ansehen will, sollte sich die Datei appcore.cpp mal ansehen.
Entscheidend hier sind die Funktionen: CWinApp::LoadAppLangResourceDLL und AfxLoadLangResourceDLLEx.

❗ Schaut man sich die MFC-DLLs übrigends noch genauer an, dann stellt man fest, dass sie gar keine Manifeste enthalten. Und das ist auch gut so, sonst wäre es ja auch nicht möglich die MFC und die zugehörige CRT als Private Assembly zu laden. Die MFC verwendet also immer die CRT, die im Kontext der EXE geladen wird.

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!

MSI.LIB fehlt in VC-2005 Installation

Das verstehe mal wieder wer will 😕 ich verstehe es auf jeden Fall mal nicht.

Die MSI.H Include-Datei  findet sich in der im $(VCInstallDir)PlatformSDK\include Verzeichnis. Die dazu passende MSI.LIB Datei dagegen ist dagegen in $(VCInstallDir)PlatformSDK\lib nicht zu finden. Da spielt es auch keine Rolle ob man VS-2005 RTM oder SP1 hat.

Man muss sich diese Datei aus einem aktuellen SDK besorgen.

Alle Version ab VC6, über VS.NET 2002+2003 verfügen sowohl über Header als auch Lib-Datei.

Heath Stewart beschreibt das zwar in seinem Blog, aber warum das so ist steht dort nicht.

PRJ0041 in einem simplen Win32 Projekt in VC-2005

Ich habe ein ganz billiges Win32 API Projekt, dass auf den Bugslayer Utilities von John Robbins basiert. Das Projekt wurde jetzt auf VC-2005 umgestellt.

Keine Probleme aber eigentümliche Meldungen PRJ0041 im Buildlog:

Project : warning PRJ0041 : Cannot find missing dependency ‚winwlm.h‘ for file ‚MyProject.rc‘.  Your project may still build, but may continue to appear out of date until this file is found.
Project : warning PRJ0041 : Cannot find missing dependency ‚macwin32.h‘ for file ‚MyProject.rc‘.  Your project may still build, but may continue to appear out of date until this file is found.
Project : warning PRJ0041 : Cannot find missing dependency ‚macwin32.h‘ for file ‚MyProject.rc‘.  Your project may still build, but may continue to appear out of date until this file is found.
Project : warning PRJ0041 : Cannot find missing dependency ‚macwin32.h‘ for file ‚MyProject.rc‘.  Your project may still build, but may continue to appear out of date until this file is found.
und die Liste geht noch weiter

Die meisten dieser Dateien sind irgendwie für den MAC bestimmt. und im normalen SDK gar nicht vorhanden. Scheinbar kommt der Parser des VS-2005 mit diesen Dateien irgendwienicht klar.

Ausgelöst werden sie offensichtlich durch dem #include der windows.h in den Read-Only Symbols directives:

#define APSTUDIO_HIDDEN_SYMBOLS
#include <windows.h>
#undef APSTUDIO_HIDDEN_SYMBOLS
#include <winver.h>

Ich habe alles mögliche versucht diese Fehler weg zu bekommen, aber irgendwie hat alles nichts gebracht. Und langsam wurde ich ungeduldig länger als 30 Minuten an so einem Seiteneffekt zu verbringen.
Schließlich habe ich zu einem kleinen Trick gegriffen und den Parser überlistet:

#define APSTUDIO_HIDDEN_SYMBOLS
/**/ #include <windows.h>
#undef APSTUDIO_HIDDEN_SYMBOLS
/**/ #include <winver.h>

Jetzt ignoriert der Parser die entsprechenden Include-Dateien und das Projekt erzeugt keine Warnungen mehr und wen interessieren schon die Dependencies der windows.h 🙂 ?

Attributed ATL für OLE-DB-Consumer in VC-2005 (db_command)

Ich habe Attributed ATL für OLE-DB-Consumer sehr nett gefunden und in VC.NET 2002 gleich eingebaut. In VC-2003 war auch alles noch OK.

Jetzt rächt sich meine Inovationsfreude in VS-2005! 😥

  1. Der Syntax von VC-2003 nach VC-2005 hat sich geändert! Aller Parameter müssen jetzt als Strings übergeben werden.
    https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=98753
  2. Der Parameter bulk_fetch in db_command wird nicht mehr verstanden. Egal ob man ihn als String oder als Integer angibt.
    https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=275256

Mein Rat: Man sollte kein Attributed ATL, so schön es auch ist. Man sollte bei den Standard Templates bleiben. Ist etwas mehr Schreibaufwand, aber:

  1. Leichter zu Debuggen
  2. Kompatibel
  3. Leichter kompatibel zu machen. 😉

Anmerkung zum Schluss:
Aus Gerüchten und Diskussionen bei Microsoft habe ich gehört, dass kein weiteres großes Augenmerk auf Attributed ATL gelegt wird.

Anfänglich als großer Wurf gefeiert. Mit der Möglichkeit in Zukunft vielleicht selbst als Entwickler attributierbare Module erzeugen zu können, scheint es jetzt eher ein Auslaufmodell zu werden.
Es gab zu viele Probleme damit und hat sich in der Entwicklung als kryptisch und schwer zu Testen herausgestellt.

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.

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.

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