Die Unsitte immer GetModuleHandle(NULL) für hInstance in CreateWindow und RegisterClass zu verwenden

Carsten hat mich dazu inspiriert noch ein wenig mehr Don Quichotte zu spielen und gegen Windmühlen zu kämpfen: Damit hier mein zweiter Beitrag zum Thema Unsitten.

Der hInstance Parameter in CreateWindow(Ex) und RegisterClass (WNDCLASS) wird oft genug nicht verstanden. Man braucht für diese Funktionen einen HINSTANCE Wert. Den hat aber niemand in der Tasche, wenn man mit der Win32 API pur mal eben so ein Programm schreibt. Die wenigsten kommen auf die Idee den hInstance Wert aus WinMain und DllMain irgendwo global zu speichern und zu verwenden. Globals sind ja irgendwie „böse“, und nicht OOP-like… 😉

Was also tun? Irgendein Unwissender empfiehlt einfach GetModuleHandle(NULL) zu verwenden und seit dem dieser Unwissende diesen Tipp in die Welt gesetzt hat kursiert er durch die Foren… unaufhaltsam…

❗ Das Problem: Es geht… manchmal… aber portabel ist der Code damit nicht und man erlebt eigentümliche Sachen damit an anderer Stelle unter annderen (DLL-)Umständen. Aber warum?

Das Geheimnis steckt darin, dass Fensterklassen nicht einfach so unter ihrem Namen abgelegt werden, sondern unter einem Namen und der hInstance, die bei RegisterClass mit angegeben wird. So hat jeder Prozess seine Liste der Fensterklassen die aus hInstance und dem Namen der Klasse bestehen. Auch die Standard Fensterklassen wie Edit, Button, Static etc. werden mit der hInstance der User32.dll gespeichert.

Wird nun ein Fenster erzeugt mit CreateWindow(Ex), dann benutzt der Windowsmanager die Kombination aus hInstance Wert, der bei CreateWindow(Ex) angegeben wird und dem Namen der Klasse um den entsprechenden Klassen Eintrag zu finden.

Es ist also vollkommen OK wenn DLLs selber Klassen registrieren mit ein und unterschiedliche Module den selben Namen verwenden. Da gibt es keinen Konflikt, denn durch die hInstance Werte bleiben die Klassen eineindeutig. Und auch jede DLL kann damit, dass von Ihr gewünschte Fenster mit der entsprechenden Klasse erzeugen, denn jede DLL hat eine unterschiedliche hInstance.

Einzige Ausnahme ist eine Klasse, die mit dem Klassenstil CS_GLOBALCLASS registriert wird. Bei solch einer Klasse wird der Windowsmanager nur auf den Namen und nicht auf den Wert der hInstance achten. Jedem wird klar sein, dass die Standardfensterklassen der USER32.DLL und auch die Klassen der COMCTL32.DLL mit diesem Stil registriert wurden.
Und klar ist auch, dass man seine eigenen Klassen, die in DLLs liegen mit diesem Flag CS_GLOBALCLASS registrieren muss, wenn man diese Klassen applikationsweit verwenden will. Würde man also ein Dialogtemplate mit einer eigenen Klasse anlegen, die in einer DLL liegt, so kann der Dialogmanager, der wieder hInstance der Applikation verwendet, die entsprechende Klasse nicht finden, wenn diese nicht mit CS_GLOBALCLASS registriert wurde.

Es ist und bleibt eine Unsitte GetModuleHandle(NULL) beim Registrieren der Windowsklasse zu verwenden, denn dieses hInstance, das man erhält ist natürlich das hInstance der Applikation, und nicht das des Modules, welches die Klasse enthält, z.B. eben eine DLL. Es wundert nicht wenn man etwas später seine Klassen in eine DLL auslagert erstaunt feststellt, dass auf einmal die Sachen nicht mehr funktionieren wie sie sollen.

Um solchen Problemen aus dem Weg zu gehen sollte man immer das hInstance verwenden, das zu dem entsprechenden Modul gehört. Diesen Wert erhält man durch WinMain oder DllMain. Am Besten man speichert diesen in einer globalen Variablen. Die MFC hat hierzu eine spezielle Funktion AfxGetInstanceHandle, die dies für das entsprechende Modul erledigt. Aber ein MFC Programmierer würde ja auch AfxRegisterClass verwenden und nicht in diese Probleme laufen, wiederum vorausgesetzt er verwendet auch brav AFX_MANAGE_STATE 🙂

❗ Anmerkung: Es gibt auch Code, der einem erlaubt das hInstance Handle zu einem Code Abschnitt zu bestimmen.
http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx (sofern man einen MS-Linker verwendet)

Auch der folgende etwas trickreichere Code ist einen Blick wert:

HINSTANCE Get_hInstance()
{
 MEMORY_BASIC_INFORMATION mbi; 
 VirtualQuery(Get_hInstance, &mbi, sizeof(mbi)); 
 return reinterpret_cast<HINSTANCE>(mbi.AllocationBase); 
}

Aber bitte diesen Code nicht in eine DLL auslagern :mrgreen:

Die Unsitte PostQuitMessage zum Beenden eines Programmes zu verwenden!

Immer wieder lese ich Postings in http://www.c-plusplus.de/forum die es anpreisen ein Programm mit PostQuitMessage zu beenden, genau so unsinnig wie WM_QUIT zu versenden. 

Das ist natürlich Unfug! Sicherlich wird ein Programm durch PostQuitMessage beendet, aber warum?
Weil die Nachrichtenschleife verlassen wird und letzten Endes WinMain verlassen wird. Dies führt dazu, dass die darunter liegenden CRT Routinen irgendwann ExitProcess ausführen. BTW: Würde hier nur ein der CRT einfacher return erfolgen, dann würde der Prozess weiterleben, wenn noch ein einziger anderer Thread aktiv wäre.

Das brutale Verlassen führt aber letzten Endes auch dazu, dass erst ExitProcess brutal alle Fenster aufräumt. D.h. kein Fenster wird normal zerstört, kein WM_DESTROY bzw. WM_NCDESTROY wird empfangen. D.h. alle normalen Prozesse, die dem Aufräumen und Freigeben von Ressourcen dienen, werden außer Kraft gesetzt.
Ja und sicherlich gibt ExitProcess Speicher frei, die der Prozess alloziert hat, auch einige Handles können freigegeben werden, aber nicht alle (z.B. benamte Mutexe und Semaphoren).

Bei einem Mikey Mouse Win32 API Programm mag dies kein Problem sein, denn hier gibt es keine Ressourcen, die Prozessübergreifend ein Leak verursachen würden, oder eine Ressource blockieren würden.
Aber grundsätzlich würde ich es unterlassen. Jede andere Library, die man verwendet, jedes externe Control, dass man einbindet könnte genau auf dieses entscheiden WM_DESTROY angewiesen sein um Ressourcen freizugeben, die ein System blockieren könnten. Solange man nicht 100%ig weiß wie die benutzen Bibliotheken arbeiten, ja nicht einmal detailliert weiß wie COM und die CRT Handles behandeln würde ich grundsätzlich abraten ein Programm einfach mit ExitProcess zu verlassen, genau so wie ich abrate TerminateProcess zu verwenden.

Der richtige Weg ist und bleibt es das/alle Main Window(s) zu zerstören und entsprechend dann (im WM_DESTROY Handler) PostQuitMessage (AfxPostQuitMessage) auszuführen. Durch das Zerstören des Hauptfensters werden natürlich alle enthaltenen Child-Windows mit zerstört. Alle Fenster bekommen damit die Chance hinter sich aufzuräumen und Ressourcen frei zu geben.

PS: Aber solche Unsitten lassen sich kaum ausmerzen. Genauso wenig wie die Unsitte einen HINSTANCE Wert einfach durch Aufruf von GetModuleHandle(NULL) zu bestimmen… Auch eine Unsitte, die wohl niemand mehr ausmerzen wird.

CreateThread und die CRT

Immer wieder sehe ich Code Snippets die CreateThread verwenden.
Immer wieder antworte ich gleichlautend:

Vermeide CreateThread, wenn Du CRT Funktionen verwendest, das erzeugt Leaks in der CRT.
Verwende deshalb immer nur _beginthread(ex). ❗

Irgendwie glauben viele Programmierer, dass es besser ist eine möglichst OS-nahe Funktion zu verwenden. In diesem Fall ist es es definitiv nicht ratsam, denn droht in diesem Fall ein Speicherleck von um die 530Bytes (in VC-2005) pro beendetem Thread.

In Kürze: Das ganze Problem liegt darin, dass bestimmte CRT Funktionen thread lokalen Speicher benötigen. Dieser wird im TLS (Thread Local Storage) bei Bedarf angelegt und entsorgt wenn die Threadfunktion returniert oder mit _endthread beendet wird.
Gleiches gilt, wenn der Thread mit ExitThread oder TerminateThread beendet wird! (Das TerminateThread sowieso nicht in Frage kommen darf weil es noch übleres tut weiß ja hoffentlich sowieso jeder 😉 )

Hier eine entsprechende Sammlung der entsprechenden Dokumentationen mit den Begründungen: 

Jeffrey Richter „Advanced Windows“ 3rd Edition
Kapitel 4, „Processes, Threads and the C Run-Time Library“ Seite 108 folgende.

CreateThread Doku: http://msdn2.microsoft.com/En-US/library/ms682453.aspx

A thread in an executable that calls the C run-time library (CRT) should use the _beginthreadex and _endthreadex functions for thread management rather than CreateThread and ExitThread; this requires the use of the multi-threaded version of the CRT. If a thread created using CreateThread calls the CRT, the CRT may terminate the process in low-memory conditions.

ExitThread Doku: http://msdn2.microsoft.com/en-us/library/ms682659.aspx

A thread in an executable that is linked to the static C run-time library (CRT) should use _beginthread and _endthread for thread management rather than CreateThread and ExitThread. Failure to do so results in small memory leaks when the thread calls ExitThread. Another work around is to link the executable to the CRT in a DLL instead of the static CRT. Note that this memory leak only occurs from a DLL if the DLL is linked to the static CRT and a thread calls the DisableThreadLibraryCalls function. Otherwise, it is safe to call CreateThread and ExitThread from a thread in a DLL that links to the static CRT.

KB-Artikel 104641: http://support.microsoft.com/kb/104641/en-us

Threads that are created and terminated with the CreateThread() and ExitThread() Win32 API functions do not have memory that is allocated by the CRT for static data and static buffers cleaned up when the thread terminates. Some examples of this type of memory are static data for errno and _doserrno and the static buffers used by functions such as asctime(), ctime(), localtime(), gmtime(), and mktime(). Using CreateThread() in a program that uses the CRT (for example, links with LIBCMT.LIB) may cause a memory leak of about 70-80 bytes each time a thread is terminated.

To guarantee that all static data and static buffers allocated by the CRT are cleaned up when the thread terminates, _beginthreadex() and _endthreadex() should be used when creating a thread. The _beginthreadex() function includes the same parameters and functionality as CreateThread().

Note It is not possible to terminate a thread with _endthreadex() when it was created with CreateThread(). ❗

❗ Anmerkung: Dieser KB artikel ist leider unvollständig. Hier werden nur ein paar time Funktionen aufgelistet, es trifft aber weitaus mehr Funktionen wie errno, strtok, strerror, tmpname, tmpfile, _ecvt, _fcvt, alle Funktionen die _errno oder _doserrno nutzen etc.

ATE beim „Ready for take off“ in Frankfurt vom 19.-21.02.2008

Wer mich mal persönlich kennen lernen will, der kann mich in Frankfurt beim großen Launch “Ready for take off” von Windows Server 2008, SQL Server 2008 und Visual-Studio 2008 vom 19.-21. Februar 2008 treffen.

Ich werde einer der vielen MVPs sein, die als ATEs (Ask the Experts) die Veranstaltung begleiten.

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.

Weitere Infos zur MFC in VS-2008 und der BCG Pro-Library

Hier noch ein paar Infos zu dem Thema:

Eine Microsoft Bekanntmachung auf den US VC++ Seiten: MFC Update Powered By BCGSoft
http://msdn2.microsoft.com/de-de/visualc/bb892882.aspx

Eine Pressemitteilung von BCG-Soft vom 09.11.2007:
http://www.bcgsoft.com/pressreleases/PR071110.pdf

Fragen und Antworten im BCG-Forum:
http://www.bcgsoft.com/cgi-bin/forum/topic.asp?TOPIC_ID=4476

Intellisense Hotfix für VS-2005 ist verfügbar

Soeben wurde ein Hotfix veröffentlicht der einige Performance Probleme mit dem Intellisense in Visual-Studio 2005 beheben soll.

Infos über diesen Hotfix gibt es im Visual C++ Team Blog.
Weitere Infos und die Liste der betroffenen Dateien findet man auch in in diesem KB-Artikel 943969

Hier der aktuelle Download Link für die englische Version.
Ob andere Sprachversionen (Deutsch) verfügbar sein werden wurde bereits bei der Produktgruppe intern nachgefragt.

Wichtig ❗ SP1 für VS-2005 muss installiert sein für diesen Hotfix.

Anmerkung: Nutzer von VisualAssist von http://www.wholetomato.com  bemerken von diesen Problemen kaum etwas.

Microsoft und sein Bekenntnis zu C++

Eigentlich ist es ja schön, dass Microsoft sich endlich mal zu etwas mehr durchgerungen hat, als nur partieller Kosmetik im Bereich der nativen Softwareentwicklung mit C++.

Nach meiner Meinung kommt das ganze allerdings um Jahre zu spät! Ich hätte spätestens mit VS-2005 erwartet, dass Microsoft den verbalen Bekenntnissen, dass es weiterhin auch native Entwicklung mit C++ geben wird, auch Taten folgen lässt.

Microsoft hat sich mit der einfältigen Umbenennung von Visual Studio in ein VS.NET 2002 keinen Gefallen getan. Die Verunsicherung unter den vielen C++ Entwicklern durfte ich damals in Neuss beim großen Launch des VS.NET 2002 als ATE (Ask the Expert) am eigenen Leib erfahren.

Geändert hat sich nicht viel in den Jahren danach. Die Verunsicherung ist geblieben und Vertrauen wurde keines zurück gewonnen. Ein vorbehaltloses Prüfen der neuen Compiler und Visual Studio Versionen hat in vielen Firmen nicht stattgefunden. Die Änderungen waren gravierend sicherlich, wenn man den Compiler und die IDE von VC++ 6.0 mit der von VC++ 2005 vergleicht, da liegen einfach Welten dazwischen.

Irgendwie hatten zu viele Firmen und Entwickler das Gefühl „nun stirbt C++“, „jetzt  ist die vielfach totgesagte MFC wirklich tot“. Zuversicht in die bestehende Technologie zu setzen war einfach nicht da.

Das Micrsoft natives C++ nicht abschreibt wurde zwar ab und zu gesagt, aber das hörte sich eher wie Pfeifen im dunklen Wald an. Die ganze Situation machte es C# und .NET Entwicklern leicht auf C++ Entwicklern herumzuhaken und sie zutiefst zu bemitleiden (natürlich immer mit einem hämischen Grinsen). Und teilweise geschah dies sogar aus den eigenen Reihen von Microsoft. Hatte man auf einmal mit C++ keine zukunftsweisende Technologie mehr?
Die Tracks bei Kongressen füllten sich mit .NET Beiträgen. Und was sich in C++ getan hat (und es hat sich noch einiges getan) wurde kaum veröffentlicht oder nicht gehört. Auch was Dokumentationen betrifft, hat sich die C++ Gruppe nicht mit Ruhm bekleckert.

Noch „besser“ wurde es als schwachsinnige Aussagen die Runde machte, dass die nächsten Betriebssysteme nur noch .NET erlauben würden, und demnächst alles, bis zum Treiber mit .NET entwicklet würden. C++ braucht man nicht mehr.
Schaut man sich heute an, wieviel .NET in Vista und Windows 2008 Server steckt, dann merkt man, wie blödsinnig diese Annahmen waren und das es kaum Ersatz gibt für native Entwicklung in C++.
Fakt ist: bis heute wurde keine wirklich weitreichende und wichtige Schnittstelle in Win32 geschaffen die nur aus der managed Welt anzusprechen wäre. Und selbst wenn, so ist die Brücke aus C++ in die managed Welt ist kurz…

Warum wurden keine Walkthroughs und Dokumentationen geliefert , die einem bei der Umstellung von Code der unter VC6 entwickelt wurde auf VC++2003/2005 erklären und leichter machen? Walkthroughs, die gezielt auf die speziellen Warnungen eingehen, die einem um die Ohren fliegen, oder konstruktiv bei Problemen mit dem Umstieg helfen, finden wir heute höchstens in der Community entsprechende Beiträge.

Wäre es zu viel verlangt gewesen, zum Beispiel sofort bei der Umstellung eines VC6 Projektes bestimmte Projekteinstellungen so zu gewährleisten, dass möglichst wenig Warnungen entstehen, aber der Programmierer auf die Änderungen, die seinen Code sicherer und kompatibler machen, klar hinweisen?

Es ist verschlafen worden! Ja es wurde Jahre lang geschlafen. MFCnext ist schön, nett. Das TR1 kommt ist prima. Ich frage mich aber ob es wirklich genügt das verloren gegangene Vertrauen wieder zurück zu gewinnen?

Just my 2 cents…

MFC Updates for Visual Studio 2008 and Beyond: MFCnext

Was ich hier schreibe ist an sich viel zu viel für einen einzigen Blog Artikel, deshalb behalte ich mir hier nur eine kurze Zusammenfassung vor. Kommentieren und diskutieren werde ich dies noch in den nächsten Wochen.

Was ❓
Heute in den C++ Sessions der TechEd 2007 in Barcelona wurde ein Geheimnis gelüftet. Das war uns MVPs schon etwas länger bekannt aber leider war es uns bis heute durch NDA (non-disclosure aggreement) untersagt davon zu berichten.
Microsoft wird der MFC einen neuen gewaltigen Schub geben und seit Jahren die ersten wirklich gravierenden Erweiterungen verpassen. Das Ganze wird unter dem Namen MFCnext laufen.

In diesem Bog will ich nur summarisch aufzählen was alles an Neuem kommen wird:

  • Office 2007 UI Stil. Ribbons und alles was dazu gehört
  • Tabbed MDI
  • Integration neuer Controls (Advanced button, Shell tree and list, Mask edit, Property list)
  • Erweiterter Applikations Assistent
  • Rückwärts kompatibel bis Windows 2000
  • Docking wie wir es von Visual Studio her kennen
  • Visual Styles (Skins)
  • Die Standard Template Library wird um TR1 erweitert (tr1::shared_ptr, tr1:: mem_fn, tr1:: bind, tr1::regex, tr1::tuple, tr1::array, unordered containers hash-based, tr1::type_traits)
  • Microsoft bekennt sich klar und offen zur weiteren Entwicklung und Unterstützung der nativen C++ Programmierung.
  • und vieles andere mehr…

Die MFC wird sich in Anzahl der Klassen und Größe verdoppeln.

Wann kommt das ❓
Das Ganze wird bereits im März 2008 nachgeliefert, in einem Update für VS-2008. Ein Release Kandidat (RC) wird bereits im Dezember 2007 zur Verfügung stehen.  So die bisherige Planung.

Die MFC ist tot… lange lebe MFCnext 😀

Weitere Infos:
Auch in Deutschland bei dieser Veranstaltung werden die Vortargenden aus Barcelona zu sehen und hören sein:
München 15.11.2007: Visual C++ 2008 und danach, Ask the Experts

Noch mehr Infos gibt es auch in dem Blog von Jochen Kalmbach, der live aus Barcelona von der TechEd 2007 berichtet.