Newsgroup: it.comp.lang.visual-basic
Titolo: [?] Subclassing? Si, grazie!

Tutti ormai parlano di subclassare (scusate il termine!) qualcosa...ok
ho notato che si possono fare cose interessanti... ma come cavolo si fa?
Non potreste postare un piccolo esempio di codice??
La domanda e' forse indirizzata in particolare al 'mago' Roal Zanazzi???


___________________________________________________
SUBCLASSING
by ^Meimi^
Detto in parole semplici, il subclassing č l'intercettazione e il processo
dei msg generati dalle finestre, ma per capire a cosa serve occorre fare un
esempio pratico.
Supponiamo di voler aggiungere una voce al system menu. Il system menu č un
oggetto che comprende i control box, la barra della caption č il system menu
popup. Per aggiungere una voce alla fine del menu (o meglio per appendere
una voce), si usa la funzione API:
Public Declare Function AppendMenu Lib "user32" Alias "AppendMenuA" (ByVal
hMenu As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long, ByVal
lpNewItem As Any) As Long
Questa funzione ritorna True se ha avuto successo e False se ha fallito, ma
questo non ci occorre. Per appendere una nuova voce, si devono passare 4
parametri. Il primo č l'handle del system menu a cui andrą aggiunta la voce.
Ogni oggetto di windows ha un indice univoco e distintivo, che gli viene
assegnato al momento della creazione: questo č l'handle. Quasi tutte le
funzioni API fanno riferimento alla finestra sulla quale devono operare,
mediante l'handle... diciamo che č una specie di codice fiscale.
La funzione che ritorna l'handle del system menu di una finestra č:
Public Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Long,
ByVal bRevert As Long) As Long
Restituisce un Long che č l'handle del system menu della finestra il cui
handle viene passato come primo paramentro. Il secondo parametro puņ essere
False o True. Nel primo caso la funzione ritorna una copia dell'handle del
system menu attualmente esistente, mentre nel secondo caso distrugge il
system menu, lo riporta nello stato originale e ritorna l'handle.
Dim hSysMenu As Long
hSysMenu = GetSystemMenu(Me.hWnd, False)
hSysMenu č un long in cui la funzione va a mettere l'handle del system menu
della finestra il cui handle č Me.hWnd. Spero di non dover spiegare cosa
vuol dire Me e hWnd.
Torniamo alla funzione AppendMenu. Nel secondo paramentro vanne messi i flag
che specificano l'apparenza e lo stato del menu item. Gli unici due flag che
userņ qui, sono: MF_SEPARATOR e MF_STRING. I quali ovviamente andranno
dichiarati come segue:
Public Const MF_SEPARATOR = &H800&
Public Const MF_STRING = &H0&
Il primo dice alla funzione che quella voce č un separatore, mentre il
secondo che č una stringa. I flag possono essere usati anche combinati:
MF_STRING Or MF_CHECKED. Vuol dire che la voce č una stringa e deve essere
selezionata.
Il terzo paramentro specifica l'identificatore di quella voce, ovvero un
nome per essere identificato all'interno del programma. Se la voce č un menu
popup (MF_POPUP), ci va messo l'handle del menu popup.
Il quarto parametro specifica il contenuto del menu. Questo varia in
relazione al secondo parametro, cioč se č una stringa ci va la stringa, se č
una bitmap ci va il contest device di una oggetto contenente una bitmap,
etc.
Per aggiungere un separatore:
AppendMenu hSysMenu, MF_SEPARATOR, False, vbNullString
Il sepratore non ha ID e nel quarto parametro non va specificato niente.
Per aggiungere una voce che chiameremo Prova:
AppendMenu hSysMenu, MF_STRING, 1, "&Prova"
Il terzo paramentro serve per identificare la voce, ma siccome 1 č un valore
scomodo e poco nmemonico, lo associamo ad una costante:
Public Const ID_PROVA = 1
quindi:
AppendMenu hSysMenu, MF_STRING, ID_PROVA, "&Prova"
Ok, passiamo adesso alla pratica. Create un nuovo progetto e aggiungete un
Modulo.
Nel modulo mettete le seguinti dichiarazioni:
Public Const ID_PROVA = 1
Public Const MF_SEPARATOR = &H800&
Public Const MF_STRING = &H0&
Public Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Long,
ByVal bRevert As Long) As Long
Public Declare Function AppendMenu Lib "user32" Alias "AppendMenuA" (ByVal
hMenu As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long, ByVal
lpNewItem As Any) As Long
Mentre, nell'evento Load della form:
Private Sub Form_Load()
    Dim hSysMenu As Long
    hSysMenu = GetSystemMenu(Me.hwnd, False)
    AppendMenu hSysMenu, MF_SEPARATOR, False, vbNullString
    AppendMenu hSysMenu, MF_STRING, ID_PROVA, "&Prova"
End Sub
Non č stato aggiunto niente di nuovo.
Lanciate il prgetto e clicate sull'icona del system menu. Sotto la voce
Close, ci sono 2 nuove voci: un separatore e la voce Prova. Provate a
clickare su Prova... non succede niente.
Ogni finestra, ha una funzione speciale chiamata window procedure. A questa
funzione vengono inviati dei messaggi ogni volta che succede qualcosa nella
finestra. Ad esempio, quando si clicca su una voce di un menu, quando si
passa con il mouse sopra una voce, viene inviato il messaggio WM_COMMAND con
i relativi parametri alla window procedure. Perņ questi messaggi non si
vedono in VB, perché ogni finestra ha una window procedure predefinita, che
si occupa di traslare i messaggi in eventi. Perņ non tutti i messaggi
vengono processati. Infatti esiste l'evento click dei menuitem, ma non
esiste l'evento highlight, perché il secondo messaggio viene ignorato.
Esempio pratico: quando si clicca sulla voce close del system menu, la
finestra invia 4 parametri alla window procedure: l'handle della finestra,
il messaggio e due paramentri. l'handle č Me.hWnd, il messaggio č
WM_SYSCOMMAND e ilwParam (o meglio la parte bassa di wParam) č WM_CLOSE che
corrisponde a &H10. Questo č il famoso ID della voce, che nel nostro menu
abbiamo chiamato ID_PROVA (Se la voce č un menu popup, invece dell'ID ci
restituisce la posizione). Bene, la window procedure si occupa di processare
questo messaggio e chiude la finestra. Altro esempio. Abbiamo il menu File
con la voce Apri da noi definita con il menu Editor di VB. Abbiamo chiamato
la voce apri mnuApri. Quando si fa click su questa voce la finestra invia il
messaggio WM_COMMAND e l'ID di mnuApri e lo processa creando l'evento Click
(cioč lancia la funzione mnuApri_Click). Quando clicki sulla voce prova da
noi aggiunta nel system menu, la finestra invia il messaggio WM_SYSCOMMAND e
il paramentro ID_PROVA alla window procedure, ma non succede niente...
questo perché la wp riconosce il messaggio, ma non sa come processarlo e lo
ignora. Bisogna in qualche modo dire alla window procedure cosa deve fare
quando riceve quel messaggio, ovvero bisogna intercettare il messaggio e
processarlo. Occorre scrivere una nuova window procedure che intercetta e
processa i messaggi provenienti dalla finestra e assegargliela. Questo č il
subclassing.
Difficile? Si. perņ si fa..
Per prima cosa diciamo che la wp č una funzione callback, ovvero una
funzione scritta dall'utente e richiamata da windows dopo che č successa una
qualche cosa. La wp viene richiamata praticamente sempre, cioč ogni volta
che succede qualcosa sulla finestra. Questa funzione non puņ essere scritta
a caso, ma deve rispettare la struttura di una funzione callback WindowProc,
cioč:
1) Deve essere una Function
2) Deve ritornare un long
3) Deve ricevere 4 paramentri. Nell'ordine: l'handle della finestra che ha
spedito il msg, il messaggio, il primo parametro e il secondo paramentro.
4) Deve risiedere in un modulo e deve essere Public
Esempio:
Public Function nuovoProcesso(ByVal hwnd As Long, ByVal iMsg As Long, ByVal
wParam As Long, ByVal lParam As Long) As Long
End Function
Abbiamo definito una funzione chiamata nuovoProcesso, restituisce un long
che č il risultato del processo di un messaggio e dipende dal messaggio.
I 4 parametri, vengono passati alla funzione da windows. Per associare ad
una finestra una nuova wp, si usa l'API SetWindoLong:
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA"
(ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Questa funzione serve per cambiare un attributo di una finestra. Il primo
parametro č l'handle della finestra, il secondo č una costante che specifica
l'attributo da cambiare e il terzo č il nuovo valore. Per cambiare la window
procedure come secondo paramentro viene passato GWL_WNDPROC:
Public Const GWL_WNDPROC = (-4)
Questa costante non fa altro che dire alla SetWindowLong che l'attributo da
cambiare č la wp. In questo caso, il terzo parametro deve essere l'indirizzo
della nuova window procedure, cioč:
AddressOf nuovoProcesso
Il subclassing andrą messo nell'evento Load della finestra.
Dim processoOriginale As Long
processoOriginale = SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf
nuovoProcesso)
La funzione restituisce un valore... questo valore č importante. E'
l'indirizzo della window procedure originale. Se si esce dal programma
quando ancora il subclassing č in atto, si va in crash. Quindi nell'evento
Unload della form si deve riassegnare alla finestra la vecchia wp:
SetWindowLong Me.hwnd, GWL_WNDPROC, processoOriginale
In pratica, riprendendo il progetto di prima, adesso diventa:
Modulo:
Public processoOriginale As Long
Public Const ID_PROVA = 1
Public Const MF_SEPARATOR = &H800&
Public Const MF_STRING = &H0&
Public Const GWL_WNDPROC = (-4)
Public Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Long,
ByVal bRevert As Long) As Long
Public Declare Function AppendMenu Lib "user32" Alias "AppendMenuA" (ByVal
hMenu As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long, ByVal
lpNewItem As Any) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA"
(ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Function nuovoProcesso(ByVal hwnd As Long, ByVal iMsg As Long, ByVal
wParam As Long, ByVal lParam As Long) As Long
End Function
Eventi:
Private Sub Form_Load()
    Dim hSysMenu As Long
    hSysMenu = GetSystemMenu(Me.hwnd, False)
    AppendMenu hSysMenu, MF_SEPARATOR, False, vbNullString
    AppendMenu hSysMenu, MF_STRING, ID_PROVA, "&Prova"
    processoOriginale = SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf
nuovoProcesso)
End Sub
Private Sub Form_Unload(Cancel As Integer)
    SetWindowLong Me.hwnd, GWL_WNDPROC, processoOriginale
End Sub
La nuova wp non č completa. Innanzi tutto, non si vogliono processare tutti
i messaggi, ma solo quelli nuovi. Quindi si deve fare in modo di far
processare i msg non processati dalla nuova wp, a quella vecchia. Questo si
fa con l'API CallWindowProc:
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA"
(ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal
wParam As Long, ByVal lParam As Long) As Long
Questa funzione restituisce un Long che dipende dal tipo di messaggio
processato. Il primo paramentro č l'indirizzo di una wp. Nel nostro caso
della wp originale. La funzione č passare un messaggio ad una wp.
Modifichiamo la nuova wp in questo modo:
Public Function nuovoProcesso(ByVal hwnd As Long, ByVal iMsg As Long, ByVal
wParam As Long, ByVal lParam As Long) As Long
    nuovoProcesso = CallWindowProc(processoOriginale, hwnd, iMsg, wParam,
lParam)
End Function
Abbiamo creato una chain, ovvero un ponte tra la finestra e la wp originale.
Tramite questo ponte possiamo:
1) intercettare e processare i nuovi messaggi
2) intercettare e modificare quelli gią esistenti
In parole povere, tutti i msg, prima di arrivare alla vecchia wp, passano
per la nuova.
Esempio: clickiamo sulla voce Close del System menu. La finestra passa il
messaggio alla nuova wp (nuovoProcesso) e la nuova wp li ripassa alla
vecchia tramite CallWindowProc. La vecchia wp lo processa. Esempio: la
finestra manda il msg WM_MOUSEMOVE con le relative coordinate alla nuova wp.
La nuova wp intercetta il msg, modifica le coordinate e manda il tutto alla
vecchia wp.
Quello che rimane da fare adesso č un Hook dei msg, ovvero la cattura di
determinati msg:
Select Case iMsg
  Case WM_SYSCOMMAND
    Select Case wParam
      Case ID_PROVA
        MsgBox "Hai clickato sul SystemMenu Prova ^_^", vbInformation
      End Select
End Select
La wp va cosģ modificata:
Public Function nuovoProcesso(ByVal hwnd As Long, ByVal iMsg As Long, ByVal
wParam As Long, ByVal lParam As Long) As Long
  Select Case iMsg
    Case WM_SYSCOMMAND
      Select Case wParam
        Case ID_PROVA
          MsgBox "Hai clickato sul SystemMenu Prova ^_^", vbInformation
        End Select
  End Select
  nuovoProcesso = CallWindowProc(processoOriginale, hwnd, iMsg, wParam,
lParam)
End Function
La vecchia wp riceverą comunque il msg che non puņ processare, ma non č
importante perché il msg viene processato prima.
Prima di lanciare il progetto, le procedure per il subclassing dovranno
essere complete, altrimenti il pc va in crash.
Programma completo.
Modulo:
Option Explicit
Public processoOriginale As Long
Public Const ID_PROVA = 1
Public Const WM_SYSCOMMAND = &H112
Public Const MF_SEPARATOR = &H800&
Public Const MF_STRING = &H0&
Public Const GWL_WNDPROC = (-4)
Public Declare Function GetSystemMenu Lib "user32" (ByVal hwnd As Long,
ByVal bRevert As Long) As Long
Public Declare Function AppendMenu Lib "user32" Alias "AppendMenuA" (ByVal
hMenu As Long, ByVal wFlags As Long, ByVal wIDNewItem As Long, ByVal
lpNewItem As Any) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA"
(ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA"
(ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal Msg As Long, ByVal
wParam As Long, ByVal lParam As Long) As Long
Public Function nuovoProcesso(ByVal hwnd As Long, ByVal iMsg As Long, ByVal
wParam As Long, ByVal lParam As Long) As Long
    If iMsg = WM_SYSCOMMAND Then
        Select Case wParam
            Case ID_PROVA
                MsgBox "Hai clickato sul SystemMenu Prova ^_^",
vbInformation
        End Select
    End If
    nuovoProcesso = CallWindowProc(processoOriginale, hwnd, iMsg, wParam,
lParam)
End Function
Eventi form:
Option Explicit
Private Sub Form_Load()
    Dim hSysMenu As Long
    hSysMenu = GetSystemMenu(Me.hwnd, False)
    AppendMenu hSysMenu, MF_SEPARATOR, False, vbNullString
    AppendMenu hSysMenu, MF_STRING, ID_PROVA, "&Prova"
    processoOriginale = SetWindowLong(Me.hwnd, GWL_WNDPROC, AddressOf
nuovoProcesso)
End Sub
Private Sub Form_Unload(Cancel As Integer)
    SetWindowLong Me.hwnd, GWL_WNDPROC, processoOriginale
End Sub
____________________________________________________

dei msg generati dalle finestre, ma per capire a cosa serve occorre fare un
[MAGACLIP]
Carissima ^Meimi^  :-)
Che ne dici di prendere il msg che hai postato, metterlo in un HTML, dargli
una sistemata ed inviarlo al Webmaster del Sito Comune per pubblicarlo in
Piccole Perle come Articolo?
Cosi' il prox che lo chiede lo reindiriziamo sul Sito Comune.
Salutoni
Ciao
By -MES-   Maronese Ettore
http://ilmes.freeweb.org
(con OCX per stampare i recordset)
Sito Comune di programmatori Visual Basic
http://www.murialdo.it/it_lang_vb


Bene, Brava 7+
Tutto ciò è estremamente interessante, ci stavo lavorando
in questi giorni e, devo dire che un tuo precedente post mi aveva
indirizzato nella giusta direzione.
Ora vorrei capire come si impostano i valori
delle costanti WM_XXX,GWL_XXX etc.
 Io ho fatto una ricerca su MSDN ma gli unici punti dove le ho trovate sono
nella documentazione specifica delle api e lì non v'è traccia di come
impostarli, o se c'è non l'ho trovata.
Il tema di discussione è ottimo ed abbondante.
Ciao.

miiitico

delle costanti WM_XXX,GWL_XXX etc.
Intendi che valori hanno o quando si usano?
Se la risposta è "Che valori hanno", allora guarda sul Visualizzatore API
Se invece la risposta è "quando si usano", beh, allora interessa anche a me... ^__^
Infatti non ho trovato nessuna documentazione, ed ho dovuto arrangiarmi da solo (ad esempio se dovevo scoprire qual'era
la costante da utilizzare in risposta ad un certo evento generato dall'utente, ho creato un programmino che intercettava
tutti gli eventi del mouse e mi sparava fuori i codici... poi sono andato a cercarli nell api32.txt e mi sono ricavato
il nome della costante....)
Ciao,
Moreno
#sirri#@
tin.it
(togliete "#" dall'indirizzo)

by ^Meimi^
E brava la nostra 'maga'! La risposta merita come minino di essere inclusa
nelle faq del sito.
Ciao Marco

WOW!! Ti ringrazio per il 'trattato' sull'argomento.  =]   =]
Spero di ricambiare al piu' presto.
---------------------------------------------------------


Questa cosa è troppo imbarazzante *^_^*
Dimmi cosa devo fare.
Ciao

...

Butta un'occhio alla sezione Piccole Perle del sito, apri un articolo
qualsiasi e prendi spunto da quello.
Comunque, fondamentalmente, non penso che siano da fare grandi modifiche al
tuo MSG, c'e' giusto da metterlo un po' in bella.
Poi invii l'html al Webmaster e.... ^_^
Salutoni

Dimmi cosa devo fare.
Vai sul sito comune e dai un'occhiata alla sezione "Piccole Perle".
Ci sono 4 articoli: dai un'occhiata a come sono fatti e fanne uno simile.
Si tratta semplicemente di convertire in HTML (magari con il tuo editor, così fai prima.. ^__^) il tuo messaggio;
preparare un esempietto dimostrativo et voilà....
Prendi spunto da quelli già pronti (il mio è scritto con notepad, così non ti da problemi di formattazione) per
l'impaginazione...



A che serve il 'mago' Roal Zanazzi, se qui abbiamo la fatina ^Meimi^ ? ;-)
Riprendendo la proposta di Ettore Maronese, se ti puo' servire, Meimi, ho da
qualche parte una bozza di articolo che spiega il subclassing (era mia
intenzione finirlo e mandarlo sul Sito Comune, ma non avendone il tempo...).
Che ne dici se te lo faccio avere (e' gia' in HTML, posso passartelo sul NG,
se preferisci), lo sistemi e lo mandi al webmaster del Sito?
Ciao.
--
Roal Zanazzi
z...@iol.it

---------------------------------------------------------
Se interessa a qualcuno io, tramite il subclassing, ho realizzato due
piccoli OCX, uno che gestisce il joystick ed uno che permette di generare
eventi al passaggio del puntatore sulle voci di menù. Ho qualche problemino
di "instabilità" su quello del joy, se a qualcuno interessa posso mandare i
sorgenti e discuterne...
:-)) a tutti
Genesio

:-)) a tutti
Noooooo, perché gli ocx????? Capisco per il Joystick (ma non approvo), ma
per le voci di menu no... io cerco di usare le API apposta per evitare di
appesantire l'installazione (che già fa schifo a causa delle Run Time).
Ciao


Ti devi sempre portare dietro classi e/o moduli, ect, ect, dai ! E' così
caruccio un OCXuccio piccolo piccolo. Ho il mio bell'evento MenuSelect... ci
sono tante applicazioni "porche" e ciuccia-risorse, non facciamo i tirchi
:-))).
Bye


Si grazie mandalo... cosě vedo se ho sbagliato qualcosa ^_^
Cmq l'ho riscritto (quasi uguale) e ho aggiunto un po' di teoria sulle
classi in c++, giusto per capire cosa NON fa VB.
Ciao

Meimi dimostri ancora una volta di essere grandissima!!
Ciao



sono tante applicazioni "porche" e ciuccia-risorse, non facciamo i tirchi
Non ti porti dietro nessun modulo, perché vengono compilati insieme all'exe.
Quindi l'evento MenuSelect potevi farlo come classe ^_^
Cmq anch'io li uso gli ocx.. tanto ci sono 2M di run time, 100k o 200k di
ocx che vuoi che facciano? ;_;
Ciao




Beh, d'altronde per postarlo 3 volte... :-)))


Tieni conto che ho preso l'argomento un po' alla larga (capirai leggendo),
ma ritengo che capire come funzionano le cose in Windows sia abbastanza
indispensabile per capire in fondo il subclassing...
Ti attacco il testo HTML sotto, non in attachment, dato che il solito robot
"spazzino" potrebbe farne un boccone prelibato ;-)
Ah, dimenticavo... chiaramente mettici il tuo nome al posto del mio :-)
--
Roal Zanazzi
z...@iol.it
---- START ----
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<META NAME="AUTHOR" CONTENT="Roal Zanazzi">
<TITLE>Il subclassing</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<H1>Che cos'&egrave; il subclassing</H1>
<HR>
<P ALIGN="right"><FONT SIZE="4" FACE="Courier New"><B><I>di Roal
Zanazzi</I></B></FONT></P>
<H2>Introduzione</H2>
<P>
Questo articolo &egrave; dedicato all'introduzione della tecnica di
subclassing, dalla spiegazione teorica e concettuale fino ad un semplice
esempio concreto.
</P>
<HR>
<H2>Windows e le sue finestre</H2>
Nel sistema operativo Microsoft Windows ogni finestra (form o controllo di
Visual Basic) viene gestita tramite una funzione (<I>window procedure</I>)
che &egrave; quasi sempre un semplice Select Case sui messaggi di Windows
(WM_MOUSEDOWN, WM_CHAR, etc...).<BR></BR>
Questa funzione &egrave; una <I>callback</I>, ossia una funzione
implementata nel programma, ma richiamata dal sistema operativo stesso, che
definisce il comportamento stesso della finestra in risposta ai messaggi che
Windows le spedisce.<BR></BR>
Ad esempio, una finestra TextBox risponder&agrave; al messaggio WM_CHAR
(tasto premuto) visualizzando il carattere corrispondente al tasto, nella
posizione corrente del cursore testo.
Ogni finestra &egrave; definita, nelle sue caratteristiche di base
(dimensione, posizione, stile e altro), da una struttura (Type di VB) che si
chiama <B>window class</B>; la window procedure viene associata alla
finestra assegnando il puntatore della funzione ad un campo di questa
struttura.<BR></BR>
Il funzionamento &egrave; questo:
quando nella coda di messaggi dell'applicazione c'e' qualche messaggio (es.
e' stato premuto un tasto -> WM_MOUSEDOWN) Windows esegue la window
procedure, la quale esegue le funzioni relative a quel messaggio (Es. il
pulsante si ridisegna come premuto).
<HR>
<H2>Il concetto di subclassing</H2>
<P>
Per subclassing si intende <B>concettualmente</B> la modifica del
comportamento di una finestra.
Tale modifica viene fatta <B>concretamente</B> sostituendo la window
procedure della finestra da subclassare con un'altra window procedure
personalizzata (che, di solito, modifica solo le risposte ad alcuni messaggi
e chiama la vecchia window procedure per tutti gli altri).<BR></BR>
Il subclassing &egrave; in generale una tecnica complessa, ma molto
utilizzata nello sviluppo delle applicazioni Windows, soprattutto dai
programmatori C.
</P>
<HR>
<H2>Subclassing in Visual Basic</H2>
<P>
Poich&eacute; per modificare una window procedure occorre passare il
PUNTATORE della nuova window procedure, questo &egrave; possibile solo da
Visual Basic versione 5.0 o superiore, utilizzando la funzione
<B>AddressOf</B> (per maggiori informazioni consultare l'help in linea di
Visual Basic).
</P>
<P>
Per assegnare la nuova window procedure si usa la funzione API
<B>SetWindowLong()</B>. Tale funzione permette di modificare i campi della
struttura relativa alla finestra, descritta precedentemente.<BR></BR>
Da notare che la funzione ritorna il puntatore alla vecchia window
procedure:
</P>
<CODE><PRE>
Dim OldWinProc As Long
OldWndProc = SetWindowLong(MyForm.hWnd, GWL_WNDPROC, AddressOf NewWndProc)
</PRE></CODE>
dove:
<CODE><PRE>
Public Function NewWndProc(hwnd As Long, uMsg As Long, wParam As Long,
lParam As Long) As Long
    Select Case uMsg
        Case WM_CHAR: MyForm.BackColor = &H00FF00
        Case Else: Call OldWndProc(hwnd, uMsg, wParam, lParam)
    End Select
End Sub
</PRE></CODE>
<P>
Il puntatore alla window procedure si pu&ograve; anche leggere usando la
funzione API <B>GetWindowLong()</B>:
</P>
<CODE><PRE>
OldWndProc = GetWindowLong(MyForm.hWnd, GWL_WNDPROC)
</PRE></CODE>
<BLOCKQUOTE>
<B>NOTE:</B> nell'ambiente di sviluppo di Visual Basic il subclassing crea
problemi, e spesso provoca un crash dell'ambiente. Questo avviene (come
spiegano nel MSDN), per il seguente motivo:
quando Visual Basic e' in pausa durante l'esecuzione del codice, non e'
possibile chiamare metodi vtable o la funzione AddressOf. Per evitare
problemi, Visual Basic ritorna 0 al chiamante della funzione AddressOf,
senza chiamare realmente la funzione.
Nel caso di subclassing, questo significa che viene ritornato 0 come
puntatore alla nuova Window Procedure. Windows richiede un valore di ritorno
diverso da 0, cosi' la costante 0 ritornata puo' creare una situazione di
stallo tra Windows e Visual Basic, bloccando il processo e costringendo a
terminare l'applicazione VB.
</BLOCKQUOTE>
<HR>
<H2>Un esempio di subclassing</H2>
<P>
Questa applicazione consiste in una semplice form con due command
buttons.<BR></BR>
L'unica operazione eseguita da questo subclassing e' l'intercettamento dei
messaggi di Windows mandati alla form e la scrittura di tali messaggi nella
finestra di Debug di Visual Basic.
</P>
<P>La prima parte del codice consiste nelle dichiarazioni per le funzioni
API, i valori delle costanti e le variabili necessarie:</P>
<CODE><PRE>
Declare Function CallWindowProc Lib "user32" Alias _
    "CallWindowProcA" (ByVal lpPrevWndFunc As Long, _
    ByVal hwnd As Long, ByVal Msg As Long, _
    ByVal wParam As Long, ByVal lParam As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias _
    "SetWindowLongA" (ByVal hwnd As Long, _
    ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Const GWL_WNDPROC = -4
Global lpPrevWndProc As Long
Global gHW As Long
</PRE></CODE>
<P>Due routine pubbliche servono per attivare e disattivare il subclassing.
La prima procedura (SubClass) chiama la funzione API SetWindowLong() per
assegnare alla form la nuova window procedure, eseguendo cosi' il
subclassing;
la seconda procedura (UnSubclass) disabilita il subclassing ri-assegnando
alla form la sua window procedure originale.
La nuova window procedure &egrave; implementata nella funzione
WindowProc.</P>
<CODE><PRE>
Public Sub Subclass()
   lpPrevWndProc = SetWindowLong(gHW, GWL_WNDPROC, AddressOf WindowProc)
End Sub
Public Sub UnSubclass()
   Call SetWindowLong(gHW, GWL_WNDPROC, lpPrevWndProc)
End Sub
Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
   Debug.Print "Message: "; hw, uMsg, wParam, lParam
   WindowProc = CallWindowProc(lpPrevWndProc, hw, uMsg, wParam, lParam)
End Function
</PRE></CODE>
<P>
Ed ecco il codice associato alla form ed ai due pulsanti.<BR></BR>
Al caricamento della form viene salvata in una variabile globale l'handle
della form (hWnd), che verr&agrave; utilizzata per il subclassing; i due
pulsanti servono semplicemente per attivare e disattivare il subclassing:
</P>
<CODE><PRE>
Private Sub Form_Load()
   gHW = Me.hwnd
End Sub
Private Sub Command1_Click()
   Subclass
End Sub
Private Sub Command2_Click()
   UnSubclass
End Sub
</PRE></CODE>
<hr>
<H2>Conclusioni</H2>
<p>
L'obiettivo di questo articolo
di condividere con voi altri i risultati delle mie ricerche e non va
considerato come &quot;verbo&quot;.
Le informazioni contenute in questo articolo sono frutto dell'esperienza
dell'autore e di altri partecipanti al newsgroup
Se avete dubbi o volete chiarimenti o approfondimenti sull'argomento
trattato, siete liberi di porre le vostre domande al newsgroup.
</p>
<Address>Roal Zanazzi</address>
---- END ----
</BODY>
</HTML>

Ah, dimenticavo... chiaramente mettici il tuo nome al posto del mio :)
?? di tuo ho usato solo le note per il crash ^_^;;;;;;;
Ciao


Groan... mesi e mesi di duro lavoro... nemmeno considerato...
sto scherzando ;-)
--
Roal Zanazzi
z...@iol.it

Fantastico!