Minidumps ganz einfach

Manchmal, wenn man ein kleines Programm entwickelt mag es als Overkill erscheinen extra Code für Minidumps einzubauen.
Was aber, wenn man doch einen Fehler aufspüren möchte und ein Minidump ad hoc ganz praktisch wäre?

Unter Vista und Windows 7 ist es ganz einfach in den WER Einstellungen Einträge vorzunehmen, mit denen man mit nur ein paar Registry Einträgen sofort zu Minidumps kommt.

Nachfolgend die Registry Einträge, die einen Fulldump im Verzeichnis %LOCALAPPDATA%\CrashDumps erzeugen.

Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps]
"DumpType"=dword:00000001
"DumpCount"=dword:00000010
"DumpFolder"=hex(2):25,00,4c,00,4f,00,43,00,41,00,4c,00,41,00,50,00,50,00,44,\
00,41,00,54,00,41,00,25,00,5c,00,43,00,72,00,61,00,73,00,68,00,44,00,75,00,\
6d,00,70,00,73,00,00,00

Eine vollständige Liste der Einstellungen findet sich in der MSDN Doku:
http://msdn.microsoft.com/en-us/library/bb787181(VS.85).aspx

PS:
Ich benutze diese Einstellungen aktuell auch einem Bug in VisualStudio 2010 auf die Spur zu kommen, damit ich Crashdumps regelmässig an http://connect.microsoft.com übertragen kann.

PPS: (14.01.2011 nach Hinweis von André)
Wie man auch der Doku ennehmen kann ist fpr dieses Funktion Vista SP1, Windows 2008 Server oder Windows 7 notwendig. Vista RTM hat diese Funktion nicht.

BUG: VC-2010 ftell liefert negative Werte wenn eine UTF-8 Datei mit ccs=UNICODE geöffnet wird, die nicht mit fseek funktionieren

In VS-2005 hielt die Unicode und UTF-8 Unterstützung Einzug in die CRT und damit auch in die MFC. Jochen und ich haben dazu gebloggt.

Leider ist die Implementierung nicht fehlerfrei. Wenn man eine UTF-8 Datei mit ccs=UNICODE öffnet dann liefert ftell zum Teil auch negative Werte. Das ftell nicht die exakte Position in der Datei liefert ist dokumentiert, nur sollte es mit dem Wert zumindest möglich sein wieder fseek aufzurufen um an eine alte Dateiposition zurück zu gelangen.
Das ist mit der jetzigen Implementierung nicht möglich.

Ein Sample dazu ist schnell gebaut:

int _tmain()
{
  FILE *file = NULL;
  if (_tfopen_s(&file,_T("Test.txt"),_T("rt") _T(", ccs=UNICODE")))
    return -1;

  TCHAR szBuffer[_MAX_PATH];
  while (_fgetts(szBuffer,_countof(szBuffer),file))
  {
    int iPos = static_cast(ftell(file));
    _tprintf(_T("%d\n"),iPos);
    _ASSERTE(fseek(file,iPos,SEEK_SET)==0);
  }

  fclose(file);
  return 0;
}

Ein entsprechendes Sample kann man hier herunterladen.

Der Fehler betrifft natürlich auch die MFC mit CStdioFile und GetPosition!

Hintergrund:
Ich kam auf den Fehler, weil wir ein Programm benutzen, dass sich bestimmte Dateipositionen merkte um einen Datenimport bei einer bestimmten Bedingung, ab einer definierten Stelle, neu aufnehmen zu können. Bisher hatten wir hier nur pure ASCII Dateien erlaubt, jetzt wollten wir auch UTF-8 Dateien unterstützen.
Sicher man kann auch alles im Speicher zwischenhalten, aber wenn es eine simple Dateiposition ist das Ganze so weitaus Ressourcen schonender.

Soweit ich das übersehen kann, betrifft der Fehler alle VS-Versionen ab VS-2005. Ein entsprechende Bug wurde von mir schon vor längerer Zeit auf Connect geöffnet. Getan hat sich bisher noch nichts:
https://connect.microsoft.com/VisualStudio/feedback/details/591030/ftell-returns-negative-value-for-utf-8-files-opened-with-textmode-and-ccs

So lange wollte eigentlich nicht warten…

Wie gut das manche Anzeigen einfach nicht stimmen:

In einer Teamviewer Session bekam ich beim übertragen der Datei, den Hinweis, dass es ca. 357.020 Jahre dauern wird bis die Datei übertragen ist 😮
Das Ganze obwohl der Fortschrittsbalken zügig voran geschritten ist. Das Ganze hatte wirklich nur 25 Sekunden gedauert und ich musste mich sputen das Bild einzufangen.

Berechnungen über die Dauer von Operationen führen immer wieder zu erstaunlichen Ergebnissen in unserer Branche…

TFS Fehler TF248015…

Ein nettes Hilfsmittel ist die Work-Item Suche mit dem Addin http://visualstudiogallery.msdn.microsoft.com/de-de/3f31bfff-5ecb-4e05-8356-04815851b8e7

Leider funktionierte dies bei mir nicht sofort. Ich bekam die folgende Fehlermeldung:

TF248015: Your work item query could not be completed due to an unexpected error returned by the Microsoft SQL Server Full-text engine. Contact your system administrator to check the Application event log for Team Foundation Server.

Na fein ❗ Also muss ich mich selbst kontaktieren weil irgendwas mit der Volltext Suche nicht klappt.

Nach einigem hin und her irren durch alle möglichen Einstellungen entdeckte ich den Missetäter: Der „SQL Full-text Filter Daemon Launcher“ war nicht gestartet. Dies ist ein Dienst, den man in der Computer-Verwaltung oder dem SQL-Server-Konfigurations-Manager findet.
Aus irgend einem mysteriösen Grund war hier ein falsches Dienstkonto angegeben und der Dienst konnte nicht starten.

Nachdem das korrigiert war habe ich danach noch ein Rebuild des Warehouse veranlasst damit die Indizes sofort zur Verfügung aktuell sind. Das erfolgt über die folgende Web Adresse: http://servername:8080/tfs/TeamFoundation/Administration/v3.0/WarehouseControlService.asmx 
in dem man ProcessWarehouse auswählt. Unter collectionName gibt man seine Team Project Collection an um die es geht. Wer die Namen der Collections vergessen hat, findet sie in der Team Foundation Server Administration Console aufgelistet.

BUG: VS-2005 langsam durch extreme Anzahl von Dateien im WebsiteCache

 Für einige Legacy-Anwednungen habe ich virtuelle Maschinen mit VC6 und VS-2005.

Bei der VS-2005 Maschine hatte ich in er letzten Zeit das Gefühl, dass die Kiste immer langsamer wird. Also ging ich etwas auf die Suche und fand schließlich die Ursache.

In dem Verzeichnis C:\Users\Name\AppData\Local\Microsoft\WebsiteCache fanden sich nicht weniger als 50000 (in Worten Fünfzigtausend Verzeichnisse) unscheinbar brachte jedes Öffnen einer Solution ein weiteres Verzeichnis für jedes Projekt in der Solution.

Ein bekannter Bug, wie ich nach weiterer Recherche herausfand:
https://connect.microsoft.com/VisualStudio/feedback/details/347228/large-numbers-of-websitecache-files-slowing-visual-studio-2005-and-windows-performance?wa=wsignin1.0

Nachdem ich die Verzeichnisse entsorgt hatte, war auch wieder die gewohnte Geschwindigkeit da.

Es bringt so gut wie nichts andere Antennen in WLAN Geräte einzubauen…

Ich habe meinen WLAN-Router in meinem Arbeitszimmer, dass sich an der einen Hausecke befindet.
Im Wohnzimmer habe ich immer noch einigermaßen Empfang. Um auf der Terrasse im Sommer surfen zu können und damit unsere Tochter in Ihrem Zimmer WLAN Empfang hat, habe ich einen Fritz!Repeater (den man nur empfehlen kann).

Um die Grundreichweite meines WLAN-Routers zu vergrößern habe ich mir eine 9dbi WLAN Antenne gekauft und die kleine WLAN Antenne getauscht. Der Erfolg war minimal. Maximal 3-5% Verbesserung. Weil mir das zu wenig vorkam, habe ich den Versuch gleich noch an einem zweiten unterschiedliche WLAN-Router, den ich noch im Haus hatte wiederholt. Der Tausch der Antenne war bei beiden gleich einfach. Die Schwierigkeit war eher die Gehäuse zu öffnen.

Resultat:
Antennentausch lohnt sich nach meiner Meinung nicht, besser das Geld in die Spardose schmeißen für einen guten Single-Malt oder so 😉
Das einzige was ich positiv anmerken kann, ist dass sich die Konstanz des WLANs verbessert hat. Vorher hatte ich ab und zu schon etwas Schwankungen im Netz. Das hat sich wirklich gebessert.

Beta Version von VS-2010 SP1 veröffentlicht

Es wurde ja schon einiges geredet über VS-2010 SP1. Jetzt ist die Beta 1 veröffentlicht worden.
Ich kann leider noch nicht sagen, was sich für VC Nutzer ändert. Der Blog Artikel (s.u.) gibt darüber keine Auskunft.

Mehr Infos hier:
http://blogs.msdn.com/b/jasonz/archive/2010/12/07/announcing-visual-studio-2010-service-pack-1-beta.aspx

Warum es manchmal nicht genügt die Basisklasse aufzurufen und die miesen Konsequenzen…

Wenn man eine Windowsnachricht bearbeitet dann ruft man in den meisten Fällen die Funktion der Elternklasse auf. Was aber, wenn man die Nachricht nicht nur entgegennehmen will, sondern sie „verändert“ an die Basisfunktion weitergeben will. Was dann?
Kein Problem denkt man. Man ändert die Parameter eben und gibt diese neuen Werte an die Basisklasse weiter.

Nehmen wir mal als Beispiel ein Eingabe Control, das sich möglichst intelligent verhalten soll. D.h. in diesem Fall, wenn ein Datum eingegeben wird, dann sollte auch bei der Eingabe auf numerischen Ziffernbock, das Komma in einen Punkt gewandelt werden, oder bei englischem Datumsformat in einen Slash…

Na OK. Man überschreibt als CMyWnd::OnChar. Man prüft ob ein bestimmtes Zeichen ankommt und wenn es mir passt, dann ändere ich den Parameter und gebe diesen dann an die Basis Funktion weiter.

:Eeek: aber was ist das? Der Code hat keine Wirkung? Egal was wir an die Basisfunktion übergeben, es ändert sich nichts. Warum?

Die Antwort liegt in dem für mich etwas eigentümlichen Design der MFC. Alle CWnd Nachrichten Routinen rufen letzten Endes immer genau ein und die selbe Funktion auf: CWnd::Default(); Aber was macht CWnd::Default? Es nimmt die ursprünglichen Parameter, die Windows mal gesendet hat und übergibt die an die Window-Proc des Fensters. D.h. alle tollen Manipulationen an der WM_CHAR Nachricht sind weg in dem Moment in dem CWnd::OnChar aufgerufen wird.

Was also tun, wenn man nun aber wirklich die WM_CHAR Nachricht manipulieren will?
Eigentlich nicht schwer. Man macht das Gleiche, dass eben auch CWnd::Default macht. Man ruft DefWindowProc mit den passenden wParam und lParam Werten auf.

Aber jetzt wird es dumm ❗ Damit umgehen wir alle anderen geerbten Funktionalitäten des Controls. Ich habe keine Ahnung was die Entwickler der MFC in der Anfangszeit dazu getrieben hat immer CWnd::Default aufzurufen? War es Ihnen zu kompliziert wieder aus den Parametern wParam und lParam zusammen zu bauen?

Manifeste sind nicht alles, DLLs und COM Objekte benötigen ISOLATION_AWARE_ENABLED oder ein eigenes Activation Context Handling

Ich habe eine DLL die einiges an Datenbank I/O für ein Produkt übernimmt. Diese DLL sollte in einem Service eingesetzt werden.
Eigentlich ist diese DLL immer nur Teil eines vollen UI Projektes und hat auch einige kleine visuelle Komponenten. Entsprechend prüft eine Init Funktion bestimmte Voraussetzungen, die für das korrekte Arbeiten notwendig sind wie: mindestens MS-XML 3.0, IE ab Version 5.1, Common Control 6.0 (also mit Manifest).
Soweit so gut. Die DLL selbst wurde mit einem Manifest (Typ 2) versehen und in den Service mit eingebaut.

Eigentümlicherweise startete der Service nicht. Die Init Funktion meldete immer, dass die Requirements nicht gegeben wären. Was eigentlich nicht sein kann, weil der selbe Code in einem anderen Kontext eines GUI Programms perfekt funktioniert.

Ein wenig debuggen zeigte, dass die Common Control 6.0 nicht gefunden wurden.
Eigentlich kein Wunder. Der Service hat keine UI, es gibt keinen Code der die Common Control 6.0 verwendet, allerdings hatte meine DLL ja ein entsprechendes Manifest für die Common Control 6.0 DLL.
Aber der Versionstest in der DLL lädt immer die 5er Version, also die Version ohne Manifest.

Es dauerte eine Weile bis es bei mir klingelte und ich verstand worin das Problem lag:

  • Wenn man ein Manifest in einer DLL mit dem Code 2 einträgt (ISOLATIONAWARE_MANIFEST_RESOURCE_ID), dann funktioniert dieses Manifest wenn in diesem Moment die ComCtl32.dll implizit geladen werden sollte.
  • In meinem Fall aber klappte das nicht. Die DLL wurde nicht implizit geladen. Die Versionkontrolle später führte LoadLibrary direkt durch und führte dann DllGetVersion aus (mit GetProcAddress ermittelt).
  • In meinem Fall bedeutet das aber, das LoadLibrary im Aktivierungskontext (Activation Context) der EXE ausgeführt wird, und die hat kein ComCtl32 Manifest für die Version 6.0. Es ist ja auch nur ein Service.
  • Damit es funktioniert muss man in jedem Fall auch den folgenden Define setzen.
#define ISOLATION_AWARE_ENABLED 1
  • oder man muss dafür sorgen, dass man seinen eigenen Aktivierungskontext nutzt. Sprich seine Manifeste, die man selber möchte.
  • Durch diesen define, werden bestimmte Funktionen wie CreateWindow, LoadLibrary, CoCreateInstance, SendMessage und andere durch einen inline-Code ersetzt der einen Aktivierungkontext benutzt und die aktuellen Manifeste der DLL bestimmt und benutzt.

In meinem Fall hieß die Lösung nicht LoadLibrary zu benutzen sondern GetModuleHandle und wenn eben ComCtl32 gar nicht im Speicher ist, also auch nicht benutzt wird auch keinen weiteren Test durchzuführen, aber zur Sicherheit habe ich auch ISOLATION_AWARE_ENABLED gesetzt.

Siehe auch:
http://msdn.microsoft.com/en-us/library/aa376607(VS.85).aspx
http://msdn.microsoft.com/en-us/library/aa375197(VS.85).aspx

ComboBox DropDown Höhe wird nicht mehr durch die Ressourcen definiert

Vor Jahren habe ich für die microsoft.public.de.vc FAQ den folgenden Beitrag geschrieben:
Warum klappt meine ComboBox im DropDown-Stil in einem Dialog nicht auf?

Beim Erstellen einer ComboBox in einem Dialog Template muss auch die Größe mit angegeben werden, die die ComboBox haben soll, wenn Sie denn aufgeklappt wird. Dies kann auf zwei Methoden geschehen.

Methode 1: ComboBox aus der Werkzeugleiste einfach durch einen Mausklick einsetzen. Anschließend auf den „DropDown“-Schalter klicken und nun die gewünschte Größe einstellen.

Methode 2: ComboBox durch Ziehen eines Rechteckes auf dem Dialog einsetzen. In diesem Fall wird die Größe gleich korrekt bestimmt. Nachträgliche Änderung der Größe erfolgt dann wieder durch anklicken des „DropDown“-Schalters.

Anmerkung: Die Größe einer ComboBox mit dem Stil CBS_DROPDOWN und CBS_DROPDOWNLIST im NICHT aufgeklappten Zustand kann beim Erzeugen nicht verändert werden. Diese Größe bestimmt Windows automatisch. Die Größe die bei CreateWindow/CreateWindowEx angegeben wird ist immer die Größe des Control im aufgeklappten Zustand. Nachdem ein gültiger Windowshandle auf die ComboBox existiert kann mit CComboBox::SetItemHeight die Höhe der Items bzw. des Editfelds der ComboBox verändert werden.

Jetzt habe ich entdeckt, dass dieser Beitrag eigentlich überflüssig geworden ist, seit dem es COMCTL32 in der Version 6.0 gibt.
Wenn ein Manifest für die 6.0 Version der Common Controls vorhanden ist, dann bestimmt die COMCTL32 DLL automatisch selbst anhand der Höhe des Monitors und der Position der ComboBox wie groß der DropDown-Bereich sein kann.

Aufgefallen ist mir das, als ich in einer RC-Datei sah, dass eine ComboBox mit der Höhe gerade einmal 20 DLUs angegeben wurde. Da in der RC Datei normalerweise immer nur die DropDown-Höhe eingetragen ist, fragte ich mich warum bisher niemandem aufgefallen war, dass diese ComboBox, nicht aufklappt. Ein kurzer Test, zeigte allerdings, dass alles normal war und die Box, den halben Monitor in der Höhe einnahm.
Ein weiter Test mit und ohne Manifest zeigte mir dann schnell, dass sich das Standardverhalten von Comboboxen offensichtlich verändert hat.

Nachtrag (07.01.2011):
Nur die neuen Common Controls ab Vista und Windows 7 verhalten sich wie oben beschrieben. Windows XP (einschließlich SP3) verhält sich noch gemäß der MSDN WinAPI Doku. D.h. die Höhe wird nicht automatisch angepasst. Man kann sich eben auf nichts verlassen.