Windows 10 mag Visual Studio 2015 nicht – nanu!

Naja, ich muss das ein wenig relativieren. Nach einigen erfolgreichen Migrationen auf Windows 10 war nun gestern mein Arbeitsplatzrechner an der Reihe. Eine ziemlich alte Windows 7 32 Bit (wg. einer Nexus DVB-S TV Karte) Installation, auf der sich SEHR viel Krams im Lauf der Jahre angesammelt hat (e.g. mehrere Versionen Visual Studio, dazu SQL Server, Office, …). Der Upgrade auf Windows 10 Home dauert daher auch recht lange (klar, er muss ja die Features aus der Windows Installer Datenbank übernehmen) und dann, so nach ca. 100 Minuten bei 70% Gesamtfortschritt (90% Features & Treiber) stehe ich plötzlich am rien ne va plus…

Über dem großen Kreis mit dem Gesamtforschritt erscheint ein Windows Befehlszeilenfenster (!), ohne Inhalt mit dem Titel “Microsoft Visual C++ Runtime …” – leider ohne Versionsnummer. Es handelt sich offenbar (?) um den Versuch, irgendeine Visual C++ Runtime zu installieren. Jeder Versuch, das Fenster zu schliessen (Klick mit der Maus, ENTER im Fenster, Alt-F4) führt dazu, dass Windows 10 auf die Vorinstallation zurückfällt. Mist!

Nun habe ich leider eine ganze Reihe von C++ Runtime Versionen auf meinem Altsystem: 2005, 2008, 2010, 2012, 2013 und 2015. Ich hatte erst einmal die wirklich alten (2005 und 2008) im Verdacht und diese zusammen mit ähnlich alten Komponenten (e.g. SQL Compact 2005, 3.5 und 4.0) entfernt. Leider ohne Erfolg.

Im Windows 10 Setup Protokoll (setupact.log) gehörte der letzte Eintrag zum Visual Studio 2015, das ich kurz vorher (RTM wohlgemerkt) installiert und ausprobiert hatte. Also schweren Herzens (das hat fast 2 Stunden gedauert – Löschen von einer SSD, echt heftig) Visual Studio 2015 samt C++ 2015 Runtime deinstalliert und siehe da: die Windows 10 Installation läuft durch. Ich gehe davon aus, dass ich darauf auch wieder Visual Studio 2015 mit allem installieren kann, aber im Moment reicht es mir erst einmal – die Nerven 🙂

Ich hoffe, dass der Tipp dem einen oder anderen dabei hilft, Windows 7 etwas schneller als ich zu modernisieren!

Viel Spaß dabei

Jochen

JavaScript und Schutz vor unerwünschtem Zugriff auf Objekte

Der genaue Hintergrund meiner Fragestellung führt jetzt zu weit, aber letztlich bin ich auf diesen Artikel gestoßen. Nehmen wir einmal an, dass ich eine JavaScript Klasse aus einer Bibliothek in meinem Programm unverändert verwenden möchte. Im Beispiel könnte das etwa so aussehen:

var SomeClass = function () {
    function SomeClass(value) {
        this.value = value;
    };

    SomeClass.prototype.getValue = function () {
        return this.value;
    };

    SomeClass.prototype.setValue = function (newValue) {
        this.value = newValue;
    };

    return SomeClass;
}();

Das Programm selbst bietet eine Erweiterungsschnittstelle an, über die ein Anwender sich in bestimmte Abläufe einklinken kann. Allerdings soll er im Beispiel nur lesend auf den in einer Instanz gespeicherten Klasse zugreifen können (getValue), Änderungen (setValue, value) sind alleine meinem Programm erlaubt – gerade ein direkter Zugriff auf Eigenschaften kann natürlich einiges durcheinanderwerfen.

Der oben erwähnte Artikel erklärt sehr schön, welche Alternativen man hat, um tatsächlich das gewünschte Ergebnis zu erreichen. Am mächtigsten scheint der Einsatz von Closures (JavaScript Scopes) pro Objektinstanz zu sein. Allerdings erwähnt der Autor des Artikels dabei auch, dass es zu Geschwindigkeitseinbußen kommen wird – zudem der Artikel davon ausgeht, dass man die verwendete Bibliothek verändern kann. Hier einmal ein Alternativvorschlag respektive eine Ergänzung.

Im Endeffekt soll das Programm in der Lage sein, eine Kapselung (hier als Proxy bezeichnet) zu erzeugen, die auf keinem Weg einen Zugriff auf die Objektinstanz der Bibliothek zulässt. Die Erstellung der Kapselung könnte man sich so vorstellen – Details gleich:

// Einmalig ganz am Anfang
var someClassProxy = Context.registerProxyType(SomeClass, "getValue");

// Das wäre unser Bibliotheksobjekt, vielleicht auch nur einmalig erzeugt
var lib = new SomeClass(42);

// Hier brauchen wir irgendwo tief im Code die Kapselung
var proxy = Context.createProxy(test, someClassProxy);
try {
    extensionMethod(proxy);
} finally {
    proxy.destroy();
}

Ziel ist es, dass über die Kapselung nur die Methode getValue angeboten wird und zudem die Eigenschaften des JavaScript Objektes keine Rückschlüsse auf das Bibliotheksobjekt zulassen. Das wäre dann eine mögliche Lösung:

var Context = function () {
    function Context() {
    };

    var index = 0;
    var map = {};

    Context.createProxy = function (instance, factory) {
        if (instance === null)
            return null;
        if (instance === undefined)
            return undefined;

        var proxy = factory(index++);

        map[proxy.index] = instance;

        return proxy;
    };

    Context.registerProxyType = function () {
        var args = arguments;
        var type = args[0];

        var Proxy = function () {
            function Proxy(index) {
                this.index = index;
            };

            Proxy.prototype.destroy = function () {
                delete map[this.index];
            };

            for (var i = 1; i < args.length; i++) {
                var name = args[i];
                var fn = type.prototype[name];
                if (fn === undefined)
                    throw "no method " + name;

                Proxy.prototype[name] = function (fn) {
                    return function () {
                        var instance = map[this.index];
                        if (instance === undefined)
                            throw "proxy already disconnected";

                        return fn.apply(instance, arguments);
                    };
                }(fn);
            }

            return Proxy;
        }();

        return function (index) {
            return new Proxy(index);
        };
    };

    return Context;
}();

Mit der statischen registerProxyType Methode kann ich zu einer beliebigen Klasse einen Kapselungserzeuger (Factory) erstellen, der in der Kapselung nur die gewünschten Methoden offenlegt. In der Kapselung wird auch keine Referenz auf das Bibliotheksobjekt gespeichert, sondern nur eine laufende Nummer einer aktiven Kapselung - da könnte der Anwender in seiner Erweiterung dran herumspielen, erreicht dadurch aber maximal andere aktive Kapselungen. Durch das Closure (den Scope) ist vor allem map für den Anwender unerreichbar. createProxy erzeugt zu einem beliebigen Bibliotheksobjekt eine neue Kapselung - alternative Kapselungen ein und der selben Klasse für unterschiedliche Erweiterungsszenarien sind denkbar. Durch einen laufenden Index (ebenfalls im Closure) wird die Kapselung aktiviert, ein destroy Aufruf auf der Kapselung hebt die Aktivierung wieder auf - auch hier kann die Erweiterung pfuschen aber letztlich nur ihre eigene Aufgabe erschweren, niemals aber die Infrastruktur der Bibliotheksobjekte antasten.

Ok, das ist als Studie nur ein erster Gedanke. Sollte ich es denn wirklich einmal brauchen, kann man sicher noch vieles verfeinern - e.g. muss destroy wirklich eine Methode der Kapselung sein? Aber ich hoffe, dass auch in diesem groben Ansatz zumindest die Idee insbesondere als Erweiterung des eingangs erwähnten Artikels verständlich wird.

Happy Coding

Jochen

DVB.NET und DirectShow – der ewige Kampf

Nachdem ich nun meinen ersten PC auf Windows 10 aktualisiert habe musste ich feststellen, dass die mit Windows 7 noch verfügbaren Microsoft Video Codecs für MPEG-2 und H.264 nicht mehr zur Verfügung stehen – genau genommen ist das schon seit Windows 8 so, aber diese Version habe ich stillschweigend übersprungen. Das ist für mich etwas schade, da der DVB.NET / VCR.NET Viewer sehr pingelig bezüglich der verwendeten Codecs ist und mit den Microsoft Codecs ausgesprochen gut zurecht kam.

Ich habe daher die LAV Codecs installiert und grundsätzlich funktionieren die auch, nur leider ist der Ton deutlich sichtbar versetzt. Ich habe hier zwar etwas drehen können, aber mein mangelndes Wissen im Bereich BDA und DirectShow macht sich mal wieder sehr schmerzlich bemerkbar. Da ich nicht die Absicht habe, dieses Wissen zukünftig zu vertiefen, kann es sein, dass mit Windows 10 das endgültige Ende des Viewers gekommen ist. Ein kleiner Tweak hilft aber zumindest mir ein bisschen, so dass ich demnächst ein Update für den Viewer bereit stellen werden – eine wirkliche Weiterentwicklung der Version 4.3 wird es wie angekündigt nicht geben.

Doch nun zu einigen Details. Im Kern setzt DVB.NET zur Anzeige von Bild und Ton zwar auf BDA und DirectShow, verwendet allerdings keine der angebotenen Splitter. Vielmehr wird im Eingang ein TS (Transport Stream) selbst analysiert, was Teile des Codes nicht nur flexibler macht (es ist egal, ob der Strom von einer TV Karte, einer Datei oder über das Netzwerk ankommt) sondern tatsächlich auch den Code vereinheitlicht und vereinfacht.

Der Preis ist allerdings, dass Bild und Ton manuell in den BDA Graphen injiziert werden müssen. Aus nicht mehr ganz nachvollziehbaren Gründen (ja, das ist teilweise schon sehr lange her – sorry) versetzt DVB.NET die so genannte Stream Time der Tonspur um eine halbe Sekunde nach hinten. Die Idee war damals bei langsameren Rechnern wohl, dass der Video-Codec Zeit erhalten sollte, das Bild vorzubereiten, bevor dies synchron mit dem Ton angezeigt wird. Irgendwie kommt LAV aber mit dieser Manipulation wohl nicht klar.

Da dieser Parameter (AVDelay im Code) sowieso schon konfigurierbar war, hat der Viewer nun die Möglichkeit erhalten, diese Einstellung in einem gewissen Rahmen (+/- 2 Sekunden) vorzugeben. Mit einem Wert von 0 scheint es im Moment einigermaßen ordentlich zu funktionieren – perfekt ist es aber nicht! Ich werde das weiter beobachten, eventuell wird dieser Work-Around aber als Patch veröffentlicht – schauen wir einmal.

Neue Einstellungen

Bis bald

Jochen

P.S.: Ein Wert von 100 hat sich zumindest bei mir hier ganz gut bewährt und ich habe die Version Online gestellt.