Neuigkeiten:

Wenn ihr euch für eine gute Antwort bedanken möchtet, im entsprechenden Posting einfach den Knopf "sag Danke" drücken!

Mobiles Hauptmenü

VBA Funktionen per Zeiger aufrufen

Begonnen von Knobbi38, Mai 24, 2026, 15:49:10

⏪ vorheriges - nächstes ⏩

Knobbi38

- FWIW -

Hallo,

durch Zufall bin ich auf das Thema ,,Zeiger auf Funktionen" in VBA gestoßen. Weil das Thema vielleicht auch andere interessieren könnte, möchte ich zwei Beispiele vorstellen, bei denen VBA-Sub-/Funktionen quasi per Name aufgerufen werden. Das funktioniert ähnlich wie das Aufrufen von Klassenmethoden mit CallByName.

Im ersten Beispiel wird das einfach nur mit der weniger bekannten Methode Application.Run umgesetzt, im zweiten dann etwas performanter per API.

Knobbi38

PhilS

Zitat von: Knobbi38 am Mai 24, 2026, 15:49:10Weil das Thema vielleicht auch andere interessieren könnte, möchte ich zwei Beispiele vorstellen, bei denen VBA-Sub-/Funktionen quasi per Name aufgerufen werden. Das funktioniert ähnlich wie das Aufrufen von Klassenmethoden mit CallByName.
Ein interessantes Thema. Allerdings finde ich deinen Vergleich mit CallByName für das zweite Beispiel (API) etwas irreführend, weil die Prozeduren ja nicht anhand ihrer Namen aufgerufen werden, sondern anhand ihrer Adresse, die mit dem Prozedurnamen im Code ermittelt werden, so wie bei einem "normalen" Aufruf auch.

Generell, d.h. ohne Bezug zu diesen Beispiel, ist mein größtes Problem im diesem Kontext, dass AddressOf nicht für Prozeduren in einem Klassenmodul funktioniert. Das wäre sehr hilfreich, wenn man manche API-Funktionalität, die eine Callback-Adresse benötigt (z.B. einen OS/API-Timer), komplett in einem Klassenmodul kapseln möchte. (Es gibt Beispielcode, wie man das selbst bauen kann, aber nur für 32bit und die analoge Implementierung für 64bit ist alles andere als trivial.)



Neue Videoserie: Windows API in VBA

Klassische CommandBars visuell bearbeiten: Access DevTools CommandBar Editor

Knobbi38

#2
Hallo Phil,

das habe ich schon mal in Verbindung mit Klassen gesehen, wo es um Timer-Klassen ging. Da wurde an die API ein ObjPtr() als Parameter übergeben. Muss mal schauen, ob ich das Beispiel nochmal wieder finde.
Dabei ist die eigentliche API CallBackRoutine in einem Module, welche dann den übergebenen ObjtPtr auswertet und dann eine bestimmte Methode in der Klasse aufruft.

Hier habe ich so etwas gefunden:
https://stackoverflow.com/questions/3008197/timer-functions-and-generating-events-with-a-callback
Allerdings wird dort 'quick and dirty' eine Referenz "NICHT COM KONFORM!" per Copymemory auf ein Objekt erzeugt. Das kann man besser und auch sowohl für 32- als auch für 64 Bit machen!

Hast du so etwas gemeint?

Ich habe mich gerade mit dem Thema ,,COM object from pointer (32-bit and 64-bit compatible)" beschäftigt, da man an OpenArgs keine Objekte übergeben kann. Damit entfällt die Möglichkeit, eine echte ,,Key/Value Pair"-Klasse zu übergeben, und es muss auf Konstrukte wie Join/Split und Enums als Positionsindex zurückgegriffen werden.

Knobbi38

Nachtrag:
Wenn es speziell nur um die Timer API geht, gibt es hier ein reine Klassenlösung als Beispiel (ungetestet):
https://github.com/thetrik/VbTrickTimer

PhilS

Zitat von: Knobbi38 am Heute um 12:48:27Dabei ist die eigentliche API CallBackRoutine in einem Module, welche dann den übergebenen ObjtPtr auswertet und dann eine bestimmte Methode in der Klasse aufruft.
Das normale Modul möchte ich ja explizit nicht. Alles soll in dem Klassenmodul gekapselt sein, ohne die Notwendigkeit ein weiteres Modul mitzuliefern.


Zitat von: Knobbi38 am Heute um 12:48:27Hier habe ich so etwas gefunden:
So ähnlich habe ich mir auch beholfen.  :-/
Es gibt sicherlich verschiedene Wege, mit denen man den Callback in einem normalen Modul unterbringt und dann an die passende Methode einer bestimmten Klasseninstanz weiterleitet. Leider sind das nur krude Workarounds, die das eigentliche Problem umgehen anstatt es zu lösen.

Um die Problemstellung und die damit einhergehenden Herausforderungen zu verstehen, verweise ich mal auf diesen Thread in den VBForums: API CallBacks Using an Object's Procedure
Dort geht es nur um die "einfachere" Implementierung auf der x86/32bit Plattform. :-(
Neue Videoserie: Windows API in VBA

Klassische CommandBars visuell bearbeiten: Access DevTools CommandBar Editor

PhilS

Zitat von: Knobbi38 am Heute um 12:48:27Nachtrag:

Wenn es speziell nur um die Timer API geht, gibt es hier ein reine Klassenlösung als Beispiel (ungetestet):
https://github.com/thetrik/VbTrickTimer
Cool, das werde ich mir bei Gelegenheit mal in Ruhe ansehen.
Mir geht es allerdings um eine generelle Lösung. Timer sind nur der häufigste praktische Anwendungsfall, den ich bisher hatte. - Das Problem mit dem verlinkten Beispiel ist, wenn ich das auf den ersten Blick richtig erkannt habe, dass die Behandlung der Argumente der Timer-Proc hardcodiert vorgegeben ist. Ich hätte gern eine Lösung die die Argumente, ihre Reihenfolge und Datentypen, zur Laufzeit erkennt und den Thunk dafür dynamisch generiert.
Neue Videoserie: Windows API in VBA

Klassische CommandBars visuell bearbeiten: Access DevTools CommandBar Editor

Knobbi38

Hallo Phil,

wie gesagt, ein Beispiel in dem Nachtrag, auch über VTAB für 32/64 Bit.

ZitatLeider sind das nur krude Workarounds, die das eigentliche Problem umgehen anstatt es zu lösen.
Sehe ich ein bißchen anders, weil VBA nun mal mit COM-Klassen arbeitet und C/C++, also die WIN-API Welt, nichts damit am Hut hat. Immerhin gibt es in VBA/VB6 die Möglichkeit, per Modul sich eine Brücke zwischen diesen beiden welten bauen zu können, was ich eigentlich ganz cool finde und keineswegs krude. Den Aufwand mit Assembler usw., nur um an die Adresse der COM-Objekt Methode zu gelangen und das Ganze noch auf den HEAP oder im virtuellen Speicher sicher abzulegen, finde ich gelinde gesagt, etwas übertrieben und würde ich mir nicht antun.

Gruß Knobbi38




Knobbi38

Zitat von: PhilS am Heute um 13:50:15... zur Laufzeit erkennt und den Thunk dafür dynamisch generiert.
So etwas wird es wohl nicht geben, weder in VBA noch sonst wo. Was es in anderen Programmiersprachen gibt, ist eine Reflection-Schnittstelle, aber leider nicht in allen Programmiersprachen und schon gar nicht in VBA.   :(
Reflection hört sich zwar ganz gut an, aber wenn man sich damit etwas näher beschädigt, macht das auch nicht wirklich Spaß - ich bevorzuge dann doch besser die statische, typsierte Methode, da findet der Compiler meine Fehler sofort.  ;)

Gruß Knobbi38   

PhilS

Zitat von: Knobbi38 am Heute um 13:56:41Sehe ich ein bißchen anders, weil VBA nun mal mit COM-Klassen arbeitet und C/C++, also die WIN-API Welt, nichts damit am Hut hat.
Naja, C++ ist ja quasi die Wiege der COM-Programmierung und neuere Ergänzungen der Windows-API (so ab Win7), verwenden häufig selbst COM-Klassen.

Zitat von: Knobbi38 am Heute um 13:56:41Den Aufwand mit Assembler usw., nur um an die Adresse der COM-Objekt Methode zu gelangen und das Ganze noch auf den HEAP oder im virtuellen Speicher sicher abzulegen, finde ich gelinde gesagt, etwas übertrieben und würde ich mir nicht antun.
Die Adresse kann man noch recht einfach und ohne Assembler ermitteln: ObjPtr + Offset der Methode aus der VTable.
Problematisch wird es, und da ist Assembler erforderlich, weil das erste Argument beim Aufruf einer COM-Objektmethode immer der Zeiger auf das Objekt selbst ist, den man ergänzen und alle weiteren Argumente dann entsprechend verschieben muss; bei 64bit mit hoher Wahrscheinlichkeit aus einem Register auf den Stack, den man dann entsprechend selbst reorganisieren muss. 
Ich stimme dir zu, dass will man sich i.d.R. nicht selbst antun. :-/

Zitat von: Knobbi38 am Heute um 14:10:36So etwas wird es wohl nicht geben, weder in VBA noch sonst wo.
Der AddressOf Operator in den .Net Sprachen tut das, der in TwinBasic ebenfalls, in Object Pascal/Delphi gab es da auch eine eingebaute Lösung dafür, wenn mein Gedächtnis mich nicht täuscht.

Zitat von: Knobbi38 am Heute um 14:10:36Was es in anderen Programmiersprachen gibt, ist eine Reflection-Schnittstelle, aber leider nicht in allen Programmiersprachen und schon gar nicht in VBA.
=> "TypeLib Information" (TLBINF32.DLL") 
(Ich weiß allerdings nicht, ob das auch mit selbst erstellten, benutzerdefinierten VBA-Klassen funktioniert.)

Ja, natürlich sind das alles eher "akademische" Ansätze, die in der Praxis nur in sehr speziellen Einzelfällen Relevanz haben.
 
Neue Videoserie: Windows API in VBA

Klassische CommandBars visuell bearbeiten: Access DevTools CommandBar Editor

daolix

technisch machen beide (callback im modul, asm in der klasse) das selbe, die funktionschnittstelle der Callbackfunktion wird für die Api schön gemacht und am ende rufen beide die Methode in der Klasse auf. In VBA würde ich auch die Modulmethode bevorzugen, weil einfacher.

Knobbi38

#9
Die Lösungsansätze in anderen Programmiersprachen sind durchaus unterschiedlich, speziell wenn es um typsichere Aufrufe gegen soll, hat aber erstmal nichts mit der Reflection-Schnittstelle zu tun, bei der Metadaten ausgewertet werden. Nur damit kann so etwas dynamisch gelöst werden.

Die "TypeLib" Information ist im Prinzip nur eine Schnittstellenbeschreibung und da VBA keine DLL oder Exe (mehr) erstellen kann, hilft das nur bedingt weiter; irgendwo muss ja der ausführbare Code stehen.

In den meisten Fällen reicht es ja schon, wenn man aus einem ObjPtr wieder ein Objekt erzeugen kann und für die WinAPI muss man dann eben tricksen oder gleich eine andere Programmiersprache verwenden.

Klasse wäre natürlich, wenn MS das TwinBasic-Projekt unterstützen und in Office implementieren würde - wird wahrscheinlich nicht passieren.  :(