Archiv der Kategorie: Softwareentwicklung

Visual Studio 2015 Update 1: TargetUniversalCRTVersion wird nicht aktualisiert

Nach der Installation vom Update 1 für Visual Studio 2015 profitiert man evtl. nicht von der einen oder anderen Korrektur in der Universal Runtime, weil die Versionsnummer des Windows SDKs in den Konfigurationsdateien der Build-Tools nicht von 10.0.10240.0 auf 10.0.10586.0 hochgesetzt wird und somit die falschen (d.h. veralteten) LIB-Dateien vom Linker herangezogen werden.

Um die Versionsnummer manuell zu korrigieren geht man (mit Adminrechten) folgendermaßen vor:

  • Eine Sicherung der Datei „%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\V140\Microsoft.Cpp.Common.props“ anlegen.
  • Das Schreibschutzattribut der Datei entfernen.
  • Die Datei bearbeiten, nach dem Eintrag für „TargetUniversalCRTVersion“ suchen und den Wert von 10.0.10240.0 auf 10.0.10586.0 hochsetzen.

Visual Studio 2015: Funktion „stat“ inkompatibel mit Windows XP/2003

Ja, Windows XP und Windows Server 2003 werden von Microsoft nicht mehr unterstützt, aber als Anwendungsentwickler muss man alte Betriebssystemversionen leider manchmal noch etwas länger unterstützen, weil der eine oder andere Kunde „etwas“ länger zur Migration benötigt.

Erfreulicherweise liefert Microsoft mit Visual Studio 2015 weiterhin Build-Tools für Windows XP/2003 mit, allerdings hat sich in der Laufzeitbibliothek zum Windows SDK 10.0.10240.0 (mindestens) ein Fehler eingeschlichen, der den Windows XP/2003 Support etwas eischränkt:

Die Funktion „stat“ zur Ermittlung von Dateieigenschaften (Größe, Zeitstempel, Attribute, …) läuft unter Windows XP/2003 für Dateien grundsätzlich in einen Fehler und errno steht danach nichtssagend auf EINVAL. Erst ein Blick auf den Rückgabewert der Windows-API-Funktion „GetLastError“ liefert ERROR_CALL_NOT_IMPLEMENTED.

Ein Blick in den Quellcode der Universal Runtime erklärt das Problem: Die intern verwendete Hilfsfunktion „common_stat_handle_file_opened“ benutzt die Windows-API-Funktion „GetFileInformationByHandleEx“, die es erst ab Windows Vista/2008 gibt.

Mit dem Visual Studion 2015 Update 1 bzw. im Windows SDK 10.0.10586.0 hat Microsoft diesen Fehler korrigiert und benutzt die ältere Windows-API-Funktion „GetFileInformationByHandle“.

Warum das korrigierte Windows SDK evtl. trotz Installation des Update 1 nicht zum Einsatz kommt, erkläre ich in einem separaten Blog-Beitrag.

Zulässige Windows-Anwendung oder nicht?

Aus der Kategorie „unglaublich“ kommt heute eine Problemmeldung eines Kunden. Sein Windows Server 2008 scheiterte, beim Versuch einen bestimmten Dienst (nennen wir ihn hier XYZ) zu starten, und schrieb den Fehlercode „%%193“ mit der Meldung „XYZ ist keine zulässige Win32-Anwendung“ ins Ereignisprotokoll.

Spontan denkt man natürlich an ein beschädigtes Binary, wobei da ja bereits der Installer hätte aufschreien müssen, aber zur Sicherheit prüft man es und ruft das Binary auf der Konsole im Debug-Modus auf und… es läuft anstandslos.

Das Rätsels Lösung ist ein Windows-Fehler der unter Q325680 bei Microsoft bekannt ist: Wenn der Pfad, unter dem ein Dienst installiert ist, mit einem Wort (!) beginnt und es eine Datei im Wurzelverzeichnis das Datenträgers gibt, die dasselbe Wort als Name hat, dann kann der Dienst nicht gestartet werden.

In diesem Fall war der Dienst unter „C:\Program Files (x86)\ABX\XYZ.exe“ installiert und es gab – von einem anderen Programm – eine Logdatei „C:\Program“. Für Windows Grund genug um den Dienst XYZ nicht mehr starten zu wollen.

MS-SQL-Server: Schlechte DELETE-Performance

Ein Kollege meldete sich vor einigen Wochen mit einem interessanten Problem: Beim Löschen von Objekten aus einem komplexeren und gut befüllten Datenmodell liefen alle DELETEs in den Subtabellen und Kreuzreferenzen mit der erwarteten Geschwindigkeit (also im niedrigen Millisekundenbereich) ab, aber die DELETEs auf der Haupttabelle benötigten je Objekt dann plötzlich einige Sekunden.

Mehrere Stunden brüteten wir zusammen mit Kollegen über dem Problem, untersuchten den Ausführungsplan der DELETE-Anweisung, misteten Indizes aus, erstellten andere Indizes neu, reorganisierten die betroffene Tabelle… alles ohne Ergebnis.

Erst ein weiterer Blick auf den Ausführungsplan, diesmal ohne Beachtung der angeblichen Aufwandsverteilung, führte dann zur Lösung: In einer anderen Tabelle mit mehreren Millionen Einträgen befand sich eine Fremdschlüsselspalte, die jedoch nicht Indiziert war. Folglich musste für jedes DELETE eine Full-Table-Scan ausgeführt werden um eventuelle Abhängigkeiten auszuschließen.

Daher nun die kurze Merkregel:

Lege auf einer Fremdschlüsselspalte immer auch einen Index an (sofern sie nicht schon führend in einem anderen Index enthalten ist). Sind in der Tabelle nur wenig Daten, dann tut der zusätzliche Index nicht weh, sind in der Tabelle jedoch viele Daten, dann wird man spätestens beim DELETE in der referenzierten Tabelle froh um den Index sein.

Und wie immer: Trotz Merkregel kann es in Einzelfällen gute Gründe geben, sich anders zu verhalten.

Heap-Corruption mittels „gflags“ untersuchen

Heap-Corruption-Fehler (Buffer-Overruns, Double-Frees, …) sind naturgemäß sehr schwer zu debuggen, da der eigentliche Fehler i.d.R. lange vor dem tatsächlichen Programmabsturz stattgefunden hat. Windows bietet für diesen Fall jedoch eine Hilfestellung an, sofern man das Programm „gflags“ aus den „Debugging Tools“ installiert hat:

gflags.exe -p /enable binary.exe [/full]

Das Programm binary.exe wird von nun an in einem Modus ausgeführt, bei dem jede unerwünschte Veränderung des Heaps sofort zu einem Absturz führt. Dadurch kann man der Ursache per Debugger oder Dump-Analyse deutlich schneller auf den Grund gehen.

Der Speicherverbrauch ist in diesem Modus etwas höher und die Arbeitsgeschwindigkeit etwas geringer. Beendet wird dieser Modus wieder mit folgendem Befehl:

gflags.exe -p /disable binary.exe

“Casting-Show” mit dem MS SQL Server – Teil 2

Vor gut zwei Monaten hatte ich über unterschiedliche Ergebnismengen bei prepared vs. non-prepared Statements berichtet. Zwischenzeitlich kam ein weiteres Phänomen dazu, was an das erste Phänomen erinnert:

Seit mehreren Monaten fallen uns immer mal wieder prepared Queries auf, deren Performance deutlich (Faktor 10 bis 100) schlechter ist, als das entsprechende non-prepared Queries. Alle Queries hatten gemeinsam, dass in einer wichtigen Bedingung eine VARCHAR-Spalte verwendet wird, auf der es einen Index gibt. Ein Blick auf die Ausführungspläne zeigte, dass die non-prepared Query einen performanten Index-Lookup durchführte, die prepared Query jedoch einen langsamen Index-Scan.

Als Ursache stellte sich der als Unicode-Wert übergebene Parameter der Bedingung auf der VARCHAR-Spalte (also keine Unicode-Werte) heraus. In so einem Fall scheint der SQL-Server – warum auch immer – keinen Lookup im Index machen zu wollen.

Lösungen:

  1. Ein Umstieg auf NVARCHAR-Spalten. Das ist unser bevorzugtes, mittelfristiges Ziel aber wie immer im Leben leider nicht sofort umsetzbar.
  2. Den JDBC-Treiber auf Non-Unicode-Parameterübergabe umschalten (sofern möglich).

„Casting-Show“ mit dem MS SQL Server

Eigentlich sollte man meinen, dass mit Unicode diverse Probleme rund um das Thema Sonderzeichen endlich gegessen wären, aber leider durfte ich neulich erleben, dass dem nicht so ist.

Gegeben sei eine einfache Tabelle „test“ mit einer NVARCHAR(50)-Spalte „name“ und zwei Datensätzen: „Preussen“ und „Preußen“.

Eine ebenso einfache Abfrage

SELECT * FROM test WHERE name = 'Preussen'

liefert – wie man es erwartet – einen Treffer. Macht man aus Ihr ein Prepared Statement, dann sind es plötzlich zwei Treffer: „Preussen“ und „Preußen“.

Natürlich sucht man den Fehler nun erstmal im eigenen Code, findet aber nichts, also probiert man alternative JDBC-Treiber aus und… findet auch nichts, bis man das ganze zum Schluss im Management Studio versucht und auch dort das seltsame Verhalten reproduzieren kann. Erst ein expliziter CAST des Vergleichswertes nach VARCHAR behebt das Problem. Ob diese Lösung aber performanter ist als der Verzicht auf Prepared Statements habe ich noch nicht geprüft.