Wie man sich mit CComDispatchDriver bzw. CComPtr<IDispatch>::InvokeN hereinlegen kann

Eigentlich müsste dieser Artikel eine weitere Überschrift bekommen:
Wie fatal es ist, dass es keine vollständige ATL Dokumentation gibt!

Einige werden CComDispatchDriver kennen. Seit den VS-200x Versionen ist diese Klasse nichts anderes als sie Spezialisierung CComPtr<IDispatch>

Ich hatte vor, eine bestimmte .NET Komponente per late binding in ein C++ Programm zu integrieren.
Kein Problem. CreateInstance, hier ein Invoke0, dort ein Invoke2 und da noch ein InvokeN 😮 … und hier geht auf einmal nichts mehr. Dokumentation in der MSDN: Fehlanzeige!

Bestandsaufnahme: Ich habe einen CComVariant Array aufgebaut und die entsprechenden Daten übergeben. Sieht alles richtig aus. Aber ich erhalte immer nur Fehler (immer E_NOINTERFACE).

  1. OK, nächster Schritt. Das ganze mal direkt mit dem entsprechenden Interface versuchen. Also CComQIPtr eingebaut und direkter Aufruf über die existierende duale Schnittstelle. Komisch, nun geht es.
  2. Nun denn. Das ganze nun mal als Klasse mit dem Classview importiert und einen MFC Wrapper gebaut. Der verwendet ja auch IDispatch. Jetzt wird es mysteriös: Es geht auch!

Dann debuggen wir mal den MFC Wrapper. Schrittweise gehe ich durch den Code im COleDispatchDriver::InvokeHelperV bis Zeile 223 und dort lese ich den folgenden Code und Kommentar:

pArg += dispparams.cArgs - 1; // params go in opposite order

❗ Aha! Hier liegt der Hase im Pfeffer. Ich hatte den CComVariant Array natürlich so aufgebaut, dass das erste Argument auch das erste Element im Array war. InvokeN will aber die selbe Reihenfolge wie IDispatch::Invoke, d.h. in umgekehrter Folge. Also beginnt der Array nun mit dem letzten Argument und endet mit dem ersten und siehe da: Es geht ❗

Schade, dass hier die ATL-Doku so lückenhaft ist. Das hätte mir hier, 2 Stunden Arbeit gespart.
Ich hätte es auch schneller haben können, wenn ich mir aufmerksam die Implementierung von CComPtr<IDispatch>::Invoke2 angesehen hätte (Man achte auf den Array varArgs):

inline HRESULT CComPtr<IDispatch>::Invoke2(__in DISPID dispid, 
       __in VARIANT* pvarParam1, 
       __in VARIANT* pvarParam2, 
       __out_opt VARIANT* pvarRet) throw() 
{ 
 if(pvarParam1 == NULL || pvarParam2 == NULL) 
   return E_INVALIDARG; 
 CComVariant varArgs[2] = { *pvarParam2, *pvarParam1 }; 
 DISPPARAMS dispparams = { &varArgs[0], NULL, 2, 0}; 
 return p->Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, 
     DISPATCH_METHOD, &dispparams, pvarRet, NULL, NULL); 
}

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 aus Performancegründen VirtualLock zu verwenden

Immer wieder tauchen in den Foren die Frage auf wie man vermeiden kann, das Seiten durch den Windows Memory Manager ausgelagert werden können.

Ursache für diese Frage ist der vermeintliche Glaube von einigen Entwicklern, dass man durch Vermeiden/Verbieten der Auslagerung von Speicherseiten, die Performance eines Programmes erhöhen könnte.

Das ist natürlich Unfug! 

Grundsätzlich sollte man die Dokumentation von VirtualLock beachten:

Locking pages into memory may degrade the performance of the system by reducing the available RAM and forcing the system to swap out other critical pages to the paging file. Each version of Windows has a limit on the maximum number of pages a process can lock. This limit is intentionally small to avoid severe performance degradation. Applications that need to lock larger numbers of pages must first call the SetProcessWorkingSetSize function to increase their minimum and maximum working set sizes. The maximum number of pages that a process can lock is equal to the number of pages in its minimum working set minus a small overhead.

Der Memory Manager weiß weitaus besser, was auszulagern ist und was nicht. Letzten Endes werden nur Seiten ausgelagert, die in der letzten Zeit nicht benötigt wurden. Ist ein Programm aktiv und nutzt den Speicher auch, besteht keine Gefahr, dass dessen Seiten ausgelagert werden.

Umgekehrt schränkt VirtualLock den Spielraum des Memory Managers ein. Werden Sie sinnlos im Speicher festgenagelt, dann müssen aktive Seiten ausgelagert werden und dass senkt meistens die Performance des gesamten Systems, weil nun unnötigerweise ausgelagert werden muss. Was nützt es, wenn das eigene Programm noch einigermaßen performant ist, aber ein Taskwechsel in den Explorer dann Minuten dauert?
Wie schon gesagt: Oft ist der Effekt genau entgegengesetzt!

Meine Erfahrung, selbst bei sehr speicherhungrigen Programmen ist:
Je weniger man Windows in die Quere kommt um so besser verhält sich das gesamte System.

Und bevor man sich an Funktionen wie VirtualLock versucht, sollteman evtl. eher den Speicherhunger seiner Programme eindämmen, oder den Performancemonitor bemühen, um heraus zu bekommen wo wirklich der Bottleneck ist. Die Perfomancewerte, die am meisten Auskunft über das Auslagern von Seiten geben sind im Abschnitt Memory (Speicher): Pages Input/sec (Seitenlesevorgänge/s) und Pages Output/Sec (Seiten-Schreibvorgänge/s). (Man beachte die inkonsistente Übersetzung 😉 !)
Pages Output/Sec (Seiten-Schreibvorgänge/s) ist mit Abstand der beste Indikator um Speicherkanppheit und Auslagerungsproblemen aufzuspüren!

Als Anmerkung sei noch hinzugefügt, dass VirtualLock Windows nicht hindert die Seiten dennoch auszulagern. Mehr Infos dazu hier, wobei dieser Artikel genauso auf die falsche Anwendung von VirtualLock hinweist:
http://blogs.msdn.com/oldnewthing/archive/2007/11/06/5924058.aspx

Ein guter Artikel über das Windows Speichermanagement findet sich noch hier:
http://members.shaw.ca/bsanders/WindowsGeneralWeb/RAMVirtualMemoryPageFileEtc.htm

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.

Die Unsitte in WM_CONTEXTMENU keine Tastaturnutzer zu berücksichtigen

Das viele Programmierer nur an die Maus denken, wenn Sie Kontextmenü hören ist schon an meinem letzten Blog Artikel klar geworden. 

Dennoch überraschen mich immer wieder Programm, die zwar korrekt auf die Kontextmenü-Taste (bzw. Umschalt+F10) reagieren, aber dann ein Kontextmenü links oben in der Ecke meines Monitors aufklappen.

OK! Es wurde verstanden, dass es eine WM_CONTEXTMENU Nachricht gibt, dass aber intelligentes Handling für Tastaturbenutzer anders aussieht, als für einen Mausbenutzer wird sehr selten verstanden oder berücksichtigt.

❗ Nehmen wir mal ein Beispiel:
Gegeben sei ein List View Control. Der Benutzer hat den Fokus auf diesem Control und drückt nun die Kontextmenütaste bzw. die Tasten Umschalt+F10.

❓ Frage: Was währe nun ein angemessenes Verhalten?

Wer die Doku gelesen hat wird wissen, dass in diesem Fall als Koordinaten (-1,-1) mit der WM_CONTEXTMENU Nachricht übermittelt werden. Dies ist nun der Grund dafür, dass viele Kontextmenüs nun in der linken oberen Ecke aufklappen, weil viele Entwickler eben nicht lesen (können), oder sich keine Gedanken machen, dass es einen Unterschied macht Mausbenutzer zu sein oder Tastaturbenutzer.

(-1,-1) ist keine geeignete Position für das Kontextmenü!

Nun die Mausposition zu bestimmen, wäre meiner Meinung nach, auch nicht angemessen. Denn der User benutzt diese ja gar nicht, und hat sie evtl. sogar einfach an den Bildschirmrand geschoben. Das Kontextmenü dort aufpoppen zu lassen wäre genauso falsch.

Einzig richtig wäre es den aktuell selektierten Eintrag mit dem Fokus im List View zu bestimmen und das Popupmenü knapp darunter und leicht rechts versetzt davon anzuzeigen. Dem Benutzer wäre der Kontextbezug sofort klar und auch seine Augen müssten nicht erst an eine andere Bildschirmposition wandern um zu erfassen, was nun möglich ist

Im Klartext heißt das für den Entwickler: selektiertes Item suchen (LVM_GETNEXTITEM), dessen Bildschirmposition Position zu bestimmen (LVM_GETITEMRECT) und sich eine gute alternative zu überlegen, wenn das Item außerhalb des sichtbaren Bereiches ist (z.B. Mitte des Controls).

Fazit: Um Kontextmenüs auch mit der Tastatur bedienbar zu machen gehört etwas mehr Grips und Aufwand dazu, als nur eine Popupmenü an einer bestimmten Koordinate anzuzeigen.

Die Unsitte WM_RBUTTONDOWN statt WM_CONTEXTMENU zu verwenden

Ich freue mich jedesmal, wenn ich ein Programm benutze und ein Kontextmenü öffnen will und es öffnet sich nicht.

„Ja da hast Du wohl nicht mit der rechten Maustaste geklickt, sonst würde es sich öffnen!“

werden jetzt einige sagen.
Und ja es stimmt, ich habe nicht mit der rechten Maustaste irgendwohin geklickt sondern die Kontextmenü-Taste rechts unten auf meiner Tastatur benutzt. Und wer es noch nicht wusste Umschalt+F10 löst die selbe Funktion aus.

Falls es also noch jemand nicht bemerkt haben sollte. Microsoft hat in der Windows API direkt eine Nachricht nur für die Behandlung von Kontextmenüs reserviert und die heißt: WM_CONTEXTMENU! Und es sollte jedem Entwickler bereits in Fleisch und Blut übergegangen sein, diese Nachricht und nichts anderes für Kontextmenüs zu verwenden.

Der nette Nebenbonus dieser Nachricht, ist, dass man sich nicht um jedes Fenster alleine kümmern muss. WM_CONTEXTMENU wird an das Elternfenster weitergereicht, wenn ein Kindfenster diese Nachricht nicht behandelt. Das macht es auch einfach für Dialoge einen zentralen Handler zu bauen.