CallByName

Die Funktion CallByName war für mich eine der Besonderheiten, die mit Visual Basic 6 in die Sprache aufgenommen wurde. Sie hat es via VBA auch in die Office Familie geschafft und ermöglicht eine ganz spezielle dynamische Erweiterbarkeit von Programmen.

Denn während die Menge an aufrufbaren Funktionen und Code im wesentlichen zur “Entwurfszeit” vom Programmierer festgelegt wird und nach dem Kompilieren nicht mehr anwachsen kann, gestattet uns diese Funktion in Kombination mit ein paar anderen neue Funktionen aufzuführen, die dem Programmierer nicht bekannt sein müssen.

Hmm … ein spannendes Feature…


Eine der genialeren Möglichkeiten des dotNet Frameworks ist es, dass Code zur Laufzeit dynamisch kompiliert und ausgeführt werden kann.

Das erreicht man, indem man einen Compiler im Framework gleich mitliefert. Der kann dann Quellcode aus Strings in ausführbaren Code übersetzen, der dann bei Bedarf ausgeführt wird.

Es handelt sich somit um ein Framework-Feature und weniger um ein Feature der dotNet Programmiersprachen C-sharp und VB.NET.

Das klassische Visual Basic und VBA setzten allerdings auf COM-Objekten auf, genau genommen auf eine ganz spezielle Erweiterung, nämlich IDispatch.

IDispatch ist auch als DIE Scripting-Schnittstelle bekannt und Teil der OLE Automation Strategie.

Jedes Objekt muss das IDispatch Interface zur Verfügung stellen, welches alle anderen Methoden-Aufrufe und Eigenschafts-Zugriffe über einige wenige generische Methoden leitet und auf die eigentlichen C Routinen übersetzt, also “dispatch-t”.

Bei “normal” kompilierten Code wird ein Funktionsaufruf in eine Adresse übersetzt und die Parameter werden je nach ihrer Größe in der exakt richtigen Reihenfolge auf den Stack gelegt. Jede Abweichung führt zum Absturz.

Mittels IDispatch werden alle Funktionsaufrufe auf eine einheitliche Form gebracht, indem alle Parameter in ein Array von VARIANT Werten gepackt werden. Der aufgerufene Dispatcher packt dann alles wieder aus und ruft wie üblich die native Implementierung auf.

CallByName macht sich diese Tatsache zu nutze und bietet dem Programmierer an, dieses Feature direkt zu nutzen.

Nachdem man über CreateObject und GetObject Objektinstanzen durch ihren String-Namen erzeugen kann, lässt uns CallByName auch Funktionen über ihren String-Namen und ein Parameter-Array ausführen.

Das gestattet uns eine primitive Form des Scripting im VB-Code, ohne dass man einen vollständigen Interpreter ausprogrammieren muss.

Es ist somit nicht zwingend notwendig TypeLibs und andere OCX oder DLL Komponenten mitzuliefern um auf Fremdkomponenten zugreifen zu können. Man muss lediglich die ProgID oder URL-Form eines Objektes und die Textnamen seiner Funktionen kennen.

Als ich noch mit VB und ADSI arbeitete, half mir dieses Feature Automationsmechanismen bereitzustellen, die vom Enduser abgeändert werden konnten. Der Benutzer war so in der Lage spätere Erweiterungen in Form von Methoden oder Properties zu bearbeiten, ohne dass diese im Quellcode vorgesehen werden mussten.


Auch wenn dieses Feature nicht täglich zur Anwendung kommt, so sehe ich in C Projekten gerne Schnittstellen vor, die ähnlich wie IDispatch native Funktionsaufrufe in generische Ausführungsblöcke umwandeln können.
In C++ lässt sich ein solches Feature mit Templates noch besser umsetzen, und so eine Kapselklasse für alles Mögliche realisieren.

Natürlich darf man dabei nicht vergessen, dass dieser Dispatch-Schritt Performance kostet, schließlich muss jeder Aufruf hin und her übersetzt werden. Doch wenn es sich um Funktionen handelt, die selbst viel erledigen und daher seltener von außen aufgerufen werden, tritt dieser Seiteneffekt in den Hintergrund.

Hilfreich wird ein solcher Zwischenschritt, wenn man die Anbindungen an mehrere Scriptsprachen plant. Man übersetzt die nativen Objekte auf einen generischen Dispatcher und die Scriptsprachen können dann munter und generisch mit dem Dispatcher sprechen.
Eine Kapselung für jede einzelne Scriptsprache entfällt auf diese Weise.