virtuelle Funktionen und const sind immer wieder eine Freude… oder wie C++0x einen Fehler verhindert hätte…

Wieder mal hat sich in unserer Software ein kleiner und ganz mieser Fehler eingeschlichen, obwohl der Entwickler „eigentlich“ an alles gedacht hatte.

class A
{
...
    virtual void Doit() const;
...
};

class DerivedA : public A
{
...
    virtual void Doit(); 
...
};

void Foo()
{
...
    A *pA = Something();
...
    pA->Doit();
...
}

Sieht alles gut aus. Leider fehlt das kleine nette Wort const beim Überscheiben der Funktion Doit und daraus folgt, dass DerivedA::Doit niemals aufgerufen wird.

Obwohl wir VC-2010 verwenden, hat der neue C++0x Standard noch keinen Einzug in unseren Köpfen gefunden.
Das kleine Wort override in DerivedA hätte diesen Fehler verhindert.

class DerivedA : public A
{
...
    virtual void Doit() override;
// This line results in:

// error C3668: 'DerivedA::Doit' :
// method with override specifier 'override' did
// not override any base class methods
...
};

Es wird wirklich Zeit, dass C++0x auch wirklich genutzt wird, aber wie alles Neue muss man es sich besonders am Anfang immer wieder bewusst machen, damit es einem direkt zur Angewohnheit wird.
override ist sogar schon in VC-2008 und dem nativen Compiler implementiert. Allerdings wundert es mich, dass hier nicht dann auch gleich das Schlüsselwort new eingeführt wurde.
Aber das man unter dem gleichen Namen wirklich einen neuen Slot in der vtable aufmacht ist wirklich eher selten. Ich persönlich hatte dafür bisher noch keine Verwendung. 

Leider ist C++0x  nicht komplett in der C++ Doku in der MSDN hervorgehoben. Man kann nicht mal erkennen, dass sealed und abstract MS-Extensions sind, und override im Standard verankert ist. Schade…

Damit man sich etwas besser einen halbwegs kompletten Überblick verschaffen kann habe ich hier ein paar Links zusammengestellt.

Siehe auch:

C:\WINDOWS\TEMP und die Tücken von Programme der Kategorie „es war einmal“

Es waren einmal die Zeiten in denen man in C:\WINDOWS\TEMP einfach mal eine Datei anlegen konnte und jeder darauf zugreifen konnte.
Seit Windows Vista hat sich ja einiges getan was Sicherheit betrifft, besonders auch die Rechtevergabe auf Dateien, die im C:\Windows\Temp Ordner angelegt werden.

Ein Programm  im Rentenalter (es ist gerade mal so um die 16 Jahre alt 😉 geschätzt) erzeugte in C:\WINDOWS\TEMP eine Datei, mit der bestimmte Zugriffe abgesichert wurden. Darunter auch wenn mehrere Instanzen des Programms auf einem Rechner liefen. Das funktionierte prima. Die Datei wurde unter einem festen Namen angelegt und nicht entfernt, nachdem das Programm beendet wurde.

Seit Vista gibt es aber nun ein kleines Problem.:
Seit Windows Vista darf in C:\WINDOWS\TEMP immer noch jeder User Dateien erzeugen. Auf diese Dateien hat er auch vollen Zugriff. Aber auf diese Dateien hat kein anderer Nutzer mehr Zugriff… 😮 … und selbst ein Admin muss erst hier erst den Besitz übernehmen, wenn er die Datei nicht erzeugt hat.

Und nun hat diese kleine alte Programm den folgenden Effekt:

  • Benutzer A meldet sich an.
  • Benutzer A startet das Programm Er arbeitet damit und die temporäre Datei wird in C:\WINDOWS\TEMP angelegt.
  • Benutzer A meldet sich ab und beendet das Programm.
  • Benutzer B meldet sich an.
  • Benutzer B startet das Programm und … bekommt eine Fehlermeldung mit einem „Access denied!“.

Mit den neuen Rechten, die auf dem C:\WINDOWS\TEMP Verzeichnis liegen, kann der zweite Benutzer auf diese Datei nicht mehr zugreifen auf die eben nur der Erzeuger Zugriff hat, und der ist eben Benutzer A.

PS: Es Frage mich keiner warum C:\WINDOWS\TEMP aus GetWindowsDir und angehängtem Text TEMP zusammengesetzt wurde. Vermutlich um zu umgehen, dass ein privates temporäres Verzeichnis benutzt wird. Tja und CSIDL_APPDATA war dem damaligen Entwickler (evtl. noch unter Windows 3.1) nicht bekannt.

Tipps & Tricks: TFS Administration Tool 2.1

Mit der Benutzerverwaltung im TFS kann man viel unnütze Zeit verbringen.
Jeder kennt das Problem der schon mal mehrere Anwender in einem TFS-Projekt verwalten musste. Durch die Dreiteilung der Rechte muss man:

  • Rechte auf dem TFS verwalten (Team Foundation Roles)
  • Rechte auf dem SharePoint Server verwalten (SharePoint Roles)
  • Rechte auf dem SQL Reporting Server verwalten (Reporting Services Roles)

Wenn also was nicht klappt, weil einem zum Beispiel ein Report nicht angezeigt wird, ist man gleich wieder am grübeln: Wo war das nochmal mit den Reporting Services? Welcher Server, welche Webadresse? Gleiches, wenn man ein Benutzer keinen Zugriff auf die Dokumente in der SharePoint Site hat, die zu einem Projekt angelegt wurde.

Ich fühle mich eigentlich eher als Entwickler, denn als Administrator von einem SharePoint Server oder SQL-Reporting Services Server.

Das Ganze wird um ein vielfaches einfacher durch das TFS Administration Tool 2.1.
Hier kann man auch einfache Art in einem Tool alle Rechte auf einmal einsehen und verwalten. Netterweise wird man in der Listenanzeige sofort durch rote Kästchen auf die Konten aufmerksam gemacht, in denen keine Rechte definiert wurden. In den meisten Fällen ist hier auch schon ein grundsätzliches Problem gefunden.

Das Tool lässt sich auf jedem Client installieren auf dem der Team-Explorer auch installiert wurde.

Link: http://tfsadmin.codeplex.com/

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

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.

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?

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.

BUG: VS-2010 vergisst regelmäßig die Tastaturkürzel für Extensions und Addins

 Toll ist, das es für VS-2010 wirklich viele nützliche Extensions gibt. Das heißt aber auch, dass man des öfteren mal ein Update einer solchen Extension installiert. Und jetzt wird es ärgerlich. Jedesmal wenn man ein Update einer Extension installiert dann sind alle Tastaturzuordnungen auf ALLE Extensions und Addins futsch.

Mich hat das so genervt, dass ich einen entsprechenden Bugreport eingereicht habe:
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=621929

Wie man lesen kann: „A known issue!“. Na dann hoffen wir mal auf einen Hotfix… 😉 (also bitte gebt Eure Votes ab), wie immer stirbt die Hoffnung zuletzt..

Ich kann jedem nur raten, die eigenen Einstellungen des Visual-Studios über die Import/Export Funktionen regelmäßig zu sichern. Dann kann man bei Bedarf auch die Tastaturmappings alleine wieder zurückspielen…

TFS-2010 zeigt nicht alle Historien Einträge für Dateien

Ein Kunde meldete ein Problem und ich entdeckte einen Fehler. Ich wollte wissen wann dieser Fehler in die Software kam oder ob er evtl. schon immer vorhanden war. Also bemühte ich die Historie des Team Foundation Servers. Allerdings erschien mir diese nicht komplett.

Vor ca. 2 Jahren habe ich einige Projekte umstrukturiert und dabei auch Verzeichnisse verschoben. Und genau bis zu diesem Punkt konnte ich die Historie einsehen.

Nach ein bisschen Recherche im Netz fand ich diesen Report auf Connect:
https://connect.microsoft.com/VisualStudio/feedback/details/538032/tfs-2010-does-not-display-history-for-a-renamed-folder
Und in dieser Meldung fand ich dann auch den entsprechenden Workarround.

Erst der Befehl:
tf.exe history  „C:\Dev\Root\Projects\the file name I am looking for.cpp“   /itemmode
zeigt die komplette Historie:

Frag mich bitte keiner warum das so sein muss 😉 Slotmode / Itemmode? Brauchen wir das wirklich?