Jubiläum: Mein 10. Award als deutscher MVP für Visual C++

Am 1. Oktober habe ich nun zum zehnten Mal den MVP-Award für Visual C++ erhalten.

Nach dem nun zehnten Award erlaube ich mir mal einen kleinen Rückblick, denn in all den Jahren hat sich doch einiges geändert.

  • In den ersten Jahren war ich hauptsächlich in den stark genutzten NNTP Foren tätig. Leider sind die im Allgemeinen auf den absteigenden Ast. Von weit über 2000 Postings im Monat in 2000 sind wir auf um die 500 abgesunken. Früher langte es auch als MVP einfach ein Teil der Gruppe von Postern zu sein, die qualitativ und auch quantitativ Fragen beantworten. Die meisten MVPs damals waren mehr oder weniger fast alles Regulars. Ich vermute jedoch, dass dies heute nicht mehr langt um MVP zu werden.
  • Vor ein paar Jahren hat mich die deutsche Szene dann doch ziemlich gelangweilt und ich war dann ziemlich aktiv in den neuen MSDN HTTP Foren, die allerdings nur in englisch wirklich Sinn machen. Die deutschen Foren benutzt kein Mensch.
    In den englischen Foren war es in den Anfangszeiten spannend und interessant. Ich habe es da sogar bis zum Moderator gebracht.
    Allerdings tummeln sich da nun auch so viele US-MVPs, dass ich mehr und mehr wieder den Spaß an diesen Foren verloren habe.
  • Und seit April 2006 bin ich dann im Deutschen C++ Forum eingestiegen.
    Seit Oktober diesen Jahres bin ich nun auch dort Moderator für das MFC und WinAPI Forum geworden.
  • Um 2006 fing ich auch mit meinem Blog an und war doch sehr erstaunt wie viele Leser ich erreiche.
    Und ich merke, dass auch das von den heutigen MVPs erwartet wird: Öffentlichkeit zu erreichen!
    Nicht wenige der aktuellen MVPs zeichnen sich mehr durch WEB-Seiten und Veröffentlichungen (Bücher) aus, als durch große Präsenz in Foren.   

Wenn ich auf all die Jahre so zurückblicke, dann sehe ich doch etwas wehleidig auf die Hochzeiten von nntp://microsoft.public.de.vc zurück, wo zeitweilig bis zu 5 VC++ MVPs aktiv waren und vermisse einige von ihnen.
Meines Wissens gibt es nur noch 2 „echte“ VC++ MVPs: Jochen Kalmbach und mich.
Hierzu jedoch noch zwei Anmerkungen:

  1. Muss ich hinzufügen: Soweit ich es weiß! Der Titel wird zwar verliehen, aber es steht einem ernannten MVP frei den Titel anzunehmen aber nicht öffentlich zu machen. So ist es möglich, dass es weitere MVPs gibt, die wir anderen MVPs gar nicht kennen. Oft kommen diese MVPs auch nicht zu treffen oder Konferenzen und somit bleibt die echte Anzahl von VC++ MVPs in Deutschland ein „kleines“ Geheimnis.
  2. Den Zusatz „Echte“ MVPs habe ich gewählt, weil die Windows SDK und TAPI MVPs mit uns VC++ MVPs zusammengelegt worden sind. Die Produktgruppe Windows SDK hat nun keine eigenen „speziellen“ MVPs mehr. Auch ein komisches und für mich nicht nachvollziehbares Vorgehen von Microsoft.
  3. Ich selbst kann nur Vermutungen über den Nominierungsprozess anstellen und so sind auch meine Äußerungen hier bitte zu werten. Es gibt sicherlich Empfehlungen für neue MVPs. Aber was genau notwendig ist um MVP zu werden weiß kein MVP (zumindest nicht ich). Genaues wissen da nur unsere MVP-Leads.

VS Tipps & Tricks: WinMerge als DIFF-Tool in Verbindung mit TFS

Ich bin ein wirklicher Fan von WinMerge und deshalb nutze ich es auch als Ersatz für das Standard TFS Tool DiffMerge.exe. Wer noch nicht wusste, dass man hier ein eigenes Tool im TFS für Compare und Merge wählen kann, der findet die entsprechenden Einstellungen unter: Tools -> Options -> Source Control -> Visual Studio Team Foundation Server -> Configure User Tools

Um WinMerge für den Compare einzusetzen verwende ich die folgenden Parameter:

Program: C:\Programme\WinMerge\WinMergeU.exe
Arguments: /x /e /wl /ub /dl %6 /dr %7 %1 %2

Und folgende Parameter habe ich für den Merge eingestellt:

Program: C:\Programme\WinMerge\WinMergeU.exe
Arguments: /x /e /wl /ub /dl %6 /dr %7 %1 %2 %4

Hier eine Erklärung aus der Onlinehilfe von WinMerge für Befehlszeilenschalter, die ich aktuell verwende:

  • /x closes WinMerge if opened files are identical (after information dialog is shown). This parameter is useful when WinMerge is used as an external compare application. It helps to faster process and/or ignore files which don’t have any differences. Note that this option does not apply when files become identical when merging/editing them.
  • /e allows WinMerge to be closed with a single Esc keypress. This is useful when using WinMerge as an external compare application. WinMerge can act like an dialog which is easy and fast to close.
  • /wl initially opens left side as read-only. Use this when you don’t want to change left-side items in compare.
  • /ub tells WinMerge to not add both paths to MRU. External applications should not add paths to Open-dialog’s MRU lists.
  • /dl adds a description for left side shown instead of folder / filename. This allows showing version number or label for compared items. Like „Version 1.0“ or „Work Copy“.
  • /dr adds a description for right side shown instead of folder / filename. This allows showing version number or label for compared items. Like „Version 1.0“ or „Work Copy“.

Die entsprechenden % Platzhalter sind so durch Visual Studio vorbelegt:

  • %1 = Original file
  • %2 = Modified file
  • %3 = Base file
  • %4 = Merged file
  • %5 = Diff options
  • %6 = Original file label (label for %1 file)
  • %7 = Modified file label (label for %2 file)
  • %8 = Base file label (label for %3 file)
  • %9 = Merged file label (label for %4 file)

Leider muss man für jeden Datentyp sowohl Compare als auch Merge einzeln einstellen. Eine Default-Einstellung habe ich nicht gefunden.

Es gibt einen schönen Blogeintrag von James Manning, der auch noch andere Diff-Tools behandelt:
http://blogs.msdn.com/jmanning/articles/535573.aspx
Wie man sieht habe ich die WinMerge Parameter jedoch angepasst.

Wie finde ich alte Artikel im Netz?

Microsoft ist dazu übergangen Artikel, die älter als 8 Jahre sind aus dem MSDN heraus zu nehmen. Leider! Denn viele wertvolle Windows API Artikel gehen damit verloren oder sind nicht mehr verfügbar, so zum Beispiel ist auch die OLE Bibel von Kraig Brockschmidt.

Besonders peinlich finde ich es, wenn Microsoft Artikel entfernt auf die in der KB noch Bezug genommen wird, wie zum Beispiel in diesem KB Artikel INFO: Syntax of the Res: Protocol and Some Known Related Issues.
Mir ging es um den Artikel http://www.microsoft.com/mind/0199/cutting/cutting0199.asp, dessen Link leider ins Leere verläuft.

Bleibt die Frage wie man solche alte Artikel wiederfindet, die noch von anderer Seite her referenziert werden ❓

Sofern man (wie hier) die komplette alte URL noch kennt gibt es zum Glück http://web.archive.org/   ❗

Einfach im Advanced Search die URL angegeben http://www.microsoft.com/mind/0199/cutting/cutting0199.asp

Und siehe da man erhält:
http://web.archive.org/web/*/http://www.microsoft.com/mind/0199/cutting/cutting0199.asp
Wie man sieht ist der Artikel Ende 2007 zuletzt auf den MS Seiten zu sehen gewesen.

Bleibt nur noch die Frage, wie man an die alten Links kommt ❓

Es gibt einen schnellen zweiten Weg, wenn der Artikel vor 2001 bereits veröffentlicht war. Google war so nett und hat uns Google 2001 bereitgestellt ❗

Die Suche ergab hier schnell das folgende Ergebnis:
http://www.google.com/search2001/search?hl=en&q=%22Pluggable+Protocols%22+res

Und siehe da, wir erhalten zusätzlich wieder einen Link auf das bereits erwähnte http://web.archive.org :
http://web.archive.org/web/2001/http://msdn.microsoft.com/library/periodic/period99/cutting0199.htm

Google 2001 kann hier also auch ein richtig gutes Helferlein sein um verschollene Informationen und Artikel wieder aufzufinden.

SETX der kleine Helfer für die wechselnden Environment Variablen des Entwicklers

In manchen Fällen kommt man um Environment Variablen nicht herum. Sie machen es auch einfach, globale Pfade für Linker, Präprozessor und auch für die Ausführung (PATH) zu setzen, oder eben auch EXEs und  DLLs zu steuern.

Solche Environment Variablen, bzw. INCLUDE und LIB Pfade zu setzen für das ganze System kann mühsam sein wenn man die Windows System UI nutzt. Der Aufruf ist umständlich und es erfolgt ein UAC-Prompt. Eine andere Variante wäre die Nutzung einer Reg-Datei. Dazu muss man aber wieder der UAC-Prompt über sich ergehen lassen.

Es geht viel einfacher, mit dem Helferlein SETX.EXE, das seit Vista nicht mehr nur im Windows Ressource Kit drin ist. Mit SETX kann man sich Ruckzuck ein kleines Batch-File schreiben, dass alle wichtigen Umgebungsvariablen für eine bestimmte Projektumgebung setzt.
Genial ist einfach, dass ab diesem Moment für jedes neu gestartete Programm diese Environment Variablen gelten. Natürlich inklusive der Visual Studio Umgebung.

In der Verbindung mit dem Aufruf von DEVENV /useenv kann man damit sehr schnell seinen Rechner auf eine komplett andere Projektumgebung einstellen. Und das ganze auch ohne UAC-Prompt 🙂 Ja! Man glaubt es kaum. Es erfolgt ja auch nur Zugriff auf HKCU.

Anmerkung:Ich benutzt sehr gerne den Property-Manager nur ist es eben nur eine Lösung für alle Software, die aus dem Visual-Studio heraus gestartet wird, und es nützt gar nichts, wenn man die EXE/DLL selber mit unterschiedlichen Umgebungen testen will.

Und SETX ist auch noch wirklich extrem flexibel. Man kann nicht nur einfach eine Environment Variable setzen, wie man es mit SET kennt. Als Quelle können hier sogar Tetxdateien, Registryschlüssel genutzt werden. Und das Multitalent SETX kann sogar bei Angabe von Computername, Nutzername und Kennwort remote benutzt werden und damit auch die Systemweiten Parameter verändern.

Hier die Beschreibung von SETX /?

SetX hat drei verschiedene Ausführungsmethoden:

Syntax 1:
    SETX [/S System [/U [Domäne\]Benutzer [/P [Kennwort]]]] var Wert [/M]

Syntax 2:
    SETX [/S System [/U [Domäne\]Benutzer [/P [Kennwort]]]] var /K
         Registrierungspfad [/M]

Syntax 3:
    SETX [/S System [/U [Domäne\]Benutzer [/P [Kennwort]]]]
         /F Datei {var {/A x,y | /R x,y Zeichenfolge}[/M] | /X}
         [/D Trennzeichen]

Beschreibung:
  Erstellt oder bearbeitet Umgebungsvariablen in der Benutzer- oder
  Systemumgebung. Ermöglicht das Festlegen von Variablen, die auf
  Argumenten, Registrierungsschlüsseln oder Dateieingabe basieren.

Parameterliste:
  /S    System             Remotesystem für die Verbindungsherstellung.

  /U    [Domäne\]Benutzer  Bestimmt den Benutzerkontext, unter dem
                           der Befehl ausgeführt wird.

  /P    [Kennwort]         Bestimmt das Kennwort für den
                           Auslassung fordert zur Kennworteingabe auf.

  Variable                 Bestimmt die zu setzende Umgebungsvariable.

  Wert                     Bestimmt einen zu setzenden Wert
                           für die Umgebungsvariablen.

  /K    Registrierungspfad Bestimmt, dass die Variable auf Informationen
                           aus einem Registrierungsschlüssel basiert.
                           Gültiges Pfadformat:
                           Struktur\Schlüssel\…\Wert, z. B.
                           HKEY_LOCAL_MACHINE\System\CurrentControlSet\
                           Control\TimeZoneInformation\StandardName.

  /F    Datei              Bestimmt den Dateinamen der zu verwendenden
                           Textdatei.

  /A    x,y                Bestimmt die absoluten Dateikoordinaten
                           (Zeile X, Element Y) als Parameter, nach denen
                           innerhalb der Datei gesucht werden soll.

  /R    x,y Zeichenfolge   Bestimmt die relativen Dateikoordinaten im
                           Verhältnis zur „Zeichenfolge“ als Suchparameter.

  /M                       Legt fest, dass die Variable in der
                           systemübergreifenden Umgebung
                           (HKEY_LOCAL_MACHINE) gesetzt werden. Der
                           Standardwert wird unter der Umgebung gesetzt
                           (HKEY_CURRENT_USER).

  /X                       Zeigt Dateiinhalte mit den x,y Koordinaten an.

  /D    Trennzeichen       Bestimmt zusätzliche Trennzeichen, wie z. B.
                           „,“ oder „\“. Standardtrennzeichen sind
                           Leerzeichen, Tabulator, Wagenrücklauf und
                           Zeilenvorschub. Beliebige ASCII-Zeichen können
                           als zusätzliche Trennzeichen verwendet
                           werden. Die max. Anzahl an Trennzeichen,
                           einschließlich Standardtrennzeichen beträgt 15.

  /?                       Zeigt diese Hilfe an.

HINWEIS: 1) SETX schreibt Variablen in die übergeordnete Umgebung
            in der Registrierung.

         2) Auf einem lokalen System sind Variablen, die mit
            diesem Programm erstellt oder bearbeitet wurden,
            erst in zukünftigen und nicht in aktuellen
            cmd.exe Befehlszeilenfenstern verfügbar.

         3) Auf einem Remotesystem sind Variablen, die mit
            diesem Programm erstellt oder bearbeitet wurden, erst
            bei der nächsten Anmeldesitzung verfügbar.

         4) Gültige Registrierungsschlüssel-Datentypen:
            REG_DWORD, REG_EXPAND_SZ, REG_SZ, REG_MULTI_SZ

         5) Unterstützte Registrierungsstrukturen: HKEY_LOCAL_MACHINE
            HKEY_CURRENT_USER (HKCU).

         6) Bei Trennzeichen wird zwischen Groß-/Kleinschreibung
            unterschieden.

         7) REG_DWORD-Werte werden aus der Registrierung
            im Dezimalformat extrahiert.

Beispiele:
    SETX MACHINE COMPAQ
    SETX MACHINE „COMPAQ COMPUTER“ /M
    SETX MYPATH „%PATH%“
    SETX MYPATH ~PATH~
    SETX /S System /U Benutzer /P Kennwort MACHINE COMPAQ
    SETX /S System /U Benutzer /P Kennwort ^%PATH^%
    SETX TZONE /K HKEY_LOCAL_MACHINE\System\CurrentControlSet\
         Control\TimeZoneInformation\StandardName
    SETX BUILD /K „HKEY_LOCAL_MACHINE\Software\Microsoft\Windows
         NT\CurrentVersion\CurrentBuildNumber“ /M
    SETX /S System /U Benutzer /P Kennwort TZONE /K HKEY_LOCAL_MACHINE\
         System\CurrentControlSet\Control\TimeZoneInformation\
         StandardName
    SETX /S System /U Benutzer /P Kennwort BUILD /K
         „HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\
         CurrentVersion\CurrentBuildNumber“ /M
    SETX /F ipconfig.out /X
    SETX IPADDR /F ipconfig.out /A 5,11
    SETX OCTET1 /F ipconfig.out /A 5,3 /D „#$*.“
    SETX IPGATEWAY /F ipconfig.out /R 0,7 Gateway
    SETX /S System /U Benutzer /P Kennwort /F c:\ipconfig.out /X

Die Return-Taste in Dialogen, eine unendliche Geschichte

Wer sich in Windows-Programmierer-Foren tummelt, dem wird die folgende Frage mindestens einmal in der Woche über den Weg laufen:

Ich möchte in einem Dialog die Eingabetaste abfangen, so dass sich der Dialog nicht schließt.
Wie geht das?

Vom Sinn und Unsinn dieses Wunsches wollen wir mal hier nicht reden. Also schauen wir mal auf dieses Problem etwas genauer.

Die Lösung WM_COMMAND mit IDOK abfangen, lasse ich nicht gelten , denn evtl. hat der Dialog ja einen OK Button. (In der MFC käme das mit dem Überschreiben von CDialog::OnOK gleich)

Die Standardantwort von MFC Entwicklern lautet immer:

Überschreibe PreTranslateMessage und fange WM_KEYDOWN mit VK_RETURN dort ab und behandle die Nachricht dort.

OK. Aber was macht ein Win32-API Entwickler. Der hat keinen Einfluss auf die Message-Loop. Muss der nun zu der Hook-Kanone greifen?

Wo liegt eigentlich das Problem?
Die Funktion, die VK_RETURN in ein WM_COMMAND mit IDOK umwandelt ist die API Funktion IsDialogMessage. Diese Funktion wird in der Messageloop von modalen Dialogen verwendet. In der MFC direkt in CDialog::PreTranslateMessage und in Win32 Dialogen wird sie in der Messageloop die DialogBox ausführt.

Diese Funktion geht aber mit solchen Tastatureingaben nicht einfach wahllos um. Sie macht das sehr intelligent. Das sieht man schon daran, dass man in einer aufgeklappten ComboBox die Eingabetaste drücken kann und es schließt die ComboBox und nicht der Dialog.
Ebenfalls kennt man den Effekt, dass ein mehrzeiliges Eingabefeld (Edit Control) sehr wohl die Eingabetaste als Zeilenschaltung nutzen kann und auch hier schließt der Dialog nicht.
Kennt also IsDialogMessage seine Pappenheimer von Controls?

Woher weiß IsDialogMessage also wann VK_RETURN mal so und mal so behandelt werden muss?
Die Antwort ist einfach: IsDialogMessage fragt seine Controls ab was sie gerne hätten!
Und das wird erreicht mit einer einfachen Windows-Nachricht, die in diesem Zusammenhang fast nie genannt wird: WM_GETDLGCODE.

Bevor IsDialogMessage eine Tastatureingabe selbst behandelt, fragt die Funktion das Zielfenster mit WM_GETDLGCODE ab, ob das Fenster evtl. selbst Verwendung für diese Tastatureingabe hat.
WM_GETDLGCODE kann hier sehr flexibel reagieren, denn in wParam erhält die Nachricht den virtuellen Tastencode und das Control weiß nun sehr genau um was es hier geht.

Kann man also in jedem Control (das natürlich den Fokus hat) die Eingabetaste selbst behandeln?
Antwort: Ja!

Das Control muss nur einfach WM_GETDLGCODE behandeln für die Taste VK_RETURN. In diesem Fall gibt es DLGC_WANTMESSAGE zurück (ist übrigends identisch zu DLGC_WANTALLKEYS). IsDIalogMessage wird in diesem Fall die Eingabetaste an das Control weitergeben und nicht selbst behandeln. So macht es übrigens auch eine ComboBox die aufgeklappt ist wenn die Eingabetaste gedrückt wird.

Ich habe einfach mal hier eine Win32 Lösung aufgebaut. Es wird einfach das entsprechende Control mit einer eigenen WndProc gesubclassed und das sieht etwa so aus:

LRESULT CALLBACK SpecialWndProc(HWND hWnd,
                        UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_GETDLGCODE:
        if (wParam==VK_RETURN)
            return DLGC_WANTMESSAGE;
        break;
    case WM_KEYDOWN:
        if (wParam==VK_RETURN)
        {
            MessageBox(hWnd,_T("VK_RETURN received!"),NULL,MB_OK);
            return 0;
        }
        break;
    }
    WNDPROC pWndProc = reinterpret_cast(GetWindowLongPtr(hWnd,GWL_USERDATA));
    return CallWindowProc(pWndProc,hWnd,message,wParam,lParam);
}

...
// Subclass the control
HWND hWndEdit = GetDlgItem(hDlg,IDC_EDIT1);
LONG_PTR pOldWndProc = SetWindowLongPtr(hWndEdit,GWL_WNDPROC,
                        reinterpret_cast(SpecialWndProc));
SetWindowLongPtr(hWndEdit,GWL_USERDATA,pOldWndProc);

Effekt: Jedes Control, dass man mit dieser WndProc subclassed wird die Messagebox anzeigen und der Dialog wird nicht geschlossen! Subclassed man also alle Controls eines Dialoges, die den Fokus bekommen können, dann kann man sofort ohne Hook, auch die Eingabetaste selbst behandeln.
Sinnvollerweise sendet man bei Erhalt von VK_RETURN dann einfach eine entsprechende Nachricht an das Parent, dass die gewünschte Aktion dann ausführt, sofern nicht das Control selbst die Eingabetaste direkt behandelt.

Man kommt also auch in reinen Win32 API Programmen ganz ohne Hooks aus, um VK_RETURN in Dialogen so zu behandeln wie man es selbst gerne möchte!

Bookmark on MSDN auch auf meiner Seite

Social Bookmarking V1 wurde ja gerade veröffentlicht, wie Kay Giza berichtete.

In meinem Blog auf der rechten Seite unter dem Blog-Info habe ich mal gleich mal das neue MSDN Bookmark Widget eingebaut. Ich bin mal gespannt wer es benutzen wird.

Der Einbau ist einfach. Man fügt nur den nachfolgenden Skript Code auf seiner HTML Seiten ein.

<script src="http://Services.social.microsoft.com/widgets/bookmark.jss?
type=1&brand=Msdn&locale=1031" type="text/javascript"></script>

So ist das ganze auch auf dieser Tools Seite beschrieben.
Was leider nicht dokumentiert ist und ich nur durch herumprobieren hinbekommen habe ist die Bedeutung des Type Parameters. Der Type Parameter hat folgenden Einfluss auf die Darstellung des Links.

  • Mit Type=0 erhält man nur ein Icon
  • Mit Type=1 erhält man das Link Icon inkl. dem Text Bookmark on MSDN

Wie man sieht habe ich mich für Type=1 entschieden.

Dialog basierende MFC-Anwendungen einmal anders

Ich habe mich zwar gerade erst darüber ausgelassen, dass man dialog basierende Anwendungen gar nicht braucht (sieh hier), aber ganz und gar unsinnig sind sie ja nicht.

Bei mir ist der häufigste Anwendungsfall eine simpler Dialog, der nur zum Steuern eines Programmes dient, das ein Icon in der Taskleiste abablegt.

Ärgerlich ist dann nur, dass man nicht vermeiden kann, dass beim Start der Anwendung der Dialog kurz aufflackert, bevor man ihn mit einem Timer oder mit PostMessage und einer benutzerdefinierten Nachricht wieder versteckt.

Aber es geht viel einfacher und dazu noch in einer Form, die der MFC-Konstruktion besser entspricht, als das Gerüst, das der Applikations-Wizard erzeugt.
Ich möchte das hier einfach kurz erläutern:

1. Erzeuge ich eine normale dialog basierende Anwendung mit dem Wizard.

2. Dann lege ich ein Objekt der Dialog Klasse mit dem Namen m_dlg in der CWinApp Klasse an. Ich möchte damit erreichen, dass die Dialogklasse so lange existiert wie die Applikation und nicht mehr nur als lokale Variable in InitInstance auftaucht.

3. Der gesamte Code, den der Wizard erzeugt hat:

CTestDLGDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
    // TODO: Place code here to handle when the dialog is
    // dismissed with OK
}
else if (nResponse == IDCANCEL)
{
    // TODO: Place code here to handle when the dialog is
    // dismissed with Cancel
}

wird ersetzt durch:

// Create the Dialog
if (m_dlg.Create(CMyDlg::IDD))
{
    m_pMainWnd = &m_dlg;
    return TRUE;
}
else
    return FALSE;

4. Kleine Schönheitskorrektur nun. Man verlagert das Laden des Applikations-Icons aus dem Konstruktor des Dialogs nach OnInitDialog.

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

Fertig ❗

Kurz erklärt: Aus dem modalen Dialog der durch DoModal gestartet wird wird nun ein modaler Dialog, der durch die Messageloop der CWinApp::Run gesteuert wird. Von der sonstigen Funktionsweise ändert sich nichts.
Dieser Konstrukt hält sich nach meiner Meinung weit mehr an das MFC Gerüst, als die Anwendung, die der Wizard erzeugt.
Die Vorteile sind schnell aufgezählt:

  1. InitInstance wird durchlaufen und CWinApp:Run wird verwendet.
    Dies hat z.B. zur Folge, dass auch CWinApp::OnIdle durchlaufen wird und temporäre Map Objekte entsorgt werden. (siehe Kommentar unten)
  2. Man kann die Applikation ohne Flackern zu dem Moment sichtbar machen an dem man es möchte. Dazu muss man nur das WS_VISIBLE Flag im Dialogtemplate entfernen und ShowWindow aufrufen, wenn es einem passt.

Vielleicht sehen ja andere Leser noch mehr Vorteile… die Diskussion ist eröffnet 😉

PS: Noch ein kleiner Nachtrag zu den oben erwähnten temporären Handle Maps in einer dialog basierenden Applikation. Diese temporären Handlemaps werden automatisch aufgeräumt und gelöscht wenn CWinApp::OnIdle ausgeührt wird. Das ist normalerweise der Fall wenn in CWinApp::Run keine Nachricht in der Messagequeue liegt, die abgearbeitet werden muss (PeekMessage gibt FALSE zurück). Im Gegensatz dazu werden Handlemaps nicht gelöscht wenn die Nachrichtenschleife mit CWnd::RunModalLoop für einen modalen Dialog ausgeführt wird. So also auch niemals in einer dialog basierenden Anwendung, oder wenn ein modaler Dialog in einer MFC-Applikation ausgeführt wird!
Das fast noch mal einen Artikel wert…

Manche ungenaue Dokumentation nervt einfach

Die MSDN ist eines der größten und besten Nachschlagewerke, die ich kenne, allerdings gehen mir auch seine Unzulänglichkeiten ab und zu auf die Nerven (und hier meine ich nicht die funktionellen Defizite, sondern die inhaltlichen)!

Das sind dann ganz besonders die kleinen Sachen, die einem das Leben leicht machen könnten, die dann nicht „vollständig“ dokumentiert sind.

Beispiel:
Die MFC List-Container (z.B. CObList). Wir lesen in der Doku zu InsertAfter:

position
A POSITION value returned by a previous GetNext, GetPrev, or Find member function call.

Gut! Aber da steht nichts zu NULL. 😕
Aber wenn ich genauer darüber nachdenken, kann GetNext und Find auch NULL zurückgeben…
Was passiert also wenn ich NULL als Argument für position?
Mein Verstand sagt mir: „Es wird ein Element am Ende eingeführt!“ und ein Blick in den Code bestätigt den Verdacht.

Warum steht in der Doku nicht gleich, dass InsertAfter mit NULL als position ein AddTail ausführt?
Wer weiß wieviele Entwicler solchen „unnützen“ Code aufgrund der mangelnden Doku geschrieben haben:

CStringList lst;
FillMyList(lst);
POSITION pos = lst.Find(_T("Anything"));
if (pos==NULL)
    lst.AddTail(_T("Something to insert"));
else
    lst.InsertAfter(pos,_T("Something to insert"));

Es würde ja genügen wie folgt zu schreiben:

CStringList lst;
FillMyList(lst);
POSITION pos = lst.Find(_T("Anything"));
lst.InsertAfter(pos,_T("Something to insert"));

Korrespondierend ist die Doku von CObjList::InsertBefore genauso unvollständig.
Wird NULL als Position verwendet wird hier ein AddHead ausgeführt.

PS: Mich ärgert auch jedesmal wenn einer meiner Kollegen solchen Code schreibt:

CSomeObject *p = NULL;
...
// Conditional create
if (SomeThing())
     p = new CSomeObject();
...
// Cleanup
if (p)		// Category meaningless
    delete p;
  1. Warum der Test auf !=NULL?
  2. Warum wird kein Autopointer verwendet, der das auch Exception-Save gemacht hätte?

Die Unsitte dialogbasierende Anwendungen zu bauen statt SDI mit CFormView zu verwenden

Es scheint mir meistens ein Anfängerfehler zu sein, dass viele MFC Entwickler (oder solche die es werden wollen) zuerst mal zu einer dialogbasierenden Anwendung greifen.
Ist ja auch nett. Man kümmert sich nur um die paar Dialogfelder und hat kein Doc/View zu verwalten.

Am Ende kommen aber dann noch weitere Wünsche:

  • Ich hätte gerne ein Menü
  • Ich hätte gernenoch einen Toolbar
  • Ich hätte gerne einen Status Bar
  • Schön wäre ein Accelerator

Triviales und nicht triviales schließt sich an:

  • Warum kann ich kein Command/Routing für meinen Toolbar und mein Menü verwenden?
  • Warum schließt mein Dialog bei Nutzung Eingabe-Taste?
  • Kann ich meinen Dialog auch resizen?
  • Die Daten sollen auch gespeichert werden, wie geht das?
  • Ich hätte gerne so einen schönen MFCNext Toolbar in meinem Dialog, oder ein Ribbon, geht das?
  • … (die Liste ist bestimmt nicht vollständig)

Gibt man dann die Antwort, dass man x Klimmzüge machen muss um so etwas in eine dialogbasierende Anwendung einzubauen (wenn es überhaupt geht), dann erntet man noch noch die stöhnende Klage: „Ohhh Mann! Ist das kompliziert!“

Und all das geht nur mit Mühe in einen CDialog einzubauen. Der Grund ist einfach: Das Commandrouting für all diese Elemente ist in CFrameWnd integriert. Aber ein CDialog leitet sich von CWnd ab und ist von Grunde auf für keine dieser Funktionen vorbereitet.

Dabei könnte alles so einfach sein!
Man muss nur einfach eine SDI Applikation mit einem CFormView erzeugen und alle die Wünsche die mancher später hat, kann man sofort erfüllen und wenn man es wirklich nicht will auch weglassen.

Die Frage stellt sich für mich also:
Warum nicht einfach immer gleich eine SDI/CFormView Anwendung bauen ❓
Das Potential dieser Anwendungsform ist einfach unerreichbar verglichen mit einer dialogbasierenden Anwendung.

Also sollte man sich mal die Liste der Wünsche, die ich hier aufgestellt habe ansehen und als Checkliste betrachten. Sollte einer dieser oben aufgeführten Punkte für die Applikation wichtig sein, würde ich dringend anraten zum SDI/CFormView zu greifen.
Das ist auch der Fall, wenn diese Anforderungen erst später integriert werden soll. Oft genug ist ja die dialogbasierende Anwendung schon fertig und es heißt dann: Ich möchte doch nur noch…

PS: Ich persönlich benutze nicht mal mehr zu Testzwecken dialogbasierende Anwendungen 😉

Aufflackern eines Konsolenfensters bei Nutzung system und _popen

Immer wieder kommt die Frage auf, warum sich ein Konsolenfenster kurz öffnet wenn man eine Windows Anwendung, wie z.B. Notepad mit den CRT Funktion system startet.

Die Antwort ist ganz einfach, dazu muss man einfach mal nicht einmal unbedingt einen Blick in die Sourcen werfen, denn es ist sogar richtig für system dokumentiert in der MSDN.

The system function passes command to the command interpreter, which executes the string as an operating-system command. system refers to the COMSPEC and PATH environment variables that locate the command-interpreter file (the file named CMD.EXE in Windows 2000 and later). If command is NULL, the function simply checks to see whether the command interpreter exists.

Also kurz und bündig: Es wird zwangsläufig immer ein Konsolenfenster geöffnet. Das verschwindet zwar sofort wieder, wenn man eine GUI Applikation startet, aber das kann man vermeiden indem man gleich ShellExecute verwendet.

Ganz anders sieht es mit der Doku bei _popen aus. _popen scheint sich anzubieten, um die Ausgaben eines Tools in eine Datei umzuleiten. Aber auch _popen nutzt wie system CMD.EXE /c (COMSPEC). Um das heraus zu bekommen muss man allerdings den Source Code der CRT zu Rate ziehen. Das bedeutet, dass auch bei Verwendung von _popen ein Flackern durch ein Konsolenfenster nicht ausbleibt.

Wie man es richtig macht, wenn man die stdin/stdout umleiten will findet man in den wohlbekannten Artikeln der MSDN http://support.microsoft.com/kb/190351 und http://msdn.microsoft.com/en-us/library/ms682499.aspx. Durch die Verwendung von CreateProcess lässt sich auch ganz vermeiden, dass der zweite Prozess angezeigt wird.