VB Declare Function Part 1

Πολλές φορές προσπαθούμε να κάνουμε πραγματάκια σε VB / VBA κώδικα που βλέπουμε να κάνουν οι άλλες γλώσσες πανεύκολα και εμείς με την VB / VBA δυσκολευόμαστε. Ψάχνουμε να βρούμε στο internet έτοιμα ActiveX controls που να μας δίνουν δυνατότητες που παρέχει (σχεδόν) έτοιμες το λειτουργικό σύστημα. Τα windows έχουν από μόνα τους χιλιάδες dll που δίνουν έτοιμη λειτουργικότητα από τις συναρτήσεις που εξάγουν.

Γιατί λοιπόν να μην χρησιμοποιήσουμε από την VB / VBA όλες αυτές τις functions που η C παίρνει τσάμπα, και να μην κοιτάξουμε και τα παραδείγματα που υπάρχουν έτοιμα ώστε να έχουμε κάποιο σημείο να ξεκινήσουμε;

Ο μόνος λόγος είναι, επειδή δεν ξέρουμε πως!

Κι όμως μπορούμε απλώς να χρησιμοποιήσουμε το MSDN για να δούμε πληροφορίες για την function που μας ενδιαφέρει και μετά απλώς να την δηλώσουμε σε ένα module  της εφαρμογής μας χρησιμοποιώντας την Declare Function. (Την πλήρη σύνταξη της, μπορεί να την ψάξει όποιος ενδιαφέρεται στο help ή στο msdn).

Για παράδειγμα:

Declare Function SayEuroA Lib "DVAUtils.dll" (ByVal money As Double, ByVal bufferA As Long) As Long
Declare Function SayEuroW Lib "DVAUtils.dll" (ByVal money As Double, ByVal bufferW As Long) As Long

Εντάξει, το να περάσει ή να πάρει κάποιος μια παράμετρο όπως boolean, int, long, double, date είναι εύκολο. Είναι απλοί τύποι δεδομένων με συγκεκριμένο μέγεθος και συγκεκριμένο τρόπο αποθήκευσης σε όλες τις γλώσσες προγραμματισμού. Τι γίνεται όμως όταν θέλεις να περάσεις κάτι λίγο διαφορετικό όπως strings; (Δεν θα ασχοληθώ με δομές ολόκληρες παρόλο που ακολουθούν κι αυτές την ίδια τακτική με τα strings). Εδώ ο τρόπος αποθήκευσης μπορεί να διαφέρει στις γλώσσες προγραμματισμού, αλλά το πρόβλημα που μας απασχολεί τώρα είναι ότι τα strings δεν έχουν συγκεκριμένο μέγεθος.

Μπορεί το string να είναι 1 χαρακτήρας, μπορεί να είναι 100, μπορεί και 5000. Πως μπορούμε να περάσουμε ή να πάρουμε πράγματα τα οποία δεν έχουν συγκεκριμένο μέγεθος;

Αυτό που πρέπει να γίνει (όπως γίνεται και στη C) είναι να περάσουμε την διεύθυνση της String μεταβλητής, (που μπορούμε να την βρούμε χρησιμοποιώντας την συνάρτηση StrPtr), και να περάσουμε αυτή τη διεύθυνση για παράμετρο.

Τουλάχιστον στα περισσότερα dlls των windows, το ansi string που περνιέται ή επιστρέφεται σαν παράμετρος είναι έτσι όπως το διαχειρίζεται η C. Μια σειρά χαρακτήρων που τερματίζονται από τον χαρακτήρα 0 (NUL). Αντίστοιχα όταν αναφερόμαστε σε utf-8, utf-16, unicode strings είναι πάλι το byte ή word 0.

Τουλάχιστον στο πέρασμα της παραμέτρου δεν υπάρχει πρόβλημα. Η VB φτιάχνει ένα string, περνάει τη διεύθυνση του string σαν παράμετρο και το dll ξέρει (μπορεί να βρεί) από μόνο του ποιό είναι το μέγεθος του string, και μπορεί να κάνει ότι χρειάζεται.

Στην περίπτωση όμως που πρέπει να επιστραφεί ένα string υπάρχει ένα μικρό πρόβλημα. Πως θα ξέρει η vb (εύκολα) πόσο μεγάλο είναι το string που επιστράφηκε; Θα μπορούσαμε να ψάχνουμε έναν έναν τους χαρακτήρες μέχρι να βρούμε τον χαρακτήρα 0 ( chr(0) ) ή θα μπορούσαμε να χρησιμοποιήσουμε μία ακόμα παράμετρο που να μας λέει ποιό είναι το μήκος του string που επιστρέφεται (αν επιστρέφονται περισσότερα από 1strings), ή στην περίπτωση που επιστρέφεται μόνο ένα η τιμή επιστροφής της function θα μπορούσε να μας λέει η ίδια πόσο είναι το μήκος του string.

Οταν φτιάχνουμε εμείς το dll, μπορούμε να πάρουμε όποια απόφαση είναι πιο βολική, να την τεκμηριώσουμε και να την χρησιμοποιήσουμε. Στην γενική περίπτωση όμως της vb, τα dlls τα έχει φτιάξει άλλος (π.χ. η Microsoft) και εμείς απλώς τα χρησιμοποιούμε. Οπότε θα πρέπει να κοιτάξουμε την σχετική τεκμηρίωση στο msdn για το πως χρησιμοποιούνται.

Κατά κανόνα, οι functions που επιστρέφουν κάποιο string από τις παραμέτρους τους, δίνουν σαν τιμή επιστροφής της function, ή εναλλακτικά σε μία output παράμετρο, ποιό είναι το μέγεθος του string που πρόκειται να επιστρέψουν, και περιμένουν στην παράμετρο που θα μπει το string να τους δωθεί κάποια διεύθυνση ενός buffer, που εκεί μέσα θα το αποθηκεύσουν. Αν περαστεί για διεύθυνση του buffer η τιμή null, τότε δεν γίνεται καμία αποθήκευση (αφού κάτι τέτοιο θα οδηγούσε σε access violation), αλλά απλώς επιστρέφουν το μέγεθος του string απαιτούμενου buffer.

Μπορούμε πλέον να δεσμεύσουμε με κάποιο τρόπο έναν buffer, τόσο μεγάλο όσο χρειάζεται και να ξανακαλέσουμε την ίδια function περνώντας την δεύτερη φορά αυτόν τον buffer.

Καλούμε δηλαδή μιά φορά την function με παράμετρο null για να δούμε πόσο μεγάλο buffer θα χρειαστούμε.
Δεσμεύουμε ικανή ποσότητα μνήμης.
Ξανακαλούμε δεύτερη φορά την function με παράμετρο τον buffer μας, ώστε να πάρουμε πίσω το string.

Πέρα από αυτόν τον τρόπο δήλωσης, η VB μπορεί να κάνει κάποιες αυτοματοποιήσεις ώστε να μας διευκολύνει, όπως:

  • αυτόματη μετατροπή των ansi string σε unicode (δεν χρειάζεται να χρησιμοποιήσουμε την StrConv)
  • αυτόματο πέρασμα της διεύθυνσης του string (δεν χρειάζεται να χρησιμοποιήσουμε την StrPtr)

Για παράδειγμα μπορεί η δήλωση να γίνει:

Declare Function SayEuro Lib "DVAUtils.dll" Alias "SayEuroA" _
                         (ByVal money As Double, ByVal buffer As String) As Long

αλλά με τους εξής περιορισμούς:

  • Πρέπει να χρησιμοποιηθεί η ansi version των κλήσεων αφού κάνει αυτόματα μετατροπή σε unicode.
  • Πρέπει να έχουμε εκ των προτέρων δεσμεύσει αρκετό buffer ώστε να χωράει τα περιεχόμενα του string.