VS-Tipps und Tricks:Feststellen ob ATL oder MFC in einem Projekt benutzt werden

Für manche Standardklassen bzw. Header oder Libraries ist es manchmal schön zu wissen ob die ATL oder die MFC in einem Projekt verwendet werden.  In der Vergangenheit habe ich dies oft benutzt um bestimmte Member in Klassen einzubauen, die dann zum Beispiel Daten auch als CString aktzeptieren, oder diese Member dann eben nicht einzubauen um eine Nutzung in einem „puren“ WinAPI Projekt zu ermöglichen.
Seit die CString Klassen allerdings eigenständige Templates wurden ist dieser Grund für mich eigentlich weggefallen.
Ich benutzte es heute nur noch um evtl. Memberfunktionen zu unterscheiden die evtl. CWnd* zusätzlich zu HWND Parametern akzeptieren.

Aber wer weiß, vielleicht hat der eine oder andere doch die Frage wie er erkennen kann ob die ATL oder die MFC in einem Projekt Verwendung finden.

Vordefinierte Preprozessor Variablen gibt es dafür nicht, allerdings kann man erkennen ob die Standard ATL/MFCHeader in einem Projekt bereits als Include eingefügt wurden, denn in diesem Fall kann man die Existenz der Include-Guards prüfen.

Die MFC benutzt __AFX_H__ als Guard für die afx.h.
Die Basisklassen der ATL befinden sich in der atlbase.hund entsprechend lautet der Guard: __ATLBASE_H__.

Sofern also diese Guards definiert sind wurden auch die entsprechenden Libraries in der stdafx.h oder anderen Headern zuvor included.

Nachtrag 12.07.2011:
Stefan
hat natürlich vollkommen recht mit seinem Kommentar, dass es die zwei Präprozessor-Variablen _MFC_VER und _ATL_VER gibt, die natürlich für den hier erwähnten Einsatz weitaus besser geeigent sind.
Siehe: http://msdn.microsoft.com/de-de/library/b0084kay.aspx
Ich habe hier den Wald vor lauter Bäumen nicht gesehen 😉
Herzlichen Dank für diese produktive Ergänzung.

VS-Tipps & Tricks: Wie man gezielt einen Breakpoint für einen Thread setzen kann

Wenn man eine Anwendung oder einen Dienst hat, der mit vielen Threads arbeitet, dann kann das Debuggen abenteuerlich werden. Besonders wenn viele Threads ein und die selbe Threadfunktion verwenden.
Was macht man nun wenn man einen Thread isoliert hat und dessen Verhalten weiter prüfen möchte? Single-Stepping ist nicht drin, denn der Breakpoint würde auch jeden anderen Thread anhalten, der diese Code-Position erreicht.

Die nachfolgende Methode ist relativ einfach um gezielt einen Breakpoint für einen Thread zu setzen.
Gehen wir mal davon aus, dass wir im Debugger einen Breakpoint haben und uns im Kontext des Threads befinden, den wir nun weiter verfolgen wollen.

  1. Zuallererst ermitteln wir die Thread-Id.
    Das geht elementar einfach über die Pseudo-Debugger-Variable $TID, die wir uns im Watch-Fenster, oder im Quick-View anzeigen lassen:
  2. Als nächstes modifizieren wir den Breakpoint so, dass er nur noch dann stoppt, wenn auch unser Thread diesen Breakpoint erreicht.
    Das erreichen wir über die Breakpoint-Eigenschaft Condition (rechter Mausklick, über das Kontextmenü):

    Hier geben wir einfach als Bedingung an, das $TID (also die Thread-ID) identisch sein muss, zu der ID des Threads, den wir beobachten wollen.
  3. Den Rest macht der Debugger für uns, wenn wir wieder den Go-Befehl (F5) geben:

Auf diese Weise kann man auch mehrere Threads einfach beobachten.

Hinweis:
Dieses Verfahren ändert das Laufzeitverhalten des Programmes, denn der Breakpoint wird immer intern ausgeführt, egal welcher Thread diese Codestelle passiert. Nur ermittelt der Debugger dann die Bedingung returniert und lässt das Programm, dann weiterlaufen, wenn die Id nicht passt. Wird diese Codestelle sehr oft passiert, dann kann ist der Einfluss eines solchen Breakpoints nicht  unerheblich.

Alternativ, kann man in solch einem Fall auch ein Stück Code einsetzen, der die aktuelle Thread-ID gegen eine statische Variable testet und einen DebugBreak ausführt. Die statische Variable setzt man dann während der Debugsession auf die gwünschte Thread-ID über das Watch-Window oder den Quick-View

Weiterführende Infos zu Debugger-Pseudo-Variablen von Visual-Studio findet man hier:
http://msdn.microsoft.com/en-us/library/ms164891.aspx

GetModuleFileName liefert nicht exakt den Namen der EXE/DLL Datei wie er auf der Platte steht

Wir haben ein Stück Code, dass verhindern soll, dass ein Programm zweimal gestartet werden kann.
Dieser basiert auf einem Mutex und einer Memory Mapped File, mit der man sich auch das Fenster-Handle einer bereits gestarteten Instanz besorgen kann.

Nun gelang es einem unserer Händler aber dennoch dieses Programm zweimal in einer Session zu starten und zwar auf folgendem Weg:

  1. Er startet die Software mit dem normalen Link auf dem Desktop, der durch das Installationsprogramm angelegt wurde.
  2. Er öffnen eine Console mit CMD.EXE und wechselt in das Verzeichnis, gibt den Programmnamen ein und das Programm startet erneut. 😮

Die Ursache ist war wie folgt:

  1. Der Mutex den wir intern verwendet haben nutzte den Dateinamen der EXE. Der Name des Mutex wird unter Anderem auch durch GetModuleFileName ermittelt.
  2. Der Dateiname der EXE, wenn sie als Verknüpfung gestartet wird ist „XYZ.exe“ (so wie die Datei auch auf der Festplatte heißt) und das liefert auch GetModuleFileName als Ergebnis.
  3. Der Dateiname, den GetModuleFileName liefert, wenn man das Programm aus CMD.EXE startest ist exakt so wie man es eintippt, also z.B. „xyz.exe“. Erstaunlich.
  4. Da der Mutex einen Namen case sensitiv behandelt (was ich nicht vermutet hätte und erst mit staunenden Augen nachgelesen habe), wurde das bereits gestartete Programm nicht erkannt und eine zweite Instanz gestartet.

Was schreiben wir uns also hinter die Löffel für die Zukunft:
a) GetModuleFileName liefert nicht den „exakten“ Dateinamen (obwohl ich es anders erwartet hätte)!
b) Mutexe sind case sensitiv wie auch Events (obwohl ich hier eine Behandlung wie bei einem Dateinamen erwartet habe)!
c) Manche Erwartungen trügen… 😉

Patchday vom 14.06.2011 behebt die Probleme der VisualStudio 2005/2008 Servicepacks vom 13.04.2011

Die nachfolgenden 4 Sicherheitsupdates wurden am 14.06.2011 von Microsoft herausgegeben:

Sicherheitsupdate für Microsoft Visual C++ 2005 Service Pack 1 Redistributable Package (KB2538242)
Sicherheitsupdate für Microsoft Visual Studio 2005 Service Pack 1 (KB2538218)
Sicherheitsupdate für Microsoft Visual C++ 2008 Service Pack 1 Redistributable Package (KB2538243)
Sicherheitsupdate für Microsoft Visual Studio 2008 Service Pack 1 (KB2538241)

Eigentlich beheben Sie nur das, was am Patchday vom 13.04.2011 hätte behoben werden sollen. Ich habe dazu ja mehrere Artikel geschrieben (1, 2, 3, 4, 5). Nach Vergleich der entsprechenden Sourcen machen Sie Änderungen jetzt das, was sie sollen.

Auch das Problem, dass mit dem letzten Sicherheitsupdate, die Größe der Executables immens angewachsen ist wurde behoben.
Schade, dass dieses Problem in VS-2010 weiter bestehen bleibt.
Insbesondere funktioniert diese neue Runtime für VS-2005 auch auf Windows 2000. Eines der Hauptprobleme vom Patchday im April.

Anzumerken wäre hier eigentlich nur noch die neuen Versionen der Runtime die mit diesem Sicherheitsupdate veröffentlicht werden, d.h. auch, dass man nun die neuen Runtimes auch in sein Setup einbauen sollte, bzw. dass man die neueste passende VCRedist_x86 nun auch mit ausliefern muss. D.h. auch, dass sich die entsprechenden Manifeste wieder ändern, sofern man diese manuell angepasst hat:

  • Die neuen Runtimedateien von VS-2005 haben die Versionsnummer 8.0.50727.6195
    Die neue Runtime für VS-2005 gibt es hier zum Download.
  • Die neuen Runtimedateien von VS-2008 haben die Versionsnummer 9.0.30729.6161
    Die neue Runtime für VS-2008 gibt es hier zum Download.

Weitere und vollständige Infos zu diesem Sicherheitsupdate finden sich im VC-Blog:
http://blogs.msdn.com/b/vcblog/archive/2011/06/17/10175518.aspx

Ich jetzt kann nur allen Entwicklern raten diese neuen Sicherheitsupdates auch zu installieren und zu nutzen!

Probleme nach Upgrade auf Microsoft SQL Server 2008 R2

Ein Kollege von mir hat ein Upgrade von seinen Microsoft SQL Server 2008 auf einen Microsoft SQL Server 2008 R2 durchgeführt.

Soweit ist alles gut abgelaufen, der SQL Server läuft. Einzig die Reporting Services ließen sich nicht mehr starten. Die Reporting Services liefen allerdings zuvor ohne Probleme.

Eine Reparaturinstallation sowie Entfernen und erneutes Hinzufügen des Dienstes wurden ohne jedweden Erfolg durchgeführt.

Nach dem fehlgeschlagenem Start des Dienst fanden sich nur die drei folgenden Einträge im Event-Log:

Protokollname: Application
Quelle:        SQL Server Reporting Services (MSSQLSERVER)
Datum:         26.05.2011 14:56:09
Ereignis-ID:   0
Aufgabenkategorie:Keine
Ebene:         Fehler
Schlüsselwörter:Klassisch
Benutzer:      Nicht zutreffend
Computer:      xyz
Beschreibung:
Der Dienst kann nicht gestartet werden. System.Exception: Default appdomain failed to initialize.
bei Microsoft.ReportingServices.Library.ServiceAppDomainController.Start()
bei Microsoft.ReportingServices.Library.ReportService.OnStart(String[] args)
bei System.ServiceProcess.ServiceBase.ServiceQueuedMainCallback(Object state)
Protokollname: Application
Quelle:        Report Server Windows Service (MSSQLSERVER)
Datum:         26.05.2011 14:56:09
Ereignis-ID:   140
Aufgabenkategorie:Starten/Herunterfahren
Ebene:         Fehler
Schlüsselwörter:Klassisch
Benutzer:      Nicht zutreffend
Computer:      xyz
Beschreibung:
Fehler beim Initialisieren der DefaultDomain-Anwendungsdomäne. Fehler: Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt..

Protokollname: Application
Quelle:        Report Server Windows Service (MSSQLSERVER)
Datum:         26.05.2011 14:56:09
Ereignis-ID:   113
Aufgabenkategorie:Protokollierung
Ebene:         Fehler
Schlüsselwörter:Klassisch
Benutzer:      Nicht zutreffend
Computer:      xyz
Beschreibung:
Der Berichtsserver kann den Leistungsindikator 'Report Requests' nicht erstellen.

Der letzte Eintrag mit der Meldung „Der Berichtsserver kann den Leistungsindikator ‚Report Requests‘ nicht erstellen.“, in Verbindung mit der Meldung „System.Exception: Default appdomain failed to initialize.“ . Hat mich letzten Endes auf die Spur gebracht.

Die Lösung ist hier zu finden: http://support.microsoft.com/kb/956155/en-us

Offensichtlich wurde bei der Deinstallation des 2008er SQL-Servers mehr entfernt als gut war und der hier vorgeschlagene Weg einer Reparaturinstallation war leider nicht erfolgreich.
Ich habe die 4 Schlüssel die fehlten einfach manuell eingefügt und der Dienst konnte danach sofort wieder gestartet werden.

„C++ and Beyond“ und andere Seminare mit Scott Meyers und anderen

Ich habe den nachfolgenden Beitrag von Scott Meyers in meinem Blog erhalten und finde es wert ihm einen eigenen Platz in meinem Blog zu geben.
Hier der gesamte originale Kommentar wie er bei mir eingegangen ist:
http://blog.m-ri.de/index.php/2011/05/08/adc-c-konferenz-in-prien-am-chiemsee-ein-ruckblick/#comment-4369

Weil Du so viel Begeisterung für C++ und “Events,” die C++ angehen, hast, wollte ich das “C++ and Beyond” Veranstaltung in August in Banff (Kanada) erwähnen. Dort sprechen Herb Sutter, Andrei Alexandrescu und ich Themen wie das Speichermodell für C++0x, die Verwendung von GPGPUs und die Behandlung von grossen Mengen von Daten an. Anzahl der Teilnehmer ist zu 100 begrenzt. Vorträge finden in Englisch statt, aber Herb ist ein Deutschmuttersprachler, und ich versuche, Deutsch zu sprechen. Details der Veranstaltung sind unter http://cppandbeyond.com/ zu finden.

Etwas näher zu Hause sind die Seminare, die ich in Oktober mit meinem Partner QA Systems in Stuttgart halte. Ich gehe vier verschiedene Themen an, und zwar “Fastware” (schnelle Software), die effektive Nutzung von C++ im eingebetteten Bereich, Richtlinien für gute C++ Klassen und wie man die Kraft der C++-Templates zu Designmuster bringen kann. Drei von diesen werden auf Englisch durchgeführt, aber die zwei-tägige Präsentation über C++ im eingebetteten Bereich gebe ich — zum ersten Mal — auf Deutsch. Linke für die Vorträge sind auf http://www.aristeia.com/seminars.html zu finden.

Bitte um Entschuldigung, falls dieser Kommentar wie eine Werbung klingt, aber Tatsache ist, dass es immer noch interessante Veranstaltungen für C++ Entwickler gibt, auch in Deutschland und auch in Deutsch.

Kanada ist etwas weit, aber Stuttgart ist in Reichweite… mal sehen 😉

GetDefaultAccelerator der unbekannte Helfer

Ich bin Tastatur-Fan und ich achte in meinen Anwendungen immer darauf, dass ein Benutzer meine Anwendungen einfach mit der mit der Tastatur bedienen kann. Accelerator sind in Windows für den Entwickler hier ein einfaches Tool, Funktionen über die Tastatur einfach verfügbar zu machen.

Tastatur Acceleratoren werden in der MFC in CFrameWnd geladen und PreTranslateMessage angewendet. Üblicherweise passiert das Laden direkt wenn LoadFrame ausgeführt wird, oder wenn in einer MDI-Anwendung das Childframe mit dem entsprechenden Document-Template erzeugt wird.

Allerdings wird es spannend, wenn man den Accelerator wechseln will aufgrund verschiedener Ansichten oder Zustände in der Anwendung. Denn ein CMainFrame weiß ja eigentlich nichts von den Dokumententypen oder dessen Zustand. Und ich fände es eigentlich nicht schön entsprechenden Code imFrame zu verankern.

Zum Glück ist das auch nicht nötig, denn es gibt schon immer die zwei netten undokumentierten Funktionen CFrameWnd::GetDefaultAccelerator und CDocument::GetDefaultAccelerator.

Sobald eine Tastatureingabe in CFrameWnd::PreTranslateMessage ankommt wird CFrameWnd::GetDefaultAccelerator aufgerufen. Nur wenn diese Funktion NULL zurückgibt wird der im Frame gespeicherte m_hAccel verwendet. Nun und CFrameWnd::GetDefaultAccelerator ruft nun CDocument::GetDefaultAccelerator für ds aktuelle Dokument auf.
Damit kann nun das Dokument selbst über den Accelerator bestimmen, der verwendet werden soll. Wird NULL zurückgegeben wird der Accelerator des Frames verwendet.

In der MFC-Next ist das Verhalten etwas anders, weil dort der Keyboard Handler eingeschaltet ist, aber letzten Endes wird GetDefaultAccelerator auch von dort aufgerufen.

Draft für C++11 (vormals C++0x) jetzt noch kostenlos herunter laden

Michael Wong auf der ADC
Michael Wong auf der ADC in Prien

Auf der ADC für C++ in Prien hat uns Michael Wong (Intel, Mitglied des C++ Standard Commitee) in seinem Vortrag zum aktuellem Stand des C++ Standard geraten, den aktuellen Draft für C++11 (vormals C++0x) jetzt noch kostenlos herunter zu laden.

Dieser Draft wird sich gegenüber dem ISO Standard Dokument höchstens noch bzgl. Rechtschreibung und vermutlich einigen Formatierungen verändern. Inhaltliche Änderungen kann und wird es keine mehr geben. Wer nicht später hunderte von Dollars für das offizielle ISO Dokument ausgeben will kann das jetzige Dokument als PDF Datei noch kostenlos hier herunterladen.

Achtung: Dieser Link scheint sich schnell zu ändern
Ich versuche ihn aktuell zu halten. Auf dieser Seite finden alle entsprechenden Dokumente inkl. des aktuellen Drafts http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/
Es steht zu vermuten, dass nach Abschluß des Dokumentes alle Entwürfe entfernt werden.

Was macht man eigentlich gegen unerwünschte Übersetzung von Acceleratoren?

Accelerator sind ein einfach gutes Hilfsmittel um Funktionen in einem Programm über die Tastatur verfügbar zu machen.
Behandelt werden Accelerator in CFrameWnd::PreTranslateMessage. D.h. bevor eine Eingabe-Nachricht aus der Message Queue an ein Fenster ausgeliefert wird, bekommt das Fenster selbst und jedes Parent des Fensters die Nachricht zur Behandlung in PreTranslateMessage angeboten.

Aber das kann auch zu einem kleinen Problem werden. Nehmen wir an, wir haben eine Datenbank Anwendung und Bild-Hoch/Runter werden über Accelerator für das Blättern in den Datensätzen definiert. Soweit OK.

Was aber wenn man nun ein Inplace Control hat, oder eine kleine dynamische Listbox, die man einblendet und in der man nun auch blättern will? Dann wird der Accelerator im CFrameWnd zum Tastenschlucker und in dem Moment führen Bild-Hoch/Runter nicht zu dem gewünschten Blättern im Popupfenster.

Wie geht man nun vor?
Wenn man im View nun in PreTranslateMessage FALSE zurückgibt frisst der Accelerator im CFrameWnd die Taste. Gibt man TRUE zurück, dann wird die Nachricht nicht ausgeliefert. Der Trick ist eigentlich ganz simpel. Wenn alle PreTranslateMessage Funktionen FALSE zurückgeben wird die Nachricht ausgeliefert indem die beiden Funktionen TranslateMessage und DispatchMessage aufgrufen werden.
Das Ganze kann man abkürzen. Im PreTranslateMessage Handler des Fensters selbst  kann man nun einfach TranslateMessage und DispatchMessage aufrufen und nun TRUE zurückgeben. Das Fenster selbst oder evtl. sein Parent sorgt dafür, dass es die gewünschte Nachricht bekommt wie man es erwartet und der Accelerator im CFrameWnd wird umgangen.

_MSC_VER, _MFC_VER und die bisherigen Werte für die bekannten Compiler

Die Microsoft C/C++ Compiler haben schon immer eine durchgängige Versionsnummer, die mit den Produkten in denen sie eingebunden sind (z.B. VS-2010) nicht zu tun hat.

Diese Produktversion spiegelt auch auch in der vordefinierten Compiler Präprozessor Variable _MSC_VER wieder. Über diese Variable ist es zum Beispiel möglich verschiedene CRT oder STL Library Eigenarten abzufragen und entsprechen den eigenen Code für mehrere Compiler lauffähig zu machen. Gleiches gilt natürlich auch für den Code der MFC (siehe Anmerkung am Fuß der Tabelle).

Hier eine kleine Tabelle der Werte, die _MSC_VER für die verschiedenen Compiler annimmt mit ein paar zusätzlichen Hinweisen.

_MSC_VER  = Compiler
510  = C Compiler 5.1 (DOS)     - 1988?
       Mein aller erster Kontakt mit dem MS-Compiler
600  = C Compiler 6.x (DOS)     - 1990?
700  = C/C++ 7.0                - 1992
       Die UI war damals die PWB. MFC 1.0 wurde veröffentlicht
800  = Visual C++ 1.0           - 1993
       Existierte IMHO als 16bit und 32bit Compiler
900  = Visual C++ 2.0
       Existierte IMHO als 16bit und 32bit Compiler. MFC 3.0
1000 = Visual C++ 4.0           - 1995-03 ?
       Ab dieser Verison nur noch 32bit Compiler. MFC 4.0
???? = Visual C++ 4.1
       War nur für MSDN Subscriber verfügbar. Kam mit erstem Game/DirectX SDK.
???? = Visual C++ 4.2 und 4.21
       Erste Cross-Platform Version für Mac und PC. MFC 4.2, 4.21
1100 = Visual Studio 97 (5.0)   - 1997
       Enthielt weiterhin MFC 4.21
1200 = Visual C++ 6.0           - 1998-06
       Populärtse VC Version. MFC 6.0
1300 = Visual Studio.NET (2002) - 2002-02-13
       .NET hält Einzug. MFC 7.0
1310 = Visual Studio.NET 2003   - 2003-04-24
       MFC 7.1
1400 = Visual Studio 2005       - 2005-11-07
       MFC 8.0
1500 = Visual Studio 2008       - 2007-11-19
       MFC 9.0
1600 = Visual Studio 2010       - 2010-04-10
       MFC 10.0
1700 = Visual C++ 2011          - 2011 ???
       Produktname noch offen evtl. WinC++

Noch ein Hinweis auf _MFC_VER:
In der Tabelle habe ich nur die Versionen der zum Compiler passenden MFC Versionen aufgeführt. Die MFC setzt einen Define mit dem Namen _MFC_VER entsprechend. Dieser definiert ein WORD im Format 0xVVRR. Wobei VV für die Version der MFC steht in hexadezimaler schreibweise und RR für die Revision allerdings in dezimaler Schreibweise. Die aktuelle _MFC_VER von VS-2010 hat also den Wert 0x0A00 und in der MFC 4.21 wurde _MFC_VER als 0x0421 definiert.

PS: Auf die Idee für diese Tabelle kam ich durch eine Diskussion mit einem Teilnehmer auf der ADC für C++ in Prien, der auch schon x-Jahre mit dem MS-Compiler verbracht hat wie ich, und wir über die Entwicklung der Sprache C/C++ nachgedacht haben und was wann als Innovation unsere Programme veränderte. Eben ein typisches Bits+Bytes Gespräch… 😉