ExitInstance gibt für dialogbasierende MFC Anwendungen Unfug zurück

Wer eine dialogbasierende Anwendung mal mit etwas mehr Aufmerksamkeit debuggt oder analysiert wird feststellen, dass der Returncode der Anwendung irgendwie ziemlich zufällig ist. Beobachtet man dies genauer dann stellt man folgendes fest:

  • Beendet man die Anwendung mit der Maus (Klick auf X) oder OK/Cancel so ist der Returncode 0
  • Hält man die Strg-Taste beim Klick fest ist der Returncode 8
  • Beendet man die Anwendung mit Alt+F4 bekommen wir 2.
  • Und jedermann kann jetzt schon mal raten was passiert, wenn wir die Umschalttaste festhalten. Genau dann bekommen wir 4 als Returncode.

Die Mystik hinter dem Ganzen ist die Behandlung von (Afx)PostQuitMessage. Eigentlich sollte mit dieser Nachricht auch der Exitcode gesetzt werden, der mit WM_QUIT versendet wird. Und wenn eben bei einer MDI/SDI Anwendung alles normal läuft, dann ist diese Nachricht die letzte, die aus der Messsagequeue gezogen wird. Und was passiert in CWinApp::ExitInstance? Genau… aus dem statischen Thread Puffer für die Windowsnachrichten wird mit  AfxGetCurrentMessage die letzte Windowsnachricht (normalerweise WM_QUIT) geholt und der wParam Wert bestimmt. Dieser wird dann zurückgegeben.

Leider ist aber WM_QUIT in manchen Fällen aber nicht die letzte Nachricht, die zum Beenden eines Programms führt. Ganz besonders eben nicht bei einer dialogbasierenden MFC-Anwendung. Da ist die letzte Nachricht ist dann eben ein WM_COMMAND oder ein WM_LBUTTONUP der das Schließen der Anwendung auslöst ❗ Und der wParam Wert ist eben entsprechend dieser Nachricht belegt!

Gleiches passiert natürlich, wenn man nach dem Beenden der Messageloop noch andere interne Fenster zerstört. Auch in diesem Fall kann noch mal die interne AfxWndProc durchlaufen werden und dann wird der Returncode auch wieder verändert.

Wer also wirklich Wert auf den Returncode legt (im wahrsten Sinne des Wortes), der sollte sich nie auf den Wert verlassen, der durch CWinApp::ExitInstance zurückgegeben wird oder den Wert, den man selbst mit AfxPostQuitMessage evtl. versucht zu setzen. Eine Variable in CWinApp tut hier einen besseren Dienst. Ebenfalls sollte man ExitInstance überschreiben und immer 0 zurückgeben, wenn man sowieso keine Verwendung für den Returncode des Prozesses hat oder haben möchte.

BTW:
Die Geschichte, wie ich darauf gekommen bin ist schon eigentümlich genug.
Ich habe komplexere Batch-Dateien, die die gesamte Erstellung einer produktiv-Version regelt. Darin kommen im Problemfall auch ein paar Userinteraktionen vor. Diese werden durch Windows Anwendungen ausgelöst, die evtl. einen Dialog anzeigen. Jedem ist klar, dass ohne Dialog und ohne Fenster keine Nachricht abgearbeitet wird. Der Returncode ist also 0! Der Batch verwendet 4NT Syntax und dort kann man ON ERROR definieren und somit sofort eine Fehlerbehandlung auslösen, wenn der Returncode eines Programms nicht 0 ist.
Nun kann sich jeder schon denken was passiert ist. Die Userinteraktion wurde ausgelöst. Der Benutzer machte eine Angabe und… der Batch terminierte erstaunlicherweise mit einem Fehler… (s.o.)

CMD.EXE versteht keine Unicode oder UTF-8 Batch Dateien

Ich habe für einen Deployment Prozess noch einen etwas größere komplexe Batch-Datei. Die BAT Datei enthielt auch zum Teil eben Umlaute die ja nicht ANSI sein dürfen. Zudem auch einige der Verzeichnisse neuerdings Umlaute enthalten, dachte ich mir: „Was soll es, Du speicherst das einfach als Unicode Datei!“ Die Vista CMD.EXE wird ja anhand der BOM erkennen was los ist.

😯 Pustekucken ❗ CMD.EXE mag in keiner Art und Weise Unicode oder UTF-8 in Batch-Dateien.

No chance at all. Einzig zur Unicode-Ausgabe kann man die CMD.EXE verleiten.
Eigentümlich ist das schon, weil TYPE z.B. BOMs korrekt berücksichtigt.

Da merke ich mal wieder wie verwöhnt ich mit 4NT bin.

PS: Ich weiß das es in Zeiten der Powershell andere Möglichkeiten gibt.
Man kann mich mit entsprechenden Kommentaren also gerne verschonen. 😉

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

CLIP.EXE der nette kleine Helfer unter Vista

Wer wollte nicht schon mal ganz schnell die Ausgabe eines Befehlszeilen Tools in der Zwischenablage haben. OK man kann einen Bereich mit dr Maus im Konsolenfenster markieren, aber was macht man wenn es um eine 2000 Zeilen Ausgabe geht?

Unter Vista ist das ganz einfach, man benützt einfach eine Pipe und CLIP.EXE!

DIR | CLIP

Siehe da. Man hat den gesamten Text in die Zwischenablage gepipet.
Was CLIP macht verrät es wenn man einfach mal CLIP /? ausführt.

CLIP

Beschreibung:
    Leitet die Ausgabe von Befehlszeilenprgrammen in die
    Windows-Zwischenablage um.
    Diese Textausgabe kann in andere Programme eingefügt werden.

Parameterliste:
    /?                  Zeigt diese Hilfe an.

Beispiele:
    DIR | CLIP     Erstellt eine Kopie des aktuellen Verzeichnis-
                         inhalts in der Windows-Zwischenablage.

    CLIP < README.TXT   Erstellt eine Textkopie von readme.txt
                         in der Windows-Zwischenablage.

BTW: Jeder 4NT bzw. TCC/LE Nutzer kennt natürlich schon längst den CLIP: Device! Diejenigen mussten auf solch ein Helferlein auch nicht bis Vista warten. Damit kann man das ganze auch einfach so

DIR > CLIP:

machen.

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.

Abgespecktes 4NT als TCC/LE von JP-Software als Freeware

Ich bin seit Jahren ein begeisterter Fan von 4NT (kennengelernt habe ich es noch als 4DOS unter DOS 4.0).
Was man so alles vermisst wenn man mal mit CMD.EXE arbeitet merke ich immer sofort, wenn ich ein DIR oder COPY ausführe und die komplexen Ranges, die 4NT bietet , sofort vermisse.
Ganz zu schweigen von netten Befehlen wie LIST, SELECT und anderem.

Auf den großen Bruder Take Command habe ich bisher immer verzichtet.
Ich verwende 4NT gerade auch da, wo ich komplexe Batchfiles für den gesamten Weg vom Build bis zum Setup benötige. All‘ das Kopieren von Dateien und Tools anwerfen, die dann irgendwann ein fertiges SETUP Paket erzeugen. Es ist einfach unschlagbar von den internen Funktionen, die hier geboten werden.

Jetzt hat mich JP-Software überrascht:

  1. Negativ: 4NT als Produkt alleine gibt es nicht mehr. Man kann den gesamten Funktionsumfang nur noch als Take Command erhalten.
  2. Positiv: Es gibt 4NT in etwas abgespeckter Form nun als TCC/LE (Take Command Console LE) kostenlos ❗

Soweit ich das sehe fehlt nur der Batch-Debugger und der Batch-Compiler, die ich sowieso kaum nutze, sonst sind alle meine gliebten Befehlszeilenfunktionen vorhanden.
In den internen Funktionen wird man evtl. die Datums- und Zeitfunktionen vermissen, die komplett herausgenommen wurden. Gleichfalls wie einige andere Funktionen.
Selbst mit den eingeschränkten Funktionen: TCC/LE ist ein Muss ❗

Download hier: http://www.jpsoft.com/tccledes.htm

Tipps & Tricks:EXE Dateien in der Eingabeaufforderung/Batches überall zugänglich machen ohne die Verwendung von PATH

Ich verwende sehr oft eine Eingabeaufforderung/Console/CMD.EXE/4-NT Session (wie man es auch nennen mag), weil vieles für mich einfach so schneller geht.
Zudem verwende ich eine Reihe von netten Helferleins (Tools), die zum Teil auch in meinen Buildprozessen integriert sind. Dort wird manchmal auch etwas gemacht was über ein TF CHECKOUT hinausgeht, oder was eben sowieso durch VS in den Path eingetragen wurde.

In den meisten Fällen nutze ich die normalen Installationsroutinen für diese Tools. Das Problem ist dann aber, dass diese Tools sich über X-C:\Program Files\Verzeichnisse verteilen.
Jetzt bei jedem Tool zu wissen wo es installiert ist übersteigt meine Kapazitäten und meine Lust die Pfade einzugeben. Es genügt, wenn ich weiß dass ich die Powertools des TFS mit TFPT aufrufen möchte, oder 7z.
Nun jeden dieser Pfade in PATH einzutragen ist ja wirklich auch nicht der Schreier. Das ganze wegen einer EXE…

Es gibt einen einfachen Weg sich Tools so zu behandeln, dass man sie von überall aufrufen kann. Dieser Weg ist in der Shell verborgen und lautet:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\
Jeder, der schon mal ein Installationsprogramm geschrieben hat kennt diesen Eintrag.

Was man zu tun hat ist ganz simpel: Man erzeugt einfach über RegEdit.exe in diesem Ast einen neuen Schlüssel mit dem Namen der EXE, die man gerne überall benutzen möchte (z.B. 7Z.EXE oder TFPT.EXE). Auf der rechten Seite als Standardwert trägt man einfach den vollständigen Pfad ein, wo diese EXE zu finden ist (in meinem Beispiel also: C:\Program Files\7-Zip\7z.exe oder eben C:\Program Files\Microsoft Team Foundation Server 2008 Power Tools\TFPT.exe).
So einfach kann es manchmal sein 😉

BTW: Leider geht das ganze nicht mit
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\!
Es gibt zwar einige dämliche Programme, die hier Eintragungen machen, allerdings haben die keine Wirkung.

Vista: Wie man den Kontextmenübefehl „Eingabeaufforderung hier öffnen“ auf die Powershell umbiegen kann

Torsten Schröder hat mir einen netten Kommentar in meinem letzten Blog-Artikel Vista: Wie man den Kontextmenübefehl “Eingabeaufforderung hier öffnen” umbiegen kann gegeben. Er meinte (Zitat):
„noch einen Tick besser fänd ich es, wenn man es Richtung Powershell umbiegt!“

Nun das gefällt mir auch und es ist auch nicht schwer. Mit dem folgenden kleinen Hack in der Registry kann man auch diesen erweiterten Kontextmenübefehl auch die Powershell umbiegen.

Einfach in der Registry unter HKEY_CLASSES_ROOT\Directory\shell\cmd\command
den Eintrag: cmd.exe /s /k pushd „%V“
z.B. in „C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe“ -noexit -command „set-location ‚%V'“ tauschen.

Damit lässt sich nun auch für mich das erweiterte Kontextmenü unter Vista perfekt nutzen:

  • 4NT integriert sich mit eigenen Kontextmenü-Befehlen.
  • und die Powershell ist nun über das erweiterte Kontextmenü des Vista-Explorers durch das Drücken der Umschalt-Taste extrem einfach zu erreichen.

Anmerkung: Wer den Faden verloren hat, alles beginnt mit diesem Tipp:
Zusätzliche Befehle im Explorer-Kontextmenü von Vista  😉

Vista: Wie man den Kontextmenübefehl „Eingabeaufforderung hier öffnen“ umbiegen kann

In meinem Artikel Zusätzliche Befehle im Explorer-Kontextmenü von Vista habe ich ja erwähnt, dass der Menüpunkt Eingabeaufforderung hier öffnen immer CMD.EXE verwendet und nicht den Befehlszeileninterpreter, der durch COMSPEC definiert ist.
Aber mit einem kleinen Hack in der Registry kann man auch diesen Befehl auf den eigenen Befehlszeileninterpreter umbiegen.

Einfach in der Registry unter HKEY_CLASSES_ROOT\Directory\shell\cmd\command
den Eintrag: cmd.exe /s /k pushd „%V“
z.B. in „C:\Program Files\4NT\4NT.EXE“ /s /k pushd „%V“ tauschen.

Anmerkung: Da 4NT normalerweise auch seine eigenen Einträge in den Kontextmenüs des Explorers macht, kann man es natürlich auch als Vorteil ansehen, wenn man bei Bedarf den originalen Befehlszeileninterpreter starten kann. 🙂

Zusätzliche Befehle im Explorer-Kontextmenü von Vista

Gerade habe ich durch Zufall im Explorer von Windows Vista entdeckt, dass sich das Kontextmenü verändert, wenn man die Umschalttaste festhält. Es ist hierbei egal ob man dies mit der rechten Maustaste oder mit der Kontextmenütaste macht. Es erscheinen 3 zusätzliche Befehle, wenn man eine Datei selektiert hat:

  1. An Startmenü anheften
  2. Zur Schnellstartleiste hinzufügen
  3. Als Pfad kopieren

und 2 neue Befehle wenn man ein Verzeichnis ausgewählt hat:

  1. Eingabeaufforderung hier öffnen
  2. Als Pfad kopieren 

Die beiden ersten Befehle für Dateien kann man vergessen.

Ganz anders sieht das jedoch mit dem letzten Befehl Als Pfad kopieren aus.  Der ist überaus nützlich um den Pfad einer Datei, oder gar die Pfade mehrerer Dateien in die Zwischenablage zu kopieren.
Unter XP hatte ich dazu immer noch das uralte Windows 95 Powertoy SendToX verwendet, das es leider für spätere Windows Versionen nicht mehr gab. Eigentlich schade, aber nun verschmerzbar.

Auch der Befehl Eingabeaufforderung hier öffnen ist sehr nützlich. Denn mit diesem Befehl kann man direkt ein DOS-Fenster in einem bestimmten Verzeichnis öffnen. Dafür gibt es zwar auch Registry-Hacks, aber Vista liefert das inklusive.
Leider verwendet Vista hier immer CMD.EXE und nicht den Befehlszeileninterpreter, der in COMSPEC eingetragen ist. Ich verwende seit Jahren 4NT.EXE und deshalb ist bei mir auch 4NT.EXE in COMSPEC eingetragen. Aber das ist verschmerzbar, denn 4NT mach seine eigenen Einträge im Explorer Kontextmenü, wenn man dies wünscht.

PS: Entdeckt habe ich das ganze, weil ich wusste, dass man bei einem Word Dokument zusätzlich den Befehl Schreibgeschützt Öffnen angeboten bekommt, wenn man die Umschalttaste festhält und das Kontextmenü öffnet über die rechte Maustaste oder die Kontextmenütaste. Das funktioniert auch für Excel-Dateien. Vielleicht wusste das ja auch noch nicht jeder ❗