Funzioni callback in Visual Basic 5.0 |
||
Data |
by Spider |
|
07/2001 |
UIC's Home Page |
Published by Quequero |
Grazie per il tutorial, non posso commentare gran che perchè il VB lo conosco abbastanza poco...Mi sono solo permesso di abbassare il livello di difficoltà dal momento che le funzioni di callback non sono proprio classiche della programmazione avanzata :) |
||
.... |
|
.... |
Difficoltà |
( )NewBies (X)Intermedio ()Avanzato ( )Master |
Contrariamente a quanto molti programmatori VB pensano, in Visual Basic 5.0 è possibile utilizzare le funzioni callback. Per far ciò bisogna ricorrere ad alcune funzioni API apposite.
Introduzione |
Tools usati |
Essay |
Per prima cosa, che cos'è una funzione callback? Una funzione
callback è una funzione che viene richiamata da un'altra
funzione che dispone del suo indirizzo in memoria. Ad esempio,
quando subclassiamo una finestra passiamo alla funzione
SetWindowLong l'indirizzo della nostra Window Procedure, che
abbiamo ottenuto con l'operatore AddressOf. Windows potrà così
richiamare la nostra funzione ad ogni messaggio. In VB,
normalmente, è questo tutto ciò che possiamo fare con le
funzioni callback. Supponiamo però di voler richiamare una
funzione callback da VB. Come fare? Hehehe! Per tanto tempo anch'io
me lo sono chiesto. La risposta l'ho trovata riflettendo sul
funzionamento del subclassing: quando richiamiamo la
SetWindowLong, essa ci restituisce l'indirizzo della vecchia
Window Procedure. Nella nostra Window Procedure, mettiamo una
chiamata alla vecchia Window Procedure utilizzando la funzione
API CallWindowProc. Questa funzione non fa altro che richiamare
la Window Procedure che c'era prima che noi istallassimo il
subclassing. Se la CallWindowProcA può fare ciò con l'indirizzo
di una Window Procedure, perchè non dovrebbe poterlo fare con
una procedura di altro genere? Allora mi sono messo a fare alcuni
esperimenti e, visti i buoni risultati iniziali, ho approfondito
l'argomento. Ho constatato che la CallWindowProcA, sebbene
normalmente funzioni con 5 argomenti, funziona anche con un
numero diverso di argomenti. Il minimo è ovviamente 1, perchè
è sempre necessario l'indirizzo della procedura da richiamare.
Facciamo un esempio pratico:
'###########################################################
'# Questo codice deve essere iserito in un modulo standard #
'###########################################################
Declare Function CallProc0 Lib "user32" Alias "CallWindowProcA" (ByVal _ FunctionAddress As Long) As Long Public Function Prova() As Long Form1.Print "Ciauz!" Prova = 2001 End Function '<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
'################################################################ '# Questo nella sezione dichiarazioni di un Form chiamato Form1 # '# contenente un bottone chiamato Command1 # '################################################################ Private Sub Command1_Click() Print CallProc0(AddressOf Prova) End Sub
Eseguitelo e cliccate su Command1. Sul form la funzione scriverà "Ciauz!", e poi verrà scritto 2001, che è il valore di ritorno della funzione. Adesso un'altra versione con 1 argomento:
'###########################################################
'# Questo codice deve essere iserito in un modulo standard #
'###########################################################
Declare Function CallProc1 Lib "user32" Alias "CallWindowProcA" (ByVal _ FunctionAddress As Long, ByVal Argomento1 As Long) As Long Public Function Prova(ByVal Argomento As Long) As Long Form1.Print "Ciauz!" Form1.Print "L'argomento che mi hai passato è: " & Argomento Prova = Argomento End Function '<-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><-><->
'################################################################ '# Questo nella sezione dichiarazioni di un Form chiamato Form1 # '# contenente un bottone chiamato Command1 # '################################################################ Private Sub Command1_Click() Print CallProc1(AddressOf Prova,2001) End Sub
In questo caso, viene passato un argomento, che
viene stampato e restituito come argomento. Potremmo mettere
decine di argomenti; l'unica cosa da ricordare è che bisogna
inserire sempre come primo argomento il puntatore alla funzione
da richiamare. Infatti, con le convenzioni di chiamata StdCall,
gli argomenti vengono immessi nello stack in ordine inverso. Ciò
significa che il primo argomento sarà l'ultimo ad essere pushato,
e quindi la CallWindowProcA può leggerlo e richiamarlo
indipendentemente dal numero di argomenti passati.
Se il numero di argomenti passati è diverso dal numero di
argomenti ricevuti dalla funzione, si avrà l'errore di run-time
49, cioè "Convenzione di chiamata DLL non valida". Se
si gestisce l'errore, la funzione sarà eseguita ugualmente, ma
non sarà possibile ricevere un eventuale valore di ritorno.
Generalmente è sufficiente utilizzare questo
sistema. Talvolta, però, può essere necessario richiamare la
funzione desiderata il più velocemente possibile, ad esempio se
si deve richiamare molte volte di seguito. In tal caso, è
necessario utilizzare delle API non documentate che, a differenza
della CallWindowProcA, sono apposite per questo scopo. Queste,
contenute in Kernel32.dll, hanno il nome di Callbackxx,
dove xx è un numero decimale che identifica quanti
argomenti passiamo alla funzione. Ad esempio, la Callback4 passerà
0 argomenti. E perchè 4? Perchè nello stack viene comunque
messa una dword, dove viene salvato l'eip. La Callback8 passerà
1 argomento, e vengono messi nello stack 2 dwords (il Return
Address e l'argomento). In generale, per passare n argomenti, si
userà la Callback(n*4+4). Con gli esempi che abbiamo fatto: 0
argomenti * 4+4 = 4, ed useremo la Callback4. 1 * 4 + 4 = 8 (Callback8).
Per formula inversa, la callback28 avrà (28-4):4, cioè 6
argomenti. Si possono passare fino ad un massimo di 15 argomenti
(callback64), ma questo limite non rappresenta un problema, perchè
15 argomenti bastano ed avanzano.
N.B.: A differenza della CallWindowProcA, ogni
Callbackxx è specifica per il numero di argomenti. Con le
Callbackxx, l'indirizzo della funzione da richiamare deve essere
passato come ultimo argomento.
Adesso facciamo un esempio con la Callproc8 (1 solo argomento):
'###########################################################
'# Questo codice deve essere iserito in un modulo standard #
'###########################################################
Declare Function Callback8 Lib "user32"(ByVal Argomento1 As Long, Byval _ FunctAddress As Long) As Long Public Function Prova(ByVal Argomento As Long) As Long Form1.Print "Ciauz!" Form1.Print "L'argomento che mi hai passato è: " & Argomento Prova = Argomento End Function '################################################################ '# Questo nella sezione dichiarazioni di un Form chiamato Form1 # '# contenente un bottone chiamato Command1 # '################################################################ Private Sub Command1_Click() Print Callback8(2001, AddressOf Prova) End Sub
Tuttavia, sebbene le callbackxx siano più veloci rispetto alla CallWindowProcA, sono molto più instabili. Non è possibile gestire eventuali errori. Un minimo sbaglio e... CRASH!!! Se non è proprio necessario, si consiglia di evitare il loro uso. In ogni caso, salvare molto spesso.
Spider
Note finali |
Saluto naturalmente Quequero,
poi un saluto va a Yado, a TheMR, a Bubbo e a tutti gli altri
studenti e frequentatori della UIC.
Un saluto particolare va ad AndreaGeddon, che mi ha sopportato
finora ed è stato disponibilissimo ad aiutarmi via e-mail (grazieeeeeeeeeeeeeeeeeeeeeeeeee!!!!!!).
Dimentico qualcuno? Spero di no.
Disclaimer |
Questo è un tute di programmazione, quindi 'sto disclaimer che a ca**o serve? Comunque io ci tengo lo stesso a ricordare che il software va comprato e non rubato, dovete registrare il prodotto dopo il periodo di valutazione perchè altrimenti la mamma si arrabbia. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial.
Questo tute ha un periodo di valutazione di 5 minuti, se l'avete letto e c'avete messo di più avete l'obbligo legale e morale di mandarmi una e-mail con il numero della vostra carta di credito o, se non vi fidate di me, di inviarmi per raccomandata lit. 19.900 (10,27 euro). Le informazioni quivi contenute sono protette dalle leggi italiane e internazionali sul copydown ©, e ogni violazione di queste leggi comporterà l'immediata perdita di tutti i vostri averi che passeranno a me :-P
Byeeeeez!