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.

Late Binding und schwache Performance durch GetIDsOfNames

Immer wieder sehe ich Entwickler, die über Late Binding COM Komponenten ansprechen. Das ist an sich nur zu unterstützen, denn DISPIDs können sich schnell mal ändern, wenn sich ein Interface ändert. Gerade bei den rasant an Verbreitung zunehmenden .NET Komponenten, die man mal schnell in die eigene Anwendung per COM einbindet, kann das Binding über die Interfaces und DISPIDs schnell zum Frust werden. Gleiches gilt für Office Komponenten, bei denen man sich nicht zwingend an ein Interface binden will. Zudem ist es der von Microsoft empfohlene Weg die Office-Automation zu benutzen (siehe auch hier).

OK! Also man nimmt Late Binding und verwendet z.B. die aktuellen Wrapper, die netterweise von der ATL zur Verfügung gestellt werden,  z.B. die netten Funktionen aus der CComDispatchDriver Klasse. GetPropertyByName, PutPropertyByName und auch die Funktionen Invoke0, Invoke1 und InvokeN haben entsprechende Überladungen, die die Verwendung von Funktions-/Eigenschaftsnamen direkt erlauben.

Der Nachteil liegt nicht gleich auf der Hand. Immer wenn solch eine Funktion aufgerufen wird, wird nicht durch der IDispatch::Involke ausgeführt, sondern auch ein Aufruf von IDispatch::GetIDsOfNames. Bei einem Out-Of-Process-Server kann dieser Roundtrip einiges an Performance kosten. Dabei ist es so einfach es besser zu machen.

Die Doku von IDispatch::GetIDsOfNames sagt folgendes:  

The member and parameter DISPIDs must remain constant for the lifetime of the object. This allows a client to obtain the DISPIDs once, and cache them for later use.

Und wer den oben genannten KB-Artikel aufmerksam gelesen hat, der hat auch was von DISPID-Caching mitbekommen.

Ich benutze gerne die die Wrapper, die in der MFC automatisch erzeugt werden, wenn man einen Type-Library über den Class View importiert. Hier werden die Funktionen auch in eine nette kleine COleDispatchDriver-Klasse verpackt und man bekommt netterweise ein gutes Exception Handling den Fehlerfall geliefert. Leider werden hier aber auch wieder nur DISPIDs verwendet.
Aber mit einem kleinen Trick, kann man diese Klassen genial einfach für Late Binding nutzen. Ich gehe wie folgt vor:

  • Ich importiere die Type-Library (tlb) mit dem Visual Studio Class View.
  • D.h. ich habe jetzt alle Wrapper mit DISPIDs, Was ich eigentlich für Late Binding vermeiden will.
  • Jetzt passe ich einfach die einzelnen Wrapper Funktionen in der folgenden Art und Weise an, ich ersetze die DISPID durch eine statische Variable:
void CMyWrapper::DoSomething(){
  static CDispIdHolder dispid(m_lpDispatch,L"DoSomething");
  InvokeHelper(dispid, DISPATCH_METHOD, VT_EMPTY, NULL, NULL);
}
  • Die kleine Klasse die ich hier verwende macht nun den Rest und besorgt die DISPID und cached sie damit weil die Variable statisch definiert ist:
class CDispIdHolder
{
public:
 CDispIdHolder(IDispatch *pDispatch,LPCOLESTR pName)
  : m_dispid(DISPID_UNKNOWN)
  {
   HRESULT hr = pDispatch->GetIDsOfNames(
         IID_NULL,
         &const_cast<LPOLESTR>(pName),
         1,
         LOCALE_SYSTEM_DEFAULT,
         &m_dispid);
   if (FAILED(hr))
    AfxThrowOleException(hr);
  }
  operator DISPID() { return m_dispid; }
private:
 DISPID m_dispid;
};

Die Exception, die man verwendet ist natürlich Implementierungsfrage.

❗ Der Performance Gain ist zum Teil beträchtlich, besonders wnen bestimmte Funktionen sehr oft aufgerufen werden müssen!

Anmerkung (für alle die es ganz genau nehmen):
Wenn man die Doku genau liest, heißt es natürlich hier, dass die Implementierung nur für die Lebenszeit des Objektes konstante DISPIDs garantiert. Wenn man allerdings bei Early Binding schon DISPIDs als konstant annimmt, ist meine Methode für Late Binding sicherlich vertretbar.

Verhindern des Flackerns von Controls wenn ein Fenster-Resize erfolgt

Immer wieder taucht in Foren die Frage aus, wie man das Flackern von Controls verhindern kann. Die Allgemeine Antwort heißt Doublebuffering, d.h. die Ausgabe wird zuerst auf einem nicht sichtbaren Memory DC durchgeführt und anschließend in einem Schlag auf den eigentlichen DC kopiert. Wichtig ist hier, dass auch der Hintergrund im eigentlichen WM_PAINT Handler mit gezeichnet wird und WM_ERASEBKGND gar nichts mehr macht. Der Code-Klassiker hierzu findet sich in Code-Project http://www.codeproject.com/KB/GDI/flickerfree.aspx

Anders liegt die Sache wenn es beim Resize eines Fensters flackert. Hier ist selten Doublebuffering eine Lösung. Meistens liegt hier das Problem darin, dass das Parent Fenster seinen Hintergrund neu zeichnet und anschließend alle Child-Windows auch neu gezeichnet werden müssen.

Aber auch hier ist Abhilfe einfach. Im Parent-Fenster wird einfach der Stil WS_CLIPCHILDREN gesetzt. Das sorgt dafür, dass das Parent Fenster einen DC bekommt bei dem die einzelnen Child-Windows ausgeclippt sind, und somit weder durch WM_ERASEBKGND noch durch den WM_PAINT Handler des Parents überschrieben werden.

Sollten sich die Child-Fenster überlappen müsste man zusätzlich an WS_CLIPSIBLINGS bei allen Kindfenstern als Stil denken (nicht beim Parent).

Produktvergleich der verschiedenen Visual Studio 2008 Editionen

In diesem Produktvergleich http://msdn2.microsoft.com/en-us/vstudio/products/cc149003.aspx kann man einfach herausfinden welche Komponenten mit den verschiedenen Visual Studio Editionen ausgeliefert werden.

Die häufigste Frage lautet immer wieder in den Foren:
Was benötige ich für eine Edition um ATL+MFC Programme zu schreiben?
Antwort: Die Visual Studio 2008 Standard Edition! Die Express-Edition enthält weder ATL noch MFC.

Anmerkung zur Installation des Visual C++ Feature Pack für 2008 Beta (MFCNext)

Die Beta Version des Feature Pack für Visual C++ 2008 ist ja seit einigen Tagen verfügbar.

In der Beschreibung auf dem Download Link steht zu lesen:

This Feature Pack Beta release may fail to install if you do not have a complete installation of Visual Studio 2008 on your system. To workaround this issue, ensure your original Visual Studio 2008 installation source (network path or DVD) is available when installing the Feature Pack.

Diese Anmerkung sollte man wortwörtlich nehmen.
Es sollte wirklich das komplette Visual Studio 2008 installiert sein. Das schließt auch die von mir nie installierten Komponenten Visual Basic und Crystal Reports ein. Diese Teile habe ich noch nie gemocht und auch normalerweise nie installiert. Aber in diesem Fall schlägt die Installation des Feature Packs fehl.

Im Installations Log sind dann folgende Einträge am Ende zu finden:

[1/10/2008, 13:21:13] (HotIron::CMspExternalUiHandler::UiHandler) Returning 0. INSTALLMESSAGE_RESOLVESOURCE [1: 0 2: vs_setup.msi 3: {80C06CCD-7D07-3DB6-86CD-B57B3F0614D8} 4: {80C06CCD-7D07-3DB6-86CD-B57B3F0614D8}; 5: 0 6: 1 7: 1 8: 0 ]
[1/10/2008, 13:21:13] (HotIron::CMspExternalUiHandler::UiHandlerRecord) Returning IDOK. INSTALLMESSAGE_ERROR [Error [1].An installation package for the product [2] cannot be found. Try the installation again using a valid copy of the installation package ‚[3]‘.: 1706Microsoft Visual Studio Team System 2008 Team Suite – ENU]
[1/10/2008, 13:21:13] (HotIron::CMspExternalUiHandler::UiHandler) Returning IDOK. INSTALLMESSAGE_ACTIONSTART [Action 14:21:13: Rollback. Rolling back action:]

Im Eventlog liest man:

 Protokollname: Application
Quelle:        MsiInstaller
Datum:         10.01.2008 14:21:58
Ereignis-ID:   1023
Aufgabenkategorie:Keine
Ebene:         Fehler
Schlüsselwörter:Klassisch
Benutzer:      MyDomain\Martin
Computer:      LAP-DEV.xyz.loc
Beschreibung:
Produkt: Microsoft Visual Studio Team System 2008 Team Suite – ENU – Update „Visual C++ 2008 Beta Feature Pack“ konnte nicht installiert werden. Fehlercode 1603. Weitere Informationen sind in der Protokolldatei C:\Users\Martin\AppData\Local\Temp\Visual C++ 2008 Beta Feature Pack – KB945273_20080110_131516000-Msi0.txt enthalten.

Protokollname: Application
Quelle:        MsiInstaller
Datum:         10.01.2008 14:21:13
Ereignis-ID:   11706
Aufgabenkategorie:Keine
Ebene:         Fehler
Schlüsselwörter:Klassisch
Benutzer:      MyDomain\Martin
Computer:      LAP-DEV.xyz.loc
Beschreibung:
Product: Microsoft Visual Studio Team System 2008 Team Suite – ENU — Error 1706.An installation package for the product Microsoft Visual Studio Team System 2008 Team Suite – ENU cannot be found. Try the installation again using a valid copy of the installation package ‚vs_setup.msi‘.
Ereignis-XML:

Ach ja, ehe ich es vergesse:
Etwas Geduld sollte man auch mitbringen. Die Installation dauerte auf meinem sonst rechte flotten Firmenlaptop über 35 Minuten!

Die Unsitte Objekte direkt in printf und Funktionen mit variabler Anzahl von Argumenten zu nutzen

Immer wieder sieht man Code wie diesen:

CString strText = _T("xyz"); 
_tprintf(_T("Irgendwas ist %s und jetzt kommt eine Zahl %d"), 
                           strText, 4711);

Sieht harmlos aus und funktioniert. Warum eigentlich?

Es funktioniert nur aus einem einzigen Grund:
CString ist 4 Bytes groß, besteht nur aus einem einzigen Element und das ist ein Zeiger auf einen TCHAR Array!

Das ganze funktioniert sofort nicht mehr wenn wir eine eigene CMyString Klasse ableiten, der wir eine virtuelle Funktion zuordnen. Was passiert nun?
Nun ist CString nicht mehr 4 Bytes groß sondern 8 Bytes und besteht aus zwei Zeigern. Einem Zeiger auf eine vtable und einen Zeiger auf einen TCHAR Array.

Nicht nur wird jetzt kein Text mehr ausgegeben, sondern auch die Zahl wird nicht korrekt ausgegeben! Das ganze obwohl CMyString immer noch einen Umwandlungsoperator für LPCTSTR hat.

Das Problem ist aber, dass _tprintf eine Variable Anzahl von Argumenten (Ellipsis) hat und der C/C++ Compiler gar nicht weiß, was _tprintf erwartet. Also wird das ganze Objekt auf den Stack geschoben.

Man macht eine kleine Änderung, und schon geht die Sache in die Hose. Das habe ich ja schon in dem Artikel Die Cx2y Falle beschrieben. Oder sollte also so etwas wie eine Änderung der Implementierung von CString erfolgen würde solch ein Programm auch sofort nicht mehr funktionieren. Man muss jedoch vermuten, dass Microsoft sich das gar nicht erlauben würde, weil zu viele Entwickler solchen nicht portablen und unabhängigen Code verwenden.

Wie macht man es richtig? Man verwendet den entsprechenden cast oder eine Funktion, die den entsprechenden Typ returniert.

CString strText = _T("xyz"); 
_tprintf(_T("Irgendwas ist %s und jetzt kommt eine Zahl %d"), 
                       static_cast<LPCTSTR>(strText), 4711); 
// -- oder -- 
_tprintf(_T("Irgendwas ist %s und jetzt kommt eine Zahl %d"), 
                       strText.GetString(), 4711);

Man sollte grundsätzlich bei der Verwendung von einer variablen Anzahl von Argumenten (Ellipsis) immer auf den Typ casten, der auch letzten Endes erwartet wird.

Das Visual C++ Feature Pack für 2008 ist als Beta-Version jetzt verfügbar (MFCNext)

Die Beta Version des Feature Packs für VC++ 2008 ist verfügbar.
Sie enthält die nächste MFC Version (MFCNext) und die Erweiterungen gemäß TR1!
Happy testing… 😉

Blog post:
http://blogs.msdn.com/vcblog/archive/2008/01/07/mfc-beta-now-available.aspx

Download link:
http://www.microsoft.com/downloads/details.aspx?FamilyId=D466226B-8DAB-445F-A7B4-448B326C48E7&displaylang=en

Dokumentation:
http://www.microsoft.com/downloads/details.aspx?FamilyId=0D805D4E-2DC2-47C7-8818-A9F59DE4CD9B&displaylang=en

Die Unsitte Tastatureingaben mit WM_KEYDOWN Nachrichten zu simulieren

Dies ist eine Ergänzung zu meinem Blog Eintrag Die Unsitte Windows interne Nachrichten zu versenden.
In einem der Kommentare wurde ich gefragt wie man es nun mit WM_KEY… Nachrichten richtig macht, weil ich auch diese Nachrichten als „intern“ definiert habe. In meinen Augen ist es unzulässig WM_KEY… Nachrichten per PostMessage oder SendMessage zu versenden. Also ist die Frage berechtigt:
Wie macht man es richtig, mit der Simulation von Tastatureingaben für andere Prozesse ❓

Ersteinmal möchte ich erklären, warum das Senden einer WM_KEY… Nachricht nicht funktioniert, oder oft genug nicht richtig funktioniert:

  1. Alleine wenn das Programm GetKeyState oder GetAsynchKeystate benutzt um zu ermitteln ob gleichzeitig sie Strg- oder die Umschalttaste gedrückt wird, dann funktioniert SendMessage/PostMessage nicht mehr.
  2. Wenn das fremde Programm Accelerator verwendet, dann wird hier auch durch senden einer WM_KEY… Nachricht der Accelerator oft genug nicht ausgelöst. Die Folge: Der gewünschte Effekt bleibt aus.
  3. Man bekommt schnell Probleme mit synthetisierten Zeichen, die zwei Tasteneingaben erfordern, z.B. ^ und a, für â.
  4. Man muss fürchterlich darauf achten, wenn ein Befehl den Fokus wechselt.
  5. Die Applikation hat nicht den Fokus! Alleine das kann schon Fehlverhalten auslösen, denn normalerweise kann nur eine aktive Applikation mit Fokus Tastatur- und Mauseingaben erhalten.
  6. WM_SYSKEY… Nachrichten werden nicht korrekt erzeugt.

Wie macht man es nun ❓  
Es ist ganz einfach: Man benutzt SendInput ❗  
Der Vollständigkeit halber sei hier noch die legacy Funktion keybd_event erwähnt.

SendInput reiht die Eingaben ein in die Eingabequeue für das gesamte Windows System ein und liefert diese an die aktive Applikation aus und an das Fenster, das den aktuellen Eingabefokus hat. Man kann also nicht einfach so auch das Zielfenster angeben, dass die Eingaben erhalten soll. Das merkt man schon daran, dass es kein Fensterhandle gibt, dass man als Ziel angeben könnte für SendInput.

Frage aber nun: Wie gewährleistet man, dass das richtige Programm die Tastaturnachricht bekommt  ❓

Auch hier ist die Antwort relativ einfach! Zwei Dinge sind dazu nötig:

  1. Dass Zielfenster, bzw. den Thread in dem das Zielfenster muss ausgewählt werden, damit er diese Eingaben auch erhalten darf. Das wird durch AttachThreadInput erreicht. Denn wir wollen die Daten ja nicht an unsere Applikation senden. ❗ Bitte hinterher nicht vergessen AttatchThreadInput mit FALSE aufzurufen und den Thread wieder zu detachen.
  2. Man muss den Fokus korrekt setzten für das Fenster, dass die Eingaben erhalten soll!
    Ist der Zielthread jetzt an die Eingabequeue angeschlossen, kann man ohne Probleme SetFocus ausführen (was ohne AttachThreadInput normalerweise auch nicht möglich wäre).
  3. Wird jetzt SendInput ausgeführt, werden alle Nachrichten korrekt erzeugt und auch alle Commands entsprechend der Tastaturfolge ausgelöst. Auch spezielle Umlaute und Unicode Zeichne lassen sich so erzeugen.

Es ist gar nicht so schwer es richtig zu machen. :mrgreen:

Die Unsitte Windows interne Nachrichten zu versenden

Neulich im http://www.c-plusplus.de/forum/ fand sich folgende Empfehlung:

XYZ schrieb:
Schreibe knapp vor der MAIN MESSAGE LOOP:
C/C++ Code:

PostMessage(DeinHandle,WM_CREATE,NULL,NULL); //oder so ähnlich vielleicht noch mit Instanz
while //MAIN MESSAGE LOOP

Ich staunte nicht schlecht. Grund für diesen Holzhammer war, dass „angeblich“ die Nachricht WM_CREATE, von Windows nicht verschickt wird. Also helfen wir einfach etwas nach und versenden diese selber.
Das sich dieser Code verbietet ist klar und ich will hier gar nicht weiter darauf eingehen warum.

Aber es bringt mich zum Thema:
Es ist eine beliebte Unsitte Windows Nachrichten, die nur für das interne Zusammenspiel der Komponenten und als „Benachrichtigungen“ dienen, selber zu versenden.
Favoriten hier sind oft WM_SETFOCUS, WM_PAINT, WM_SIZE, WM_KEYDOWN und manche andere.

Und Windows macht es dem Programmierer nicht einfach. In den Anfangszeiten von Windows wurde zu wenig bei der Namensgebung darauf geachtet, welche Nachrichten mehr oder weniger Aktionen auslösen und Eigenschaften setzen (z.B. WM_SETFONT) und welche Nachrichten als Notifications des OS dienen einem Programm mitzuteilen, dass etwas passiert ist, oder geschehen soll (z.B. WM_CREATE, WM_PAINT, WM_SETFOCUS, WM_SIZE, WM_ACTIVATE etc.).
Vieles wäre einfacher, wenn man Anhand des Nachrichtenamens erkennen würde, dass diese Nachricht vom OS versendet wird und damit nicht für den Eigenbedarf bestimmt ist.
Die letzte Gruppe der Nachrichten steht hier oft in einem direkten Zusammenhang mit einer API-Funktion (CreateWindow, UpdateWindow, SetFocus, SetWindowPos, SetActivteWindow etc.). Mit in diese Kategorie fällt, der Versuch mit WM_KEY… Nachrichten Tastatureingaben zu simulieren was auch nur in Ausnahmefällen korrekt funktioniert.

Mit der Einführung der neuen Common Controls, wurde hier nachgebessert. Sicherlich auch weil WM_COMMAND als Benachrichtigungs Medium etwas schmalbrüstig ist. WM_NOTIFY wurde eingeführt
Dieser Schritt war gut.  Interessanter Weise habe ich noch niemals den Versuch gesehen solche Notifications zu simulieren. Obwohl dies hier auch kein Problem wäre, denn es gibt hier keine API Funktionen, die Konfliktpotential anbieten würden.

Häufigster Grund solch einer Versuchung nachzugeben eine Nachricht selbst zu versenden, ist oft einfach Unwissen über die Windows-API. Und Eingangs erwähnte ich es schon: Die Dokumentation ist oft genug nicht eindeutig und macht zu selten auch Hinweise auf die entsprechenden verbundenen API Funktionen, siehe Doku zu WM_SETFOCUS in der wir nichts zu SetFocus lesen, außer in der Fußnote für See also. Erst die Doku der SetFocus API-Funktion gibt Aufschluss, dass diese Nachricht durch SetFocus versendet wirdund damit in gewisser Weise intern ist. Windows führt Buch welches Fenster den Fokus hat, aber dieser wird eben durch SetFocus gesetzt und nicht durch WM_SETFOCUS!

Fazit: Verurteilen kann man diese Unsitte schwer, außer mit dem Hinweis: „Es wurde nicht korrekt in der Doku gelesen, RTFM (Read the fine MSDN)“. Allerdings ist für Anfänger die MSDN oft genug einfach nur mehr erschlagend als informativ.
Dennoch kommt man nicht umhin sorgfältig auf die Zusammenhänge von Nachrichten und API-Funktionen zu studieren. Das bedeutet in diesem Zusammenhang, gerade die Dokumentationen der Funktionen, Nachrichten und Methoden zu lesen, die in der Fußnote unter See also aufgeführt werden. Oft genug findet man auf diesem Weg die Zusammenhänge (oft etwas mühsam) heraus.

Anmerkung: Die Dokumentation der MFC mach das Ganze leider noch schlimmer, denn hier werden nur kurze Auszüge der Windows-API wiedergegeben, die leider oft genug nicht detailliert genug sind. Hier ist immer angeraten, hinter einer virtuellen On… Funktion einen Windows Nachrichten Handler zu vermuten und in der Windows API noch einmal genauer nachzulesen. Allerdings macht sie auch einiges wiederklarer, denn sie kapselt die WM_… Nachrichten, die als Methoden/Getter/Setter funktionieren direkt in eigenständigen Memberfunktionen. Aber auch hier empfiehlt es sich nachzusehen was unter See also steht.