CB_ADDSTRING/CB_INSERTSTRING+CBS_UPPERCASE+Unicode+const String Pointer == das große Erstaunen

Es gibt immer wieder Momente in denen einen die Win32 API in größtes Erstaunen versetzt.
Meistens ist dies allerdings in diesen Momenten nichts positives. Das zweite Erstaunen folgt, dann wenn man in der MSDN nachliest und das Verhalten  als dokumentiert vorfindet. Meistens endet solch eine Kette dann in einem Kopfschütteln und dem Gedanken: Das darf doch gar nicht sein.

Genug der langen Vorworte:

  • Gegeben ein normales MFC Projekt. (WinAPI pur tut es auch 😉 )
  • Ein Dialog
  • Darin eine Combobox als Dropdown mit Eingabemöglichkeit
  • Die Combobox wird mit einigen Variablen Werten gefüllt mit CComboBox::Addstring/InsertString, was letzten Endes nur ein Wrapper für CB_ADDSTRING/CB_INSERTSTRING ist.
  • Das ganze sieht also in OnInitDialog in etwa wie folgt aus
cb.AddString(CString(MAKEINTRESOURCE(IDS_DATA1)));
cb.AddString(CString(MAKEINTRESOURCE(IDS_DATA2)));
cb.AddString(CString(MAKEINTRESOURCE(IDS_DATA3)));
cb.AddString(strSomeDynamicData);
cb.InsertString(0,_T(""));

Alles OK… Als letztes noch den Stil CBS_UPPERCASE dazu – weil hier eben nur Eingaben in Großbuchstaben erlaubt sein sollen und Sinn machen – und… Peng!
Das Programm schmiert ab. 😮

Was ged’n hier ab Alder ❓

In den tiefen des Aufrufs von cb.InsertString(0,_T(„“)); schmiert mein Programm ab. UAE
Nun aber doch großes Erstaunen, denn der selbe Code funktioniert in einem MBCS Programm.
Es liegt eindeutig an der Nutzung von CBS_UPPERCASE und Unicode.

Jetzt habe ich schon gedacht einen Bug in Vista und XP gefunden zu haben, denn auf beiden schmiert bersagter Code ab. Und wahrscheinlich ist es auch ein Bug! Aber das genauere Nachlesen der MSDN belehrt mich eines besseren. Dieses dämliche Verhalten ist dokumentiert, zumindest für CB_ADDSTRING (der Zusatz fehlt in der CB_INSERTSTRING Doku):

Comclt32.dll version 5.0 or later: If CBS_LOWERCASE or CBS_UPPERCASE is set, the Unicode version of CB_ADDSTRING alters the string. If using read-only global memory, this causes the application to fail. ❗

Unfassbar! Die Nachricht ist dabei selbst so beschrieben, wie es sich jeder vernünftige Entwickler auch denkt und vor allem erwartet, eben mit einem LPCTSTR:

lResult = SendMessage(      // returns LRESULT in lResult
     (HWND) hWndControl,    // handle to destination control
     (UINT) CB_ADDSTRING,  // message ID
     (WPARAM) wParam,      // = 0; not used, must be zero
     (LPARAM) lParam       // = (LPARAM) (LPCTSTR) lParam;
);

Aber was nützt schon ein vernünftiger Gedanke eines Entwicklers wenn es um das irrwitzige Eigenleben der Win32 API geht.
Und denke nie einen Bug gefunden zu haben, bevor Du nicht jede Zeile der MSDN studiert hast 😉

Visual Studio 2008 Service Pack 1 Beta wurde gestern veröffentlicht

Die Beta des SP1 für Visual Studio 2008 wurde getsern veröffentlicht und steht zum Download zur Verfügung!

We are excited to pre-announce the availability of the the Visual Studio 2008 Service Pack 1 Beta download.

Visual Studio 2008 SP1 delivers various improvements to Visual Studio 2008 such as support SQL Server 2008 and new ADO.NET features such as the Entity Framework, numerous improvements to the WPF designers, WCF Templates for Silverlight projects, debugger support for the .NET Framework public symbols and source release, control improvements and additions (such as the DataRepeater for Windows Forms and Office 2007 Ribbons for C++), and a number of general debugging and Intellisense updates. This Service Pack also includes fixes to improve the stability, performance and security of many areas of the product. Due to the release schedule of this product, and the fact we have a very short feedback window, we would love to hear from our targeted community members 

Downloads

Hotfix für UseMSPrivateAssemblies.h und VC-2008

Einige nutzen ja meine Lösung für private CRT und MFC Assemblies unter VC-2005, die ich in dem diesem Artikel unter Codeproject veröffentlicht habe
http://www.codeproject.com/KB/cpp/PrivateAssemblyProjects.aspx

Das Interesse und die Nachfrage ist groß dieses Verfahren auch unter VC-2008 zu nutzen.
Da ich aber aktuell wenig Zeit habe den Artikel komplett zu überarbeiten, veröffentliche ich den relevanten Code hier erst mal vorab als „Hotfix“. Dieser Hotfix setzt voraus, dass das aktuelle Feature Pack installiert ist. Der Code ist nicht auf die RTM Version hin zugeschnitten und getestet.

UseMSPrivateAssemblies.h

// Version 2.0 by Martin Richter [WWJD]
// Supports VC-2005 and VC-2008
#pragma once    

#ifndef RC_INVOKED
// Avoid problems with the resource compiler if included    

// This defines bock the creation in the header files
#pragma message("Using private assemblies for the MS runtimes")
#define _STL_NOFORCE_MANIFEST
#define _CRT_NOFORCE_MANIFEST
#define _AFX_NOFORCE_MANIFEST
//#define _ATL_NOFORCE_MANIFEST    

// The next statements block the linker from including object files in the
// CRT and the MFC, that would create manifest pragmas too.
#ifdef __cplusplus
extern "C" {            /* Assume C declarations for C++ */
#endif    

__declspec(selectany)       int _forceCRTManifest;
__declspec(selectany)       int _forceMFCManifest;
// __declspec(selectany)    int _forceAtlDllManifest;    

// The next symbols are used by the several versions of VC 9.0
__declspec(selectany)       int _forceCRTManifestRTM;
__declspec(selectany)       int _forceMFCManifestRTM;
__declspec(selectany)       int _forceMFCManifestCUR;    

#ifdef __cplusplus
}                        /* __cplusplus */
#endif    

// We use crtassem.h with the defines there. It just gives us the
// versions and name parts for the dependencies.
// Note that there is also a MFCassem.h but this include file has the
// manifest pragma's already in it. So we can't use it
//
// Three files are controlling this crtassem.h, MFCassem.h and atlassem.h!
// Happily __LIBRARIES_ASSEMBLY_NAME_PREFIX is used in CRT, MFC and ATL!
// Doing it right would need to use _MFC_ASSEMBLY_VERSION for the MFC
// but in fact _CRT_ASSEMBLY_VERSION and _MFC_ASSEMBLY_VERSION and
// _ATL_ASSEMBLY_VERSION are the same
//  - VC-2005 SP1 8.0.50727.762
//  - VC-2008 RTM 9.0.21022.8
//  - VC-2008 Feature Pack 9.0.30411.0 (used if _BIND_TO_CURRENT_VCLIBS_VERSION
//    and _BIND_TO_CURRENT_MFC_VERSION are defined to 1)    

#include <crtassem.h>

// We don't have a seperate block for the Debug version. We just handle
// this with a extra define here.
#ifdef _DEBUG
#define __LIBRARIES_SUB_VERSION    "Debug"
#else
#define __LIBRARIES_SUB_VERSION    ""
#endif    

// Manifest for the CRT
#pragma comment(linker,"/manifestdependency:\"type='win32' "                        \
    "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX "." __LIBRARIES_SUB_VERSION "CRT' "   \
    "version='" _CRT_ASSEMBLY_VERSION "' "                                          \
    "processorArchitecture='x86' \"")    

// Manifest for the MFC
#pragma comment(linker,"/manifestdependency:\"type='win32' "                        \
    "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX "." __LIBRARIES_SUB_VERSION "MFC' "   \
    "version='" _CRT_ASSEMBLY_VERSION "' "                                          \
    "processorArchitecture='x86'\"")    

// #pragma comment(linker,"/manifestdependency:\"type='win32' "                     \
//     "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".MFCLOC' "                        \
//     "version='" _CRT_ASSEMBLY_VERSION "' "                                       \
//     "processorArchitecture='x86'\"")    

// Manifest for the ATL
// #pragma comment(linker,"/manifestdependency:\"type='win32' "                     \
//    "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".ATL' "                            \
//    "version='" _CRT_ASSEMBLY_VERSION "' "                                        \
//    "processorArchitecture='x86' \"")    

#endif // RC_INVOKED

Anmerkungen:

  • Im Endeffekt sind nur 3 Zeilen (26-28) hinzugekommen.
  • Diese Version funktioniert sowohl für VC-2005 als auch VC-2008!
  • Unter Vista wird allgemein das Problem beobachtet, das private Assemblies nur genutzt werden können, wenn diese in einem Unterverzeichnis liegen. Liegen die Assembly Dateien im gleichen Verzeichnis wie die EXE kommt es zu einem Fehler „The application failed to initialize properly (0xc0000034). „ Dieser Sache bin ich (und andere) auf der Spur.
  • Es spielt für diesen Code keine Rolle ob die beiden Defines _BIND_TO_CURRENT_VCLIBS_VERSION und _BIND_TO_CURRENT_MFC_VERSION gesetzt wurden. Werden diese Defines auf 1 gesetzt bevor UseMSPrivateAssemblies inkludiert wird, dann werden die Manifeste so erzeugt, dass die Feature Pack DLLs gezogen werden. Sind diese beiden Defines nicht gesetzt werden Manifeste für die RTM Version erzeugt.
    Ich empfehle dringend diese beiden Defines zu setzen ❗

Das ist erstmal ein Schnellschuss für alle, die die es etwas eiliger haben.

Der Vorteil gegenüber der Lösung, bei der die Manifeste manuell bearbeitet werden, wie es zum Beispiel Jochen Kalmbach in seinem Blog vorgestellt hat ist klar:
Man muss eben nichts manuell machen 🙂
Es macht wieder alles der Compiler und Linker.

Tipps & Tricks: Testcode sollte immer in #ifdef _DEBUG #endif Blöcke integriert sein!

Dieser Tipp hört sich trivial an, aber wenn man sich nicht dran hält erlebt man übelste Überraschungen.
Nur zu oft muss man während der Entwicklung oder bei der Fehlersuche Code einbauen, der Test, Ausgaben, Verzögerungen oder sonstige Operationen ausführt, die mir als Entwickler helfen ein Problem zu finden, oder einer Lösung für eine verzwickte Frage zu lösen. Um so größer das Problem wird um so mehr Stellen werden oft verändert.
Es bleibt die Frage ob sich jeder Entwickler noch erinnert wo er überall etwas für Testzwecke eingebaut hat.

Das üble Ergebnis ist, dass man manchmal toten nutzlosen, Performance fressenden Code ausliefert. Oder gar Code ausliefert, der evtl. zu neuen Fehlern führt. Da muss nur ein einfacher DebugBreak im Code zurückbleiben und schon crashed die Anwendung sauber beim Kunden…

Testcode sollte grundsätzlich in einem #ifdef Block eingebaut werden. Und Code der wirklich nur für Tests vorhanden ist und er sogar später in der Debug Version des Programmes nichts zu suchen hat sollte mit einem #else #error versehen werden. Ein ASSERT kann einen viel abnehmen um so etwas zu vermeiden, aber sogar mancher ASSERT  ist später überflüssig und behindert auch Tests in der Debug Version.

So habe ich in unserer Software einen Sleep(100); 😮 gefunden, der von einem Entwickler eingebaut wurde, um einen Crash in einem komplexen Kommunikationsproblem zwischen mehreren Threads zu finden.Er hat den Fehler gefunden, den Sleep aber nie wieder entfernt.
Hätte mein Kollege sich an meine Spezifikationen gehalten, hätten wir nicht nachträglich auf die mühsame Suche gehen müssen wo unsere Performanceverluste bei 5% Prozessorlast herkommen. So wäre das Ganze schon beim Release-Build aufgefallen:

#ifdef _DEBUG
// Test Sleep to find cross thread problems for bug#234
Sleep(100);
#else
#error Remove test condition here. Just used to find bug#234 
#endif

BTW: Ich verwende deshalb immer ein Code-Snippet über mein VisualAssist X, der mir einen entsprechenden Codeblock einsetzt.

Der zweite Versuch: Visual C++ 2008 Feature Pack Refresh (MFCNext & TR1)

Nach dem ersten Versuch nun der zweite Versuch. Alle bisher bekannten Installationsprobleme sind gefixed.

Weitere Infos: http://blogs.msdn.com/vcblog/archive/2008/04/22/visual-c-2008-feature-pack-refresh.aspx

Download: http://www.microsoft.com/downloads/details.aspx?FamilyID=d466226b-8dab-445f-a7b4-448b326c48e7&displaylang=en

Das bereits installierte Feature Pack muss deinstalliert werden! Glücklich wer nur die Beta bisher installiert hat, der kann wie bisher einfach drüber installieren. ❗

BTW: Eine Responsezeit von 16 Tagen ist gar nicht soooo schlecht 😉

VS Tipps & Tricks: Ganze Solutions zu einer Master-Solutions zusammenfügen

Wie andere Programmierer auch habe ich meine Projekte in einzelne Solutions zusammengefasst. Nicht wenige dieser Solutions haben auch komplexere Abhängigkeiten.

Bisher hatte ich insgesamt dann 4 größere Solutions, die alle Projekte dann für einen vollen Release-Build bündelten. Bisher wurden diese großen Solution-Builds, dann durch spezielle Batchfiles gesteuert. 

Seit ich den TFS (Team Foundation Server) verwende habe ich begonnen alle einzelnen Komponenten direkt auch auf das Buildsystem des TFS hin abzubilden. Dazu gehört auch, alle Projekte in einer großen Solution zusammenzufassen, um nun im Teambuild alles weitere ablaufen zu lassen.

Als ich mühsam Projekt für Projekt in eine große Master-Solution einfügen wollte machte ich eine nette Entdeckung:
Man kann nicht nur existierende Projekte in eine Solution einfügen. Nein! Es ist sogar möglich eine ganze Solution in eine bestehende Solution einzufügen und dabei sogar alle Abhängigkeiten in dieser Solution zu erhalten. Einfach im Dateidialog die Solutions auswählen und das war es auch schon.
Das macht es wirklich einfach auch komplexere Solutions in eine Master-Solution zusammenzufügen. Jetzt ist es auch nicht weiter schwer die verbleibenden Abhängigkeiten, die bei mir in dem Batch geregelt wurden zu definieren.

Visual Studio 2008 Feature Pain…

Hier die bisherige offizielle Bug-Liste des gerade veröffentlichten Feature Packs:

http://blogs.msdn.com/vcblog/archive/2008/04/12/visual-c-2008-feature-pack-setup-deployment-issues.aspx

Mit Ruhm hat sich Microsoft hier wirklich nicht bekleckert.
Vor allem ist das Feature Pack ohne ein funktionierendes Deployment für Vista erstmal wertlos…

In der Haut derjenigen, die das Setup und Deployment zu verantworten haben möchte ich nicht stecken.

Leider ist das Ganze wieder mal nicht ganz vertrauenserweckend. Man kann ja jetzt nur hoffen, dass der Rest des Packs mit nicht ganz so heißer Nadel gestrickt wurde. Die nächste Zeit wird es zeigen.

Visual C++ 2008 Libraries Extension Feature Pack Final Release (MFCNext & TR1)

❗ Es ist da, die finale Version des MFC-Feature Pack für VS-2008 (MFCNext & TR1) ❗

Das Feature Pack ist nur für die Englische VS-Version (ENU) ab Visual Studio 2008 Standard Edition verfügbar.
Andere Sprachversionen werden in Visual Studio 2008 Service Pack 1 enthalten sein.

Visual C++ 2008 Libraries Extension Feature Pack Final Release
Download link, 322.8 MB
Redistributable Package (x86)

Dokumentation:
MSDN MFC Feature Pack for Visual C++ 2008
MSDN TR1 Extensions

❗ Wichtige Anmerkung sofern ein Deutsches OS eingesetzt wird ❗

Bei mir schlug die erste Installation fehl (sowohl auf Deutschem Vista Ultimate SP1 als auch XP SP2). In der Log-Datei im %TMP% Verzeichnis konnte ich herausbekommen, dass die Datei SPInstallerResources.1031.dll vermisst wird:

Unable to load UI satellite DLL SPInstallerResources.1031.dll 

Nach einigem hin und her Tricksen mit Entpacken und Umbenennen der Datei fand ich folgenden Trick um das Feature Pack zu installieren:
Man muss einfach vorher in der Systemsteuerung in den Regions- und Spracheinstellungen, die Einstellungen für die Formate auf „Englisch (USA)“ umstellen. Zusätzlich habe ich auch den Standardort auf „Vereinigte Staaten“ eingestellt. Danach lief die Installation korrekt ab.

Es spielt übrigends keine Rolle ob die Feature Pack Beta Version zuvor installiert war!

Vista Stil bei Tree-Controls und List-Controls

Unter Vista wurden die Tree- und List-Controls noch einmal kräftig aufgemöbelt.
Diesen neuen Look kann man auch in seinen Applikationen nutzen und das mit 2 simplen Codezeilen.

#pragma comment(lib,"uxtheme.lib") 
SetWindowTheme(m_myCtrl.GetSafeHwnd(),L"Explorer",NULL);

Und schon hat man die neue Darstellung des Tree-Controls mit den Dreiecken, die Knotenlinien sind weg.
Beim List-Control hat man nun die hellblaue Färbung mit dem Hover-Effekt.

Vorraussetzung dafür ist, dass auch ein Manifest für die Windows Common Controls Version 6 vorliegt. Dem entsprechend ist SetWindowTheme und die Uxtheme.dll erst ab Windows XP verfügbar. Wer das ganze auch kompatibel mit Windows 2000 haben möchte, der verwendet natürlich das dynamische Laden der Uxtheme.dll und sucht den Einsprungpunkt für SetWindowTheme mit GetProcAddress!

Siehe auch MSDN Doku zu SetWindowTheme

C++ Community Event am 17.04 findet in Bad Homburg statt

Der folgende Community Event findet in Bad Homburg statt und nicht wie angekündigt in Frankfurt.

C++ Community Event in Bad Homburg mit Microsoft Program Managern
Ich habe den entsprechenden Artikel geändert.

Termin & Ort:

  • Datum: 17.4.2008, 16 – 19 Uhr

  • Ort: Steigenberger Hotel Bad Homburg
    Kaiser-Friedrich-Promenade 69-75
    61348 Bad Homburg v. d. Höhe

Anmeldung erfolgt per Email über den offizieller Link für die Veranstaltung:
http://blogs.msdn.com/softwarehersteller/pages/cpp2008-4-ffm.aspx