Arithmetik mit GetTickCount

Immer wieder sehe ich Code um GetTickCount oder Aussagen, die sich um den Überlauf von GetTickCount drehen, der alle 49,7 Tage entsteht. Muss man also irgendwelche Vorkehrungen treffen, wenn man Zeitdifferenzen errechnen will?

Eigentlich muss man sich gar nicht darum kümmern, sofern man in 49,7 Tagen eben mindestens einmal diese Differenz errechnet und das damit dafür gesorgt ist, dass diese Differenz nicht größer sein kann als eben besagt 49,7 Tage.

DWORD dwStart=::GetTickCount(); 
while (::GetTickCount()-dwStart < dwTimeout) 
   DoSomething();

Die Arithmetik über unsigned Integer macht es an dieser Stelle möglich. Dieser Code funktioniert, solange eben DoSomething nicht länger als 49,7 Tage dauert. Das ist die einzige Bedingung!

Leider schweigt sich die normale Windows SDK Doku zu GetTickCount darüber aus.
http://msdn2.microsoft.com/en-us/library/ms724408.aspx

Eigentlich problematisch an dieser Stelle, ist es nicht Differenzen zu bilden sondern Vergleiche durchzuführen ❗

Einen interessanten Zusatz dazu findet sich jedoch in der Windows CE Doku. Warum dieser Zusatz nicht auch in der normalen SDK Doku steht ist eigentümlich. Im Allgemeinen empfinde ich die CE-Doku in der MSDN immer eher als lästig.
Es ist aber eben wahrscheinlicher, dass dein CE Rechner und dementsprechend Programme die darauf laufen, länger als 49,7 Tage laufen als auf einem Desktop, ohne einen Reboot, alleine schon wegen der Security Updates, so kann man sich diesen Umstand zumindest erklären.
http://msdn2.microsoft.com/en-us/library/ms885645.aspx

When using GetTickCount, subtraction is safe but comparisons such as

if (GetTickCount() > MyTickCount)

are not. You can use the GetTickCount function to time the duration of an activity as shown in the example below, but using GetTickCount for any other operation will cause issues.

wOldTime = GetTickCount(); 
DoSomething(); 
dwTimeElapsed = GetTickCount() – dwOldTime;

Gleiches findet sich in der Windows Mobile 6 Doku noch etwas besser
http://msdn2.microsoft.com/en-us/library/aa915056.aspx

When using GetTickCount, subtraction is safe, even if the rollover occurred, and subtraction always yields the correct difference and the number of clock ticks passed between two tick values. Comparing tick values directly does not always yield the correct results; only compare the differences. Be sure that your code can service the difference before the second rollover, that is, before another 49.7 days pass. Comparisons such as the following are not safe:

#define DELTA_TICKS sample_tick_value 
// initialized somewhere in the code 
DWORD dwStartTick = GetTickCount(); 
DWORD dwEndTick =   GetTickCount() + DELTA_TICKS;     

// The following function fails on a rollover. 
BOOL no_compare_tick_difference() 
{ 
  if ( GetTickCount() > dwEndTick ) 
    return ( TRUE); 
  return (FALSE); 
}

The following code shows how to properly use GetTickCount by comparing tick differences. This code handles the rollover situation.

BOOL compare_tick_difference() 
{ 
  if ( (GetTickCount() – dwStartTick) > DELTA_TICKS) 
    return ( TRUE); 
  return (FALSE); 
}

C++ Community Event in Bad Homburg mit Microsoft Program Managern

Bei Microsoft passiert wieder mehr zum Thema C++.
Nachdem sich Dariusz Parys schon über volle C++ Vorträge bei dem Launch in Frankfurt wunderte und er nach Interessenten gefragt hat, die an weiteren lokalen Events für C++ interessiert sind tut sich nun was!

Am 17. April bietet Microsoft für die C++-Entwickler-Gemeinschaft eine dreistündige Veranstaltung zum Thema C++ in Visual Studio 2008 und dem VS2008 Feature Pack für C++ in Frankfurt/Main an.

Es wurden zwei Program Manager aus der Product Group eingeladen und sie einiges zum Thema C++ vortragen:

  1. What’s new in Visual Studio 2008
    • walkthrough new C++ features
    • VSTS for C++ developers
  2. A deeper look into the VS2008 Feature Pack for C++
    • demo of improvements to MFC
    • the value of TR1
  3. A brief glimpse into the future of Visual C++
    • highlights from what we’re building for the next version of C++

Termin & Ort (Update 31.03.2008):

  • Datum: 17.4.2008, 16 – 19 Uhr

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

Die Teilnahme an der Veranstaltung ist kostenfrei ❗

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

Anmerkung (31.03.2008):
Der ursprüngliche Veranstalltungsort war Frankfurt. Wurde am 31.03. endgültig auf Bad Homburg festgelegt!

LoadIcon macht es nicht richtig…

… oder sollte man sagen: Nicht so wie erwartet. 

In einer der Micosoft Newsgroup fand sich eine Anfrage, die sich um folgenden Code drehte:

HICON m_hIcon[2]; 
[...] 
m_hIcon[0] = AfxGetApp()->LoadIcon(IDR_MAINFRAME); 
m_hIcon[1] = AfxGetApp()->LoadIcon(IDR_SMALL); 
[...] 
SetIcon(m_hIcon[0], TRUE);   // Großes Symbol verwenden 
SetIcon(m_hIcon[1], FALSE);  // Kleines Symbol verwenden

Das Problem des Posters war, dass er sich Mühe mit dem Layout eine kleines Icons gegeben hat, aber leider immer nur ein verkleinertes 32×32 Icon verwendet wurde.

Das Problem liegt an der Verwendung von LoadIcon! LoadIcon lädt immer das Standard-Format 32×32. Bzw. in dem Format, dass durch das System als Standard-Icon-Größe vorgegeben wird.

Um es richtig zu machen, muss man das Icon wirklich in der gewünschten Größe laden. Dazu verwendet man die Funktion LoadImage. Eine kleine Helper Routine LoadIconEx kann dann wie folgt aussehen:

HICON LoadIconEx(PCTSTR pszIconName, bool bLargeIcon) 
{ 
 HINSTANCE hInstance = ::AfxFindResourceHandle(pszIconName,RT_GROUP_ICON); 
 return reinterpret_cast<HICON>(::LoadImage( 
       hInstance, 
       pszIconName, 
       IMAGE_ICON, 
       GetSystemMetrics(bLargeIcon ? SM_CXICON : SM_CXSMICON), 
       GetSystemMetrics(bLargeIcon ? SM_CYICON : SM_CYSMICON), 
       0)); 
}

External Tools in VS-2005 IDE begrenzt die Argumente auf 251 Zeichen

Wenn man sich ein eigenes Tool baut, dann kann man Überraschungen erleben, wenn nicht das passiert was man möchte.

Auffällig ist schon, dass der Eingabebereich nicht sonderlich lange Befehlszeilen zulässt. Bei 251 Zeichen ist Schluss. Aber wirkliche Überraschungen erlebt man, wenn man Environment-Variablen oder die schönen vordefinierten Makros für den aktuellen Projektpfad verwendet. Wird hier eine etwas komplexere wirklich lange Befehlszeile aufgebaut, dann ist das Ergebnis oft genug zufällig.

Das Problem ist, dass alle Argumente der Befehlszeile auch nach dem Expandieren der Makros eine Länge von 251 Zeichen nicht überschreiten. Der Rest wird einfach abgeschnitten!

So werden z.B. Dateien kopiert, aber nichtdahin wo man sie hin haben wollte.

Good News: Diesen Bug hat man in VS-2008 gefixt. Die Eingabezeile für Argumente ist zwar immer noch begrenzt, aber Makros werden jetzt korrekt expandiert und die entsprechende Befehlszeile darf jetzt länger werden.

VS-Tipps & Tricks: Downgrade für VC-200x Projekte

Kann man ein auf VC-2005 erstelltes Projekt einfach auch unter VC-2003 builden?
Oder ein 2008er Projekt auf VS-2005 laden?

Der Mühsame weg, ist es, das Projekt neu aufzubauen. D.h. ein leeres Projekt anzulegen und die entsprechenden Dateien aus dem alten Projekt in das neue Projekt in der niedrigeren Version aufzunehmen.
Es geht etwas leichter mit einem kleinen Hack ❗

Nehmen wir das Beispiel eines Downgrades von VS-2008 auf VS-2005.

  • Man kopiert einfach die entsprechende VCPROJ Datei unter einen neuen Namen
  • Man öffnet diese Datei mit dem Editor seiner Wahl
  • Man schaut in die Datei und findet den folgenden XML Code

<?xml version="1.0" encoding="Windows-1252"?>
 <VisualStudioProject
  ProjectType="Visual C++"
  Version="9,00"

Alles was man nun ändern muss ist die Zeile 4 mit dem Version Eintrag.
Wie ändern diesen Eintrag auf

 Version="8,00"

für VS-2005. Will man zurück auf die Version VS-2003 dann muss der neue Wert

 Version="7.10"

heißen.

Und schon sind wir fertig. Man glaubt es kaum 🙂

Klar ist, dass natürlich neue Features, wie z.B. Eigenschaften des Manifest Compilers in der vorher gehenden Version, die das z.B. gar nicht kennt untergehen. Diese Dateien bleiben in der Projektdatei, aber werden Dank XML einfach in der alten VS Version ignoriert.

Hotfix für GDI Leaks unter Windows XP-SP2/2003 Server in MFC Applikationen

Unter XP SP2 und Windows 2003 Server kann es bei eingeschalteten Themes zu GDI-Leaks kommen. Speziell wird hier auf MFC Anwendungen hingewiesen (siehe dazu meine Anmerkung unten).

Die Beschreibung und der Download-Link für den Hotfix finden sich hier:
MFC applications leak GDI objects on computers that are running Windows Server 2003 or Windows XP

Anmerkung:

Dies ist ein Hotfix für Theme Handler unter Windows 2003 und Windows XP SP2. Er greift nicht in die MFC ein ❗
Man muss also eigentlich davon ausgehen, dass der Bug im Windows Themes Kern steckt und nichts mit der MFC zu tun hat, sondern eher damit zu tun hat wie in der MFC mit Windows Ressourcen umgegangen wird.
Den Effekt bekommt man ziemlich einfach hin, indem man mit dem Wizard eine MFC-MDI Applikation anlegt. Dann einfach Strg+N festhalten und alle Fenster wieder schließen mit Strg+F4, danach findet man einige Hundert GDI Objekte ausgewiesen im Task-Manager, die vorher nicht da waren.
Ich habe einen Test mit dem MDI-Sample aus dem Petzold gemacht und dieses zeigt diesen Effekt nicht. Evtl. liegt es auch einfach nur an dem Umgang mit den Toolbars. Genaueres konnte ich nicht herausbekommen. 

  • Erstaunlich 1.: Dieser Patch ist mit 2 Jahren doch relativ alt (März 2006) und er ist mir erst durch eine Diskussion in einer Produkt-Gruppe über den Weg gelaufen …
  • Erstaunlich 2.: Dieser Patch wird nicht durch den Windows Update Service installiert, obwohl er in meinen Augen da rein gehört ❗
    Ob nun der Update Service nun den primären Fokus auf Sicherheit hat oder nicht. Ich empfinde dieses Verhalten des Theme Handlers als kritisch!

SYSTEM_FONT, DEFAULT_GUI_FONT und der Font der eigentlich benutzt wird…

Was sind eigentlich  die beiden Fonts SYSTEM_FONT und DEFAULT_GUI_FONT, die durch GetStockObject zurückgegeben werden?
Sind sie ein schneller Weg um an den „MS Shell Dlg“ bzw. „MS Shell Dlg 2“?

Es ist ein Irrtum wenn man glaubt man bekommt den Standard UI Font mit DEFAULT_GUI_FONT oder SYSTEM_FONT. Dieser Irrtum basiert auf der Annahme es gebe nur einen Font den die Windows UI verwendet!

Der richtige Weg vorab: Der einzige korrekte Weg einen richtigen Font für die Default UI zu erhalten ist SystemParametersInfo mit SPI_GETNONCLIENTMETRICS. Bzw., in Dialogen sollte immer als Font „MS Shell Dlg“ bzw. „MS Shell Dlg 2“ verwendet werden (siehe auch The old new thing). Man beachte den Plural ❗

Und was sind diese beiden Fonts nun?
Es sind Relikte aus alten Tagen, die kein Mensch und vor allem kein Windows Entwickler bei Microsoft mehr verwendet.  (Anmerkung des Autors: Ich habe zumindest aus zuverlässiger Quelle gehört, die Entwickler bei Microsoft wären immer noch Menschen 😉 )

  • In alten Tagen von Windows 2.0 wurden SYSTEM_FONT für die Dialoge verwendet. Dieser Font hieß auch System. Man glaubt es kaum, dieser Font ist immer noch der Standard-Dialog-Font, den aber wirklich keiner mehr will. Es ist ein Bitmap Font und er kann nicht mit Anti-Aliasing verwendet werden.
  • Die Geschichte von DEFAULT_GUI_FONT ist weniger spektakulär. Er wurde mit Windows 95  eingeführt und man dachte damit einen Standard gesetzt zu haben. Aber das wurde noch im selben Release zugunsten von SystemParametersInfo  fallen gelassen. Es ist geblieben als weiteres Relikt aus alten Tagen.

Alle Fonts die GetStockObject liefert sind Bitmap Fonts, die auch kein ClearType unterstützen.

Siehe auch:
What are SYSTEM_FONT and DEFAULT_GUI_FONT?
What about logical fonts?
DEFAULT_GUI_FONT really stinks

AfxBeginThread versus _beginthreadex

Es hat sich ja mittlerweile schon herumgesprochen, dass man _beginthread(ex) anstatt CreateThread verwenden sollte, wenn man die CRT verwendet. Die Frage ist wie steht es nun mit _beginthread(ex)  und AfxBeginThread wenn man die MFC verwendet?

Es ist ähnlich wie bei der CRT, es gibt auch für die MFC einen Thread State, der im Thread Local Storage abgelegt wird.  Zu diesem Thread Local Storage gehören z.B. die temporären Maps für die Fensterverwaltung und Maps für GDI-Objekt Verwaltung. Auch für Tooltips und diverse OLE Funktionen werden in diesem Module Thread State Daten abgelegt.

Weiterhin werden einige Hooks gesetzt, die notwendig werden, wenn GUI verwendet wird. Dito Aufräumarbeiten, falls COM verwendet wird… (AfxOleInit)
Dieser Thread Local Storage wird normalerweise beim Zerstören des assoziierten CWinThread wieder freigegeben. Wird die Threadfunktion verlassen oder AfxEndThread aufgerufen, dann wird der Thread Module State mit dem entsprechenden Speicher freigegeben.

Was passiert, wenn man in einem MFC Programm einen Thread mit _beginthreadex startet und anschließend MFC Funktionen verwendet und dann mit _endthread den Thread terminiert?
Was passiert, wenn man mit AfxBeginThread einen Thread startet und ihn mit _endthread terminiert ❓

❗ Dann entsteht ein Leak, und je nach verwendeten Komponenten kein kleines ❗

Sollte die MFC Applikation also dynamisch Threads erzeugen, weil damit zum Beispiel Sockets überwacht werden oder andere nette Workerthreads bedient werden, dann kann man relativ  schnell unerfreuliches erleben.

Es gilt also 2 Dinge zu beachten:

  1. In einem MFC Programm sollte man immer AfxBeginThread verwenden! Man geht sonst das Risiko ein, dass bei der Verwendung der ersten MFC Funktion (AfxGetResourceHandle o.ä.) ein entsprechender Speicherblock angelegt wird und eben sonst nicht mehr freigegeben wird..
  2. Und beim Verlassen einer Threadfunktion kümmert man sich am Besten gar nicht darum, wie der Thread gestartet wurde :mrgreen: . Man muss explizit AfxEndThread gar nicht aufrufen. Man sollte einfach alle Threadfunktionen einfach durch return verlassen. Der Thread wird dann entsprechend der Funktion, die man beim Start gewählt hat auch terminiert. Dann bleibt – auch bei einer Änderung der Umgebung , ob nun mit oder ohne MFC. 

Anmerkung:
Warum werden diese Leaks nicht in der Debugausgabe angezeigt? Ganz einfach, weil diese Allokationen durch die MFC nicht getrackt werden. Das Speichertracking der Debugversion wird explizit für diese Allokationen ausgeschaltet.

Warum man manchmal AFX_MANAGE_STATE auch in seiner eigenen EXE aufrufen muss

AFX_MANAGE_STATE ist jedem bekannt, der mit DLLs hantiert. Es garantiert bei Verwendung der MFC DLLs, dass die entsprechenden Ressourcen bei den verschiedenen Ladeoperationen, wie z.B. CString::LoadString gefunden werden.

Warum ❓
AfxSetResourceHandle und AfxGetResourceHandle bedienen, eine globale Variable, die in der MFCn.DLL liegt.
Genau genommen ist es keine prozessglobale Variable, dies wird klar wenn man an Threads denkt. Die Variable wird threadlokal gespeichert.  Die HINSTANCE für AfxGetResourceHandle liegt in einer Struktur, die AFX_MODULE_STATE heißt. In dieser Struktur werden noch einige andere wichtige threadlokale Daten gespeichert. Das sind z.B. für die Interaktion mit der Managed-World, der Activation Context. Einen Blick auf den Inhalt dieser Struktur zu werfen lohnt sich.
Durch diese Struktur AFX_MOUDLE_STATE hat das Makro AFX_MANAGE_STATE seinen Namen.

Gesetzt den Fall wir haben EXE und DLLs (egal ob Standard oder Extensions DLLs), die alle die MFC DLLs dynamisch binden, dann wird AFX_MANAGE_STATE wichtig. Ruft eine EXE also eine Funktion aus einer DLL auf, hat natürlich die EXE das entsprechende Handle an AfxSetResourceHandle übergeben. Damit nun seinerseits die DLL eigene Ressourcen laden kann, wird durch AFX_MANAGE_STATE die alte AFX_MODULE_STATE Struktur gesichert (genau genommen der Zeiger darauf), und ein Zeiger auf die neue aktuelle AFX_MODULE_STATE Struktur gesetzt. CString::LoadString und CDialog::DoModal finden nun die richtigen Ressourcen in der DLL.
Der Destruktor sorgt nun am Ende der Funktion, dass der alte Zeiger der ursprünglichen AFX_MODULE_STATE Struktur zurückgesetzt wird, auf den Wert vor dem Aufruf.

Soweit mal die Theorie 🙂
Was passiert aber nun unter den folgenden Gegebenheiten:

  • Eine DLL ruft einen Dialog oder eine MessageBox auf. In der EXE existieren Fenster, die einen Timer gesetzt haben?
  • Oder eine DLL ruft einen Dialog auf, und die EXE hat einige COM-Objekte veröffentlicht, die nun von extern angesprochen werden können.
  • Die DLL ruft über einen Mechanismus eine Callback Funktion in der EXE auf.

Das Alles ist kein Problem, solange nicht ihrerseits die Funktionen aus der EXE auf die Idee kommen eine Ressource zu laden. Was würde dann passieren?

Klar, die DLL hat mit AFX_MANAGE_STATE den AFX_MODULE_STATE umgesetzt. Würde die EXE in der Callback-Funktion oder Timer-Funktion nun selbst auf die Idee kommen einen Dialog zu laden oder nur einfach CString::LoadString auszuführen, dann wird evtl. ein String oder Dialog geladen, aber vermutlich nicht der, den man erwartet.
Verwendet man nun Formatierungsfunktionen, wie z.B. Format, wird man manchmal böse Wunder erleben. Oder man lädt einen Dialog mit DoModal. Wenn man Glück hat ist der Dialog mit dieser ID nicht da und DoModal schlägt fehl. Wenn man Pech hat wird der Dialog geladen, aber die entsprechenden Controls die gebunden werden oder mit GetDlgItem gesucht werden sind nicht vorhanden. Und da die meisten Entwickler keine Prüfung auf NULL durchführen (z.B. hier bei GetDlgItem(IDC_MYITEM)->EnableWindow(FALSE)) kracht es an den absonderlichsten Stellen.
Auch in der EXE ist man gut beraten in OnTimer Handlern AFX_MANAGE_STATE zu verwenden, wenn der Timer auch Ressourcen verwendet. Gleiches gilt in COM Interfaces oder IDispatch Interfaces. Glücklicherweise sorgen hier die Wizards für korrektes Verhalten.

Mehr noch: Unglücklicherweise kann theoretisch jeder Windows Handler zu diesem Problem führen, wenn diese Fensterfunktion direkt über die modale Nachrichtenschleife aus einer DLL aufgerufen wird.
Und auch bei mancher Funktion, die als Callback aus einer DLL verwendet wird, kann ein zusätzliches AFX_MANAGE_STATE nicht schaden.

VS-Tipps & Tricks: Benötigt man eigentlich noch DEF Dateien?

Das was eine DEF Datei tut, benötigt man oft genug noch. Stellen wir die Frage mal etwas anders:
Kann man den Inhalt einer DEF Datei auch wo anders unterbringen?

Ja man kann sich eine DEF Datei sparen. Auch der Linker kennt entsprechende Optionen auf der Befehlszeile, die das gleiche tun, was eben eine DEF Datei macht.

Wer schon mal ATL Support zu einem DLL Projekt hinzugefügt hat, oder eine ATL DLL mit VS-2005/2008 angelegt hat, der wird feststellen, dass es gar keine DEF Datei mehr gibt, aber dennoch Funktionen wie DllCanUnloadNow exportiert werden.

Schaut man sich den Code an, der erzeugt wird, dann sieht man einen netten Block von pragmas.

#pragma comment(linker, 
        "/EXPORT:DllCanUnloadNow=_DllCanUnloadNow@0,PRIVATE") 
#pragma comment(linker, 
        "/EXPORT:DllGetClassObject=_DllGetClassObject@12,PRIVATE") 
#pragma comment(linker, 
        "/EXPORT:DllRegisterServer=_DllRegisterServer@0,PRIVATE") 
#pragma comment(linker, 
        "/EXPORT:DllUnregisterServer=_DllUnregisterServer@0,PRIVATE")

Die Ähnlichkeit zur DEF Datei ist frappierend, was aber auch wieder nicht wundert.
Nett ist auch, dass man sich die declspec(dllexport) Spielereien sparen kann. Alles macht hier einfach der Linker ❗

Das eigentliche Problem an dieser Syntax ist, dass man auch den intern gemangelten Namen kennen muss. Es enthebt den Programmierer nicht die Funktion auch entsprechend korrekt zu deklarieren.

#pragma comment(linker, 
        "/EXPORT:ExportedFunction=_ExportedFunction@0") 
extern "C" BOOL __stdcall ExportedFunction() 
...

Der einzige kleine Trick steckt hinter der Frage: Wie kommt man an den gemangelten Namen der Funktion?
Aber auch das ist nicht schwer. Funktion gewünscht deklarieren und definieren, z.B.:

__declspec(dllexport) BOOL __stdcall ExportedFunction2(const char *) 
{ 
 return FALSE; 
}

Dann mit Depends, den exportierten Namen abgreifen (rechte Maustaste Copy Function Name).
Dann einfach die Funktion final übernehmen.

#pragma comment(linker, 
        "/EXPORT:ExportedFunction2=?ExportedFunction2@@YGHPBD@Z") 
BOOL __stdcall ExportedFunction2(const char *) 
{ 
 return FALSE; 
}

Netter Effekt: Auf diesem Weg kann man auch eine Funktion leicht unter zwei Namen exportieren…