Newsgroup: it.comp.lang.visual-basic
Titolo: [REPOST] Programmi VB composti solo dalla Sub Main e poco altro (soprattutto, niente form) (lungo) - devo decidermi a farlo pubblicare

COME CREARE UN PROGRAMMA VB
COMPOSTO DA UN SOLO MODULO DI CODICE
Innanzitutto raccogliamo il materiale necessario. Lavoreremo parecchio con le
API, ecco quelle che useremo (ometto le Declare - usate il Visualizzatore API):
CreateWindowEx()
[ in un sistema operativo chiamato Windows, l'avrete capito, le finestre sono
essenziali. Mai più senza ]
ShowWindow()
[ le finestre create con l'API precedente sono restie a mostrarsi. Questa
funzione serve a convincerle ]
DefWindowProc()
CallWindowProc()
[ come le gestisce Windows, le finestre, non le gestisce nessun altro... ]
SetWindowLong()
[ ...ma per certe cose dovremo cavarcela da soli ]
RegisterClass()
UnregisterClass()
[ creiamoci la nostra classe personale di finestre ]
PostQuitMessage()
[ altro che End, gente... ]
Indispensabile infine il Type WNDCLASS. Gradite le Const WS_, WS_EX_ e WM_
Ora che abbiamo le Declare che ci servivano, pensiamo a mettere giù un po' di
codice.
Sub Main()
End Sub
[ questa è indispensabile ]
Function WinProc(ByVal hwnd As Long, _
ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
WinProc = DefWindowProc(hwnd, uMsg, wParam, lParam)
End Function
[ ecco un esempio di callback. Windows, come vedremo, chiamerà questa funzione
ad ogni evento avvenga sulla finestra che creeremo. La chiamata alla funzione
DefWindowProc rimette il controllo della situazione in mano alle librerie di
Windows. Ad esempio, se il sistema deve ridisegnare la finestra, noi non ci
sporcheremo le mani con un compito così basso e servile, ma lo delegheremo a
Windows ]
Sub Main()
Dim myclass as WNDCLASS
With myclass
.lpfwndproc = Dummy(AddressOf WinProc)
.hInstance = App.hInstance
.lpszClassName = "myclass"
End With
End Sub
Cosa ho fatto? Sto cercando di creare una nuova classe di finestre chiamata
"myclass", e assegno ad una struttura WNDCLASS gli attributi più importanti
della nostra classe: il nome, indispensabile e obbligatoriamente unico, l'handle
dell'istanza dell'applicazione e l'handle della nostra callback, ottenuto
tramite l'operatore AddressOf ed una funzione Dummy, che altro non è se non:
Function Dummy (d As Long) As Long
Dummy = d
End Function
che è un piccolo trucco per salvare un puntatore ricavato con AddressOf in una
variabile
Vediamo se il sistema operativo è pronto ad accogliere la nostra finestra.
retval = RegisterClass(myclass)
Se retval è uguale a 0 al sistema la nostra classe di finestre decisamente non
piace. Non ci resta che arrenderci.
If retval = 0 Then
    End
End If
Proviamo a cambiare nome alla classe, magari usando un nome che nessun serio
programmatore oserebbe usare, del tipo "The Super Duper Window Class". Comunque,
in genere "myclass" va benissimo.
Se il sistema ha gradito, allora procediamo
If retval = 0 Then
    End
Else
    hwnd1 = CreateWindowEx(0, "myclass", "My window", WS_OVERLAPPEDWINDOW _
    , 100, 100, 200, 150, 0, 0, App.hInstance, 0)
End If
Se non funziona, hwnd1 sarà uguale a 0, quindi togliamo il disturbo e
un-registriamo la classe che abbiamo registrato. Se funziona, hwnd1 conterrà
l'handle della finestra creata, quindi, se serve, la mostriamo (nell'IDE
mostratela sempre, o saremo costretti ad usare il pulsante Stop, e il crash sarà
assicurato)
(non vi sto a spiegare tutti gli argomenti di CreateWindowEx, ci sono delle
guide per questo che lo fanno pure meglio di me)
If retval = 0 Then
    End
Else
    hwnd1 = CreateWindowEx(...)
    If hwnd1 = 0 Then
        UnregisterClass "myclass", App.hInstance
        End
    Else
        ShowWindow hwnd1, 1
    End If
End If
Bene. Per ottenere la residenza in memoria, facciamo semplicemente:
Do
DoEvents 'se non ci fosse bisognerebbe inventarlo
If dead Then Exit Do
'dead è una variabile Boolean da definire nelle dichiarazioni del modulo
Loop
--
Function WinProc(ByVal hwnd As Long, _
ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Select Case uMsg
    Case WM_DESTROY
        'notifichiamo la distruzione della finestra alla Sub Main
        dead = true
        'Lasciamo che Windows distrugga la finestra
        WinProc = DefWindowProc(hwnd, uMsg, wParam, lParam)
    Case Else
        'In tutti gli altri casi lasciamo che se ne occupi Windows
        WinProc = DefWindowProc(hwnd, uMsg, wParam, lParam)
End Select
End Function
Questo significa che quando la finestra viene chiusa, il programma esce dal
Loop, dopodichè:
Do
DoEvents 'se non ci fosse bisognerebbe inventarlo
If dead Then Exit Do
'dead è una variabile Boolean da definire nelle dichiarazioni del modulo
Loop
'De-registriamo la nostra classe
UnregisterClass "myclass", App.hInstance
'Il thread chiede al sistema di ucciderlo e ritornare 0 come valore
PostQuitMessage 0
--
Questo programma ha una sola finestra, nuda. L'ideale se vi serve semplicemente
intercettare messaggi o monitorare il sistema. Se vi serve aggiungere pulsanti o
controlli di sorta, basta chiederlo e continuerò questo "tutorial"

Se basta chiedere, allora chhiedo subito.
Questo argomento e' molto interessante.
Ciao e grazie.
 Kjo


Concordo... anche per me che ormai lavoro su C++ che su VB pero e' un
ottimo ripasso per entrambi i mondi
-- 
Sidewinder  
---------------------------------------------------------------------
In the Spring, I have counted 136 different kinds of weather inside of
24 hours.
		-- Mark Twain, on New England weather
---------------------------------------------------------------------

Ok. L'avevo continuato fino alla prima "lezione", eccola. Se serve sapere altro
(i Window Message, ad esempio, che sono una bella gatta da pelare) lo continuo
perchč mi fermai qui
-----
A GRANDE RICHIESTA CONTINUA
IL TUTORIAL SUL SUBCLASSING
(NOTA: il subclassing, checchč se ne dica, č facilissimo)
 - piatto del giorno: messagebox alla pressione di un pulsante -
Bene, le Declare sono tutte quelle dell'ultima volta. Ne aggiungiamo giusto due
o tre perchč stavolta faremo qualcosa di pių.
LoadLibrary()
[ da maneggiare con molta cautela, ma da' grandi soddisfazioni se domata ]
FreeLibrary()
[ perchč dobbiamo lasciare la stanza nelle stesse condizioni in cui era ]
CallWindowProc()
[ chiama una funzione di cui č noto il puntatore, quindi dinamite pura per il
VB. Preparatevi a salvare spesso, e resettare ancora di pių ]
Le Const sono le stesse della lezione scorsa, a cui si aggiunge la GWL_WNDPROC.
SetWindowLong() ve l'avevo fatta mettere l'ultima volta, e non l'avevamo usata.
Stavolta ci sarā indispensabile, perchč lavoreremo con giocattoli non nostri, e
in qualche modo ce ne dovremo impossessare.
Vi direi anche di aggiungere la Sub InitCommonControls(), perchč faremmo
veramente scintille (in pratica vi insegnerei come fare a meno di molti OCX), ma
purtroppo il database del mio API Viewer č obsoletuccio, e questa mi manca.
Inoltre aggiungete queste Const, che nei testi non si trovano ma che se dovete
creare finestre molto complesse, contenenti decine e decine di controlli
alleggeriranno molto il carico del compilatore:
Const WC_BUTTON = "BUTTON"
Const WC_COMBOBOX = "COMBOBOX"
Const WC_EDIT = "EDIT"
Const WC_LISTBOX = "LISTBOX"
Const WC_MDICLIENT = "MDICLIENT"
Const WC_SCROLLBAR = "SCROLLBAR"
Const WC_STATIC = "STATIC"
Questo modo di programmare vi costringe ad una precisa visione d'insieme, ossia
un'attenta pianificazione a tavolino dell'aspetto e del comportamento del vostro
programma. Come forse giā sapete, la struttura del linguaggio e le limitazioni
intrinseche non ci permetteranno di creare funzioni o thread (questo poi, mai e
poi mai) a runtime, al massimo potrete farvi un bel modulo di classe per
semplificare alcune operazioni, ma del resto č proprio cosė che funzionano i
_veri_ ActiveX (almeno dovrebbero).
Il nostro primo obiettivo non sarā comunque molto ambizioso: ci limiteremo ad
aggiungere un pulsante alla nostra finestra, e a mostrare un messaggio quando
questo verrā cliccato.
Nella sezione Dichiarazioni aggiungerei:
Dim hwnd2 As Long
Dim proc2 As Long
Dim old_proc2 As Long
(d'ora in poi, se vedete un 2 in fondo ad una variabile, significa che č
riferita al pulsante - che ancora non abbiamo creato)
Modificate la WinProc in questo modo:
Function WinProc(ByVal hwnd As Long, _
ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Select Case hwnd
Case hwnd1
    Select Case uMsg
        Case WM_DESTROY
            'notifichiamo la distruzione della finestra alla Sub Main
            dead = true
            'Lasciamo che Windows distrugga la finestra
            WinProc = DefWindowProc(hwnd, uMsg, wParam, lParam)
        Case Else
            'In tutti gli altri casi lasciamo che se ne occupi Windows
            WinProc = DefWindowProc(hwnd, uMsg, wParam, lParam)
    End Select
Case hwnd2
        Select Case uMsg
            Case Else
                WinProc = CallWindowProc(old_proc2, hwnd2, uMsg, wParam, lParam)
End Select
End Function
In pratica la nostra WinProc č cosė versatile (generica) che con i giusti
accorgimenti puō andar bene per tutte le finestre del nostro thread. Gli
accorgimenti altro non sono che una distinzione dell'handle della finestra,
ossia una distinzione della finestra (gli handle sono univoci), e una chiamata
alle funzioni standard delle finestre, giusto per non doverci riscrivere tutte
le librerie.
Avrete notato una cosa, se avete prestato particolare attenzione al codice. E
cioč che CallWindowProc() ha un parametro in pių di DefWindowProc(). Posso
spiegare tutto, lo giuro. Ma per ora cerchiamo almeno di mostrare il pulsante.
La Sub Main va modificata, innanzitutto:
If retval = 0 Then
    End
Else
    hwnd1 = CreateWindowEx(...)
    If hwnd1 = 0 Then
        UnregisterClass "myclass", App.hInstance
        End
    Else
        ShowWindow hwnd1, 1
        hwnd2 = CreateWindowEx(0, WC_BUTTON, "Click me!", WS_VISIBLE, _
        10, 10, 200, 200, hwnd1, 0, App.hInstance, ByVal 0&)
    End If
End If
Qui sto cercando di creare un pulsante. L'ho definito "figlio" della finestra
creata in precedenza, almeno spero - ricontrollate il codice, magari, perchč
sono sicuro di essermi lasciato dietro almeno un parametro.
(NOTA: WS_VISIBLE č un trucco per evitare di dover usare ShowWindow())
Se avviate il codice cosė com'č... beh, vi aspetta una piccola sorpresa, diciamo
un piccolo scherzo che ho voluto farvi perchč ha un effetto davvero curioso e
molto divertente (provatelo). Se vogliamo fare le cose sul serio, facciamo cosė,
piuttosto:
...
hwnd2 = CreateWindowEx(0, WC_BUTTON, "Click me!", _
WS_VISIBLE Or WS_CHILD, 10, 10, 200, 200, hwnd1, _
0, App.hInstance, ByVal 0&)
...
Oooh, il pulsante adesso č lė dove dovrebbero stare tutti gli onesti pulsanti di
questo mondo, perfetto. Diamogli un po' di vita, adesso. Impadroniamocene.
hwnd2 = CreateWindowEx(...)
proc2 = Dummy(AddressOf WinProc)
old_proc2 = SetWindowLong(hwnd2, GWL_WNDPROC, proc2)
Ok! Ora il pulsante č in mano nostra. Tutti i messaggi diretti al pulsante
arriveranno alla nostra callback. Ma noi non ci accontentiamo di gioire, noi ne
approfittiamo per conseguire il nostro scopo:
Function WinProc(ByVal hwnd As Long, _
ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Select Case hwnd
Case hwnd1
    Select Case uMsg
        Case WM_DESTROY
            'notifichiamo la distruzione della finestra alla Sub Main
            dead = true
            'Lasciamo che Windows distrugga la finestra
            WinProc = DefWindowProc(hwnd, uMsg, wParam, lParam)
        Case Else
            'In tutti gli altri casi lasciamo che se ne occupi Windows
            WinProc = DefWindowProc(hwnd, uMsg, wParam, lParam)
    End Select
Case hwnd2
        Select Case uMsg
            Case 533
                MsgBox "Il pulsante č stato cliccato!!!"
                WinProc = CallWindowProc(old_proc2, hwnd2, uMsg, wParam, lParam)
            Case Else
                WinProc = CallWindowProc(old_proc2, hwnd2, uMsg, wParam, lParam)
End Select
End Function
Provate pure, dovrebbe funzionare alla perfezione. Il parametro extra di
CallWindowProc() č il puntatore alla funzione originale. Nel caso di una
finestra generica, DefWindowProc() va pių che bene. Ma per una finestra giā
discretamente evoluta, come un Button, dobbiamo fare uso di una funzione pių
specifica.
E LoadLibrary()? Anche stavolta vi ho fatto mettere una funzione inutile?
Giammai. Se anzichč quello che abbiamo fatto, avessimo fatto questo:
(dichiarazioni)
Dim richedlb As Long
(Sub Main)
If retval = 0 Then
    End
Else
    hwnd1 = CreateWindowEx(...)
    If hwnd1 = 0 Then
        UnregisterClass "myclass", App.hInstance
        End
    Else
        ShowWindow hwnd1, 1
        richedlb = LoadLibrary("RICHED32.DLL")
        hwnd2 = CreateWindowEx(0, "RichEdit", "Click me!", _
        WS_VISIBLE Or WS_CHILD, 10, 10, 200, 200, _
        hwnd1, 0, App.hInstance, ByVal 0&)
    End If
End If
Anzichč un misero Button abbiamo creato nientepopodimenoche una Rich Text Box!!!
Perchč la classe "RichEdit" viene registrata automaticamente dalla libreria
RICHED32.DLL, ma la libreria in questione concede i suoi servigi ad un thread
alla volta (mi ricorda qualcuno...), quindi ogni thread che intende usare una
Rich Text Box la deve caricare con LoadLibrary() (VB č strettamente
mono-threading, quindi non ci sarā mai bisogno di chiamarla pių di una volta per
libreria in ogni programma).
Certo, una Rich Text Box va proprio subclassata di brutto, perchč non č un
semplice coso simil-3D che si limita ad andare su e gių quando viene cliccato
con il mouse. Perō, cavolo, non male, che ve ne pare?
In ogni caso, ad ogni utilizzo della LoadLibrary dovrā corrispondere una
chiamata alla funzione FreeLibrary(). Nel nostro caso, appena prima di
PostQuitMessage(), aggiungiamo:
FreeLibrary richedlb
e cosė facciamo un po' di pulizia in memoria.
Se sapessi la Declare di InitCommonControls() vi insegnerei persino come fare a
meno dell'OCX dei Common Controls, pensate un po'. Magari la prossima volta.
Anzi, la prossima volta vi spiego tutti i controlli pių comuni, come le caselle
di testo, le combobox, eccetera
--
In ogni caso, per maggiori dettagli, consultate la Win32 Programmers' Reference
della Borland, dove ho imparato tutto quello che so sul subclassing, e molto
altro. Il libro in questione viene distribuito gratuitamente sotto forma di un
file HLP delle dimensioni approssimative di 17 MegaByte (č davvero
dettagliatissima), e si puō trovare in FTP, in circa 10-20 archivi pubblici di
universitā o organizzazioni senza scopo di lucro. Un tempo avevo tutti questi
indirizzi, ma tra un casino e l'altro li ho persi tutti. Ma se non sbaglio Pal
ne ha conservato almeno uno.
--
Se avete altri dubbi, chiedete pure. Sarō ben felice di arricchire questo
tutorial di nuove lezioni. Magari (azzardo) lo si potrebbe pubblicare anche sul
Sito Comune, una volta completo.
Un'idea che mi viene cosė su due piedi č insegnarvi come si condividono dati tra
due istanze del vostro programma usando RegisterWindowMessage() e SendMessage(),
appena imparerō a farlo io :). Alla prossima, in ogni caso. Che ho scritto
davvero tanto, e sono un po' stanco.

perchè mi fermai qui
Te lo avevo già proposto la volta scorsa: perché non impacchetti il tutto e ne fai un bel articolo
per il Sito Comune???
Ciao,
Moreno
#sirri#@#morenosoft#.com
(togliete "#" dall'indirizzo)
Moreno Sirri VB Site - 
http://www.morenosoft.com/MSVBSite/
---------------------------------------------------------------------------
Le risposte alle tue domande le trovi sul Sito Comune di questo NewsGroup:
 
http://www.murialdo.it/it_lang_vb
---------------------------------------------------------------------------

 per il Sito Comune???
approvo vivamente

Se lo dice anche pal, allora... ;)


Se lo dice anche pal, allora... ;)
:-((
Grazie, eh.... non ti parlo più!!!
;-))


 ;-))
:))

 wrote in message MKN25.218528$VM3.1...@news.infostrada.it...
