CallByName
« | 15 Dec 2018 | »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.