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 :)

 

....

E-mail: [email protected]
Canale IRC frequentato: irc.azzurra.it #crack-it

....

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.


Funzioni callback in Visual Basic 5.0
Written by Spider

Introduzione

Anche la guida di VB dice che in Visual Basic non si possono utilizzare le funzioni callback. Tuttavia, ricorrendo a qualche API, è possibile abbattere questo limite di VB5.

Tools usati

Ovviamente Visual Basic 5.0 o superiore.

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!