VS Tipps & Tricks: Goto Dialog im Class View

Eventuell ist dies auch gar kein Tipp für Euch, weil Ihr diese Funktion schon läääängst kennt. In diesem Fall ist dieser Artikel die Erkenntnis, dass ich (selbst nach Jahren intensiver Arbeit) immer wieder Funktionen entdecke, die ich gerne früher gekannt hätte ;). In diesem Fall habe ich ihn erst Anfang dieser Woche entdeckt.

Es ist eine Funktion, die nur MFC/ATL Entwickler nutzen können (oder Ihrer bedürfen):

  • Man hat ein MFC Projekt
  • Wähle im Classview eine von CDialog abgeleitete Klasse
  • Dann öffnet man das Kontextmenü
  • und findet den Befehl Goto Dialog

Führt man diesen Befehl aus, öffnet sich der Ressourcen-Editor mit dem entsprechenden Dialog.

Schön, dass es diese Funktion gibt, denn IDD_’s heißen oft genug so anders als die Klassen, dass es bei manchen großen Projekten immer erst eines Blickes in die Header Datei bedarf um den richtigen Dialog zu finden.

Wunderbar! Hätte ich diesen Befehl zuvor gekannt, dann hätte In den letzten Jahren bestimmt 10 Minuten Zeit sparen können, in denen ich verzweifelt Dialoge in den Ressourcen suche… 😉

CB_ADDSTRING/CB_INSERTSTRING+CBS_UPPERCASE+Unicode+const String Pointer == das große Erstaunen

Es gibt immer wieder Momente in denen einen die Win32 API in größtes Erstaunen versetzt.
Meistens ist dies allerdings in diesen Momenten nichts positives. Das zweite Erstaunen folgt, dann wenn man in der MSDN nachliest und das Verhalten  als dokumentiert vorfindet. Meistens endet solch eine Kette dann in einem Kopfschütteln und dem Gedanken: Das darf doch gar nicht sein.

Genug der langen Vorworte:

  • Gegeben ein normales MFC Projekt. (WinAPI pur tut es auch 😉 )
  • Ein Dialog
  • Darin eine Combobox als Dropdown mit Eingabemöglichkeit
  • Die Combobox wird mit einigen Variablen Werten gefüllt mit CComboBox::Addstring/InsertString, was letzten Endes nur ein Wrapper für CB_ADDSTRING/CB_INSERTSTRING ist.
  • Das ganze sieht also in OnInitDialog in etwa wie folgt aus
cb.AddString(CString(MAKEINTRESOURCE(IDS_DATA1)));
cb.AddString(CString(MAKEINTRESOURCE(IDS_DATA2)));
cb.AddString(CString(MAKEINTRESOURCE(IDS_DATA3)));
cb.AddString(strSomeDynamicData);
cb.InsertString(0,_T(""));

Alles OK… Als letztes noch den Stil CBS_UPPERCASE dazu – weil hier eben nur Eingaben in Großbuchstaben erlaubt sein sollen und Sinn machen – und… Peng!
Das Programm schmiert ab. 😮

Was ged’n hier ab Alder ❓

In den tiefen des Aufrufs von cb.InsertString(0,_T(„“)); schmiert mein Programm ab. UAE
Nun aber doch großes Erstaunen, denn der selbe Code funktioniert in einem MBCS Programm.
Es liegt eindeutig an der Nutzung von CBS_UPPERCASE und Unicode.

Jetzt habe ich schon gedacht einen Bug in Vista und XP gefunden zu haben, denn auf beiden schmiert bersagter Code ab. Und wahrscheinlich ist es auch ein Bug! Aber das genauere Nachlesen der MSDN belehrt mich eines besseren. Dieses dämliche Verhalten ist dokumentiert, zumindest für CB_ADDSTRING (der Zusatz fehlt in der CB_INSERTSTRING Doku):

Comclt32.dll version 5.0 or later: If CBS_LOWERCASE or CBS_UPPERCASE is set, the Unicode version of CB_ADDSTRING alters the string. If using read-only global memory, this causes the application to fail. ❗

Unfassbar! Die Nachricht ist dabei selbst so beschrieben, wie es sich jeder vernünftige Entwickler auch denkt und vor allem erwartet, eben mit einem LPCTSTR:

lResult = SendMessage(      // returns LRESULT in lResult
     (HWND) hWndControl,    // handle to destination control
     (UINT) CB_ADDSTRING,  // message ID
     (WPARAM) wParam,      // = 0; not used, must be zero
     (LPARAM) lParam       // = (LPARAM) (LPCTSTR) lParam;
);

Aber was nützt schon ein vernünftiger Gedanke eines Entwicklers wenn es um das irrwitzige Eigenleben der Win32 API geht.
Und denke nie einen Bug gefunden zu haben, bevor Du nicht jede Zeile der MSDN studiert hast 😉

Vista SP1 und so manche Ruhezustand/Hibernate Probleme

Irgendwie wunderte ich mich in letzter Zeit, dass mein Outlook 2007 beim Neustart meines PCs meinte, es wäre nicht korrekt beendet worden. Und irgendwann habe ich im Event Log dann den Folgenden Eintrag gefunden:

Das System wurde zuvor am 11.05.2008 um 19:26:46 unerwartet heruntergefahren.

Nach ein wenig Analyse kam ich dahinter, dass gemäß Energiesparplaneinstellungen mein Rechner irgendwann in in den Ruhezustand (Hibernate) geht. Allerdings beim Neueinschalten des Rechners nicht wieder den letzten Zustand wieder herstellt.
Die Startmeldung, dass das System nicht ordnungsgemäß herunter gefahren wurde. Diese Meldung habe ich irgendwie immer übersehen, da ich meistens meinen Rechner einschalte und wieder weg gehe und noch anders erledige bis er gebootet hat.

Um solche Probleme wird sehr viel Blödsinn im Netz geschrieben. Zu allerst stehen die Tipps mit einem BIOS Update, und alles mögliche andere Spielereinen mit POWERCFG und anderes. Für mein Problem trafen sie alle nicht zu. Ich hätte einfach damit leben können und mit POWERCFG -h OFF einfach den Ruhezustand ausschalten können. Aber irgendwie lassen mich solche Probleme nicht los und ich will sie lösen.
Einiges Suchen und viele, viele  erfolglose Versuche und Tests brachten mich schließlich zu dieser Lösung, die hier in diesem Thread beschrieben ist:
http://forums.microsoft.com/TechNet/ShowPost.aspx?PostID=2897541&SiteID=17

  1. Run CMD.EXE as administrator
  2. Run the following command: bcdedit -enum all
    Look for „Resume from Hibernate“ in the output from the command above(example below):
    Resume from Hibernate
    ———————
    identifier {3d8d3081-33ac-11dc-9a41-806e6f6e6963}
    device partition=C:
    path \Windows\system32\winresume.exe
    description Windows Vista (TM) Enterprise (recovered)
    inherit {resumeloadersettings}
    filedevice partition=C:
    filepath \hiberfil.sys
    pae Yes
    debugoptionenabled No
  3. Once you have found it, copy the value for identifier (in this example : {3d8d3081-33ac-11dc-9a41-806e6f6e6963})
  4. Run the following command:
    bcdedit /deletevalue {3d8d3081-33ac-11dc-9a41-806e6f6e6963} inherit
  5. Test hibernation.

OK! Das wäre behoben. Alles wieder heile.

Da ich aber jetzt schon etwas hin und her gespielt hatte mit POWERCFG -h ON|OFF etc. fragte ich mich nun, warum in meinem Startmenü, Energiesparen und alles mögliche andere angeboten wird, aber nicht Ruhezustand. Was habe ich nun kaputt repariert?

Komischer Weise lies der Rechner sich sofort in den Ruhezustand per Netzschalter-Tastendruck am PC versetzen, genauso wie eingestellt. Aber im Startmenü erscheint es nicht. Bei meinem Laptop geht es doch auch?

Einiges Lesen und Suchen in der Online-Hilfe brachte dann die Erklärung! Ja es ist wirklich beschrieben:
Nur wenn der Hybriden Standbymodus ausgeschaltet ist, erscheint auch der Ruhezustand im entsprechenden Startmenü.

Systemsteuerung -> System und Wartung -> Energieoptionen -> Energiesparplaneinstellungen bearbeiten -> Erweiterte Energieeinstellungen ändern -> Energiesparen -> Hybriden Standbymodus zulassen -> Einstellung: Aus

Jetzt ist wieder alles so, wie ich es will…

Was tun wenn Groove 2007 nicht mehr startet?

Ich nutze gerne Groove 2007 für einige Daten, die ich zwischen einigen Rechnern (Laptop, Desktop, Firmen-Desktop) austausche.
Leider startete aus irgend einem Grunde mein Groove 2007 auf meinem Desktop-Rechner nicht mehr. Groove crashte sofort, wenn es in irgend einer Form gestartet wurde. Auch das Eventlog zeigte entsprechende Einträge über den Crash, aber eben keine weiteren Informationen.

Also:

  • Reparaturinstallation: erfolglos
  • Deinstallation neue Installation: erfolglos
  • Gesharte Ordner gelöscht: erfolglos
  • Googlen nach ähnlichen Probleme: erfolglos

Schließlich stieß ich auf GrooveClean.exe im Office12 Verzeichnis. Googlen ergab den folgenden Artikel:
http://support.microsoft.com/kb/907854/en-us

Na ok! Dann mal los. GrooveClean brachte keinen Erfolg aber nach GrooveClean -all startete Groove wieder und ich konnte mir (etwas mühsam) meinen Account und die gesharten Ordner wieder einrichten.

Anmerkung:
Es ist wirklich ratsam seine Kontodaten als Datensicherung aus Groove zu exportieren und an einem sicheren Ort zusätzlich zu verwahren!

Lange habe ich es vor mir hergeschoben: Das WordPress 2.5.1 Update

Es war ja schon einige Zeit fällig, das Update auf die Version 2.5!
Direkt bei den ersten Versuchskaninchen wollte ich nicht dabei sein, nachdem aber nun um die 2.5.1 Version Ruhe eingekehrt ist, habe ich mich eben kurzer Hand entschlossen das Update durchzuführen.

Es ging alles – wider erwarten – ganz glatt 😉 !
Damit ist mein Blog wieder auf dem neuesten Softwarestand. Die Datensicherung wurde nicht benötigt. Wie gut!

Anmerkung:
Zum Glück verwende ich nur populäre Plugins, die alle 2.5 kompatibel sind. Und das Theme, das ich verwende ist handgestrickt und ziemlich simpel (Widgets verwende ich dort nicht).

Nachtrag für das erschienene Vistual Studio 2008 SP1 Beta

Hier noch der passende KB Artikel für die SP1 Beta:
http://support.microsoft.com/kb/945140/en-us

Es finden sich in diesem Artikel noch einige nützliche Informationen. Unter anderem auch die Links auf die Liste der Änderungen.

Besonders möchte ich hier darauf hinweisen, dass die Links, die veröffentlicht wurden alles Online-Installer sind.
Wer nun lieber ein komplettes Image haben möchte der findet in diesem KB-Artikel die entsprechenden Anweisungen, wie man eine volles Installationspaket bekommt.

Visual Studio 2008 Service Pack 1 Beta wurde gestern veröffentlicht

Die Beta des SP1 für Visual Studio 2008 wurde getsern veröffentlicht und steht zum Download zur Verfügung!

We are excited to pre-announce the availability of the the Visual Studio 2008 Service Pack 1 Beta download.

Visual Studio 2008 SP1 delivers various improvements to Visual Studio 2008 such as support SQL Server 2008 and new ADO.NET features such as the Entity Framework, numerous improvements to the WPF designers, WCF Templates for Silverlight projects, debugger support for the .NET Framework public symbols and source release, control improvements and additions (such as the DataRepeater for Windows Forms and Office 2007 Ribbons for C++), and a number of general debugging and Intellisense updates. This Service Pack also includes fixes to improve the stability, performance and security of many areas of the product. Due to the release schedule of this product, and the fact we have a very short feedback window, we would love to hear from our targeted community members 

Downloads

Hotfix für UseMSPrivateAssemblies.h und VC-2008

Einige nutzen ja meine Lösung für private CRT und MFC Assemblies unter VC-2005, die ich in dem diesem Artikel unter Codeproject veröffentlicht habe
http://www.codeproject.com/KB/cpp/PrivateAssemblyProjects.aspx

Das Interesse und die Nachfrage ist groß dieses Verfahren auch unter VC-2008 zu nutzen.
Da ich aber aktuell wenig Zeit habe den Artikel komplett zu überarbeiten, veröffentliche ich den relevanten Code hier erst mal vorab als „Hotfix“. Dieser Hotfix setzt voraus, dass das aktuelle Feature Pack installiert ist. Der Code ist nicht auf die RTM Version hin zugeschnitten und getestet.

UseMSPrivateAssemblies.h

// Version 2.0 by Martin Richter [WWJD]
// Supports VC-2005 and VC-2008
#pragma once    

#ifndef RC_INVOKED
// Avoid problems with the resource compiler if included    

// This defines bock the creation in the header files
#pragma message("Using private assemblies for the MS runtimes")
#define _STL_NOFORCE_MANIFEST
#define _CRT_NOFORCE_MANIFEST
#define _AFX_NOFORCE_MANIFEST
//#define _ATL_NOFORCE_MANIFEST    

// The next statements block the linker from including object files in the
// CRT and the MFC, that would create manifest pragmas too.
#ifdef __cplusplus
extern "C" {            /* Assume C declarations for C++ */
#endif    

__declspec(selectany)       int _forceCRTManifest;
__declspec(selectany)       int _forceMFCManifest;
// __declspec(selectany)    int _forceAtlDllManifest;    

// The next symbols are used by the several versions of VC 9.0
__declspec(selectany)       int _forceCRTManifestRTM;
__declspec(selectany)       int _forceMFCManifestRTM;
__declspec(selectany)       int _forceMFCManifestCUR;    

#ifdef __cplusplus
}                        /* __cplusplus */
#endif    

// We use crtassem.h with the defines there. It just gives us the
// versions and name parts for the dependencies.
// Note that there is also a MFCassem.h but this include file has the
// manifest pragma's already in it. So we can't use it
//
// Three files are controlling this crtassem.h, MFCassem.h and atlassem.h!
// Happily __LIBRARIES_ASSEMBLY_NAME_PREFIX is used in CRT, MFC and ATL!
// Doing it right would need to use _MFC_ASSEMBLY_VERSION for the MFC
// but in fact _CRT_ASSEMBLY_VERSION and _MFC_ASSEMBLY_VERSION and
// _ATL_ASSEMBLY_VERSION are the same
//  - VC-2005 SP1 8.0.50727.762
//  - VC-2008 RTM 9.0.21022.8
//  - VC-2008 Feature Pack 9.0.30411.0 (used if _BIND_TO_CURRENT_VCLIBS_VERSION
//    and _BIND_TO_CURRENT_MFC_VERSION are defined to 1)    

#include <crtassem.h>

// We don't have a seperate block for the Debug version. We just handle
// this with a extra define here.
#ifdef _DEBUG
#define __LIBRARIES_SUB_VERSION    "Debug"
#else
#define __LIBRARIES_SUB_VERSION    ""
#endif    

// Manifest for the CRT
#pragma comment(linker,"/manifestdependency:\"type='win32' "                        \
    "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX "." __LIBRARIES_SUB_VERSION "CRT' "   \
    "version='" _CRT_ASSEMBLY_VERSION "' "                                          \
    "processorArchitecture='x86' \"")    

// Manifest for the MFC
#pragma comment(linker,"/manifestdependency:\"type='win32' "                        \
    "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX "." __LIBRARIES_SUB_VERSION "MFC' "   \
    "version='" _CRT_ASSEMBLY_VERSION "' "                                          \
    "processorArchitecture='x86'\"")    

// #pragma comment(linker,"/manifestdependency:\"type='win32' "                     \
//     "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".MFCLOC' "                        \
//     "version='" _CRT_ASSEMBLY_VERSION "' "                                       \
//     "processorArchitecture='x86'\"")    

// Manifest for the ATL
// #pragma comment(linker,"/manifestdependency:\"type='win32' "                     \
//    "name='" __LIBRARIES_ASSEMBLY_NAME_PREFIX ".ATL' "                            \
//    "version='" _CRT_ASSEMBLY_VERSION "' "                                        \
//    "processorArchitecture='x86' \"")    

#endif // RC_INVOKED

Anmerkungen:

  • Im Endeffekt sind nur 3 Zeilen (26-28) hinzugekommen.
  • Diese Version funktioniert sowohl für VC-2005 als auch VC-2008!
  • Unter Vista wird allgemein das Problem beobachtet, das private Assemblies nur genutzt werden können, wenn diese in einem Unterverzeichnis liegen. Liegen die Assembly Dateien im gleichen Verzeichnis wie die EXE kommt es zu einem Fehler „The application failed to initialize properly (0xc0000034). „ Dieser Sache bin ich (und andere) auf der Spur.
  • Es spielt für diesen Code keine Rolle ob die beiden Defines _BIND_TO_CURRENT_VCLIBS_VERSION und _BIND_TO_CURRENT_MFC_VERSION gesetzt wurden. Werden diese Defines auf 1 gesetzt bevor UseMSPrivateAssemblies inkludiert wird, dann werden die Manifeste so erzeugt, dass die Feature Pack DLLs gezogen werden. Sind diese beiden Defines nicht gesetzt werden Manifeste für die RTM Version erzeugt.
    Ich empfehle dringend diese beiden Defines zu setzen ❗

Das ist erstmal ein Schnellschuss für alle, die die es etwas eiliger haben.

Der Vorteil gegenüber der Lösung, bei der die Manifeste manuell bearbeitet werden, wie es zum Beispiel Jochen Kalmbach in seinem Blog vorgestellt hat ist klar:
Man muss eben nichts manuell machen 🙂
Es macht wieder alles der Compiler und Linker.

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.

Tipps & Tricks: Testcode sollte immer in #ifdef _DEBUG #endif Blöcke integriert sein!

Dieser Tipp hört sich trivial an, aber wenn man sich nicht dran hält erlebt man übelste Überraschungen.
Nur zu oft muss man während der Entwicklung oder bei der Fehlersuche Code einbauen, der Test, Ausgaben, Verzögerungen oder sonstige Operationen ausführt, die mir als Entwickler helfen ein Problem zu finden, oder einer Lösung für eine verzwickte Frage zu lösen. Um so größer das Problem wird um so mehr Stellen werden oft verändert.
Es bleibt die Frage ob sich jeder Entwickler noch erinnert wo er überall etwas für Testzwecke eingebaut hat.

Das üble Ergebnis ist, dass man manchmal toten nutzlosen, Performance fressenden Code ausliefert. Oder gar Code ausliefert, der evtl. zu neuen Fehlern führt. Da muss nur ein einfacher DebugBreak im Code zurückbleiben und schon crashed die Anwendung sauber beim Kunden…

Testcode sollte grundsätzlich in einem #ifdef Block eingebaut werden. Und Code der wirklich nur für Tests vorhanden ist und er sogar später in der Debug Version des Programmes nichts zu suchen hat sollte mit einem #else #error versehen werden. Ein ASSERT kann einen viel abnehmen um so etwas zu vermeiden, aber sogar mancher ASSERT  ist später überflüssig und behindert auch Tests in der Debug Version.

So habe ich in unserer Software einen Sleep(100); 😮 gefunden, der von einem Entwickler eingebaut wurde, um einen Crash in einem komplexen Kommunikationsproblem zwischen mehreren Threads zu finden.Er hat den Fehler gefunden, den Sleep aber nie wieder entfernt.
Hätte mein Kollege sich an meine Spezifikationen gehalten, hätten wir nicht nachträglich auf die mühsame Suche gehen müssen wo unsere Performanceverluste bei 5% Prozessorlast herkommen. So wäre das Ganze schon beim Release-Build aufgefallen:

#ifdef _DEBUG
// Test Sleep to find cross thread problems for bug#234
Sleep(100);
#else
#error Remove test condition here. Just used to find bug#234 
#endif

BTW: Ich verwende deshalb immer ein Code-Snippet über mein VisualAssist X, der mir einen entsprechenden Codeblock einsetzt.