Sei sulla pagina 1di 85

I listati sono suddivisi per capitoli e per argomento

all'interno di ciascun capitolo.

Capitolo 1 – Il linguaggio
Interpretato o compilato?
Function DebugMode() As Boolean
On Error Resume Next
Debug.Print 1 / 0
DebugMode = (Err <> 0)
Err.Clear
End Function

Function DebugMode() As Boolean


Static Counter As Variant
If IsEmpty(Counter) Then
Counter = 1
Debug.Assert DebugMode() Or True
Counter = Counter - 1
ElseIf Counter = 1 Then
Counter = 0
End If
DebugMode = Counter
End Function

Attenzione agli elementi di un ParamArray


Function Max(ParamArray args() As Variant) As Variant
Dim result As Variant, index As Integer
result = args(LBound(args))
For index = LBound(args) + 1 To UBound(args)
If result < args(index) Then result = args(index)
Next
Max = result
End Function

Function Max(ParamArray args() As Variant) As Variant


Dim result As Variant, index As Integer
For index = LBound(args) To UBound(args)
If IsMissing(args(index)) Then
' ignora questo argomento
ElseIf IsEmpty(result) Then
' la prima volta si imposta un possibile valore di ritorno
result = args(index)
ElseIf result < args(index) Then
result = args(index)
End If
Next
Max = result
End Function

Confrontare velocemente gli UDT


Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As _
Any, source As Any, ByVal bytes As Long)

' una struttura UDT d'esempio che contiene praticamente ogni possibile tipo di dato
Private Type MyUDT
item1 As Boolean
item2(10) As Integer
item3 As Long
item4 As Single
item5 As Double
item6 As Currency
item7 As String * 20
End Type
Dim udt1 As MyUDT, udt2 As MyUDT

' inizializza il primo UDT


udt1.item1 = 10
udt1.item2(1) = 4
udt1.item3 = 12345
udt1.item4 = 345.567
udt1.item5 = 111.333444
udt1.item6 = 223344.5566
udt1.item7 = "this is a test"

' inizializza il secondo UDT


' (in questo test entrambi gli UDT contengono lo stesso valore)
udt2 = udt1

' il numero di byte da confrontare


Dim bytes As Long
bytes = LenB(udt1)

' le stringhe utilizzate per il confronto


Dim s1 As String, s2 As String
' le rende sufficientemente grandi da ospitare gli UDT
s1 = Space$((bytes + 1) \ 2)
s2 = s1

' copia gli UDT nelle stringhe


CopyMemory ByVal StrPtr(s1), ByVal VarPtr(udt1), bytes
CopyMemory ByVal StrPtr(s2), ByVal VarPtr(udt2), bytes

' ora è possibile effettuare il confronto


If s1 = s2 Then
MsgBox "Equal"
Else
MsgBox "Different"
End If

Creare un valore Missing


' non passare mai un argomento a questa funzione

Function MissingValue(Optional DontPassMe As Variant) As Variant


' se viene passato un argomento, solleva l'errore "Invalid Procedure Call"
If Not IsMissing(DontPassMe) Then Err.Raise 5
MissingValue = DontPassMe
End Function

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As _


Any, source As Any, ByVal bytes As Long)

Function MissingValue() As Variant


' un valore "missing" è in realtà un codice d'errore pari al valore esadecimale 80020004
' pertanto qui viene memorizzata la parte intera di tale valore
MissingValue = &H80020004
' e quindi si modifica il VarType in vbError (= 10)
CopyMemory MissingValue, 10, 2
End Function
Capitolo 2 - Numeri e Date
Convertire numeri esadecimali
Function HexToDec(HexValue As String) As Long
HexToDec = Val("&H" & HexValue)
End Function

Function HexToDec(HexValue As String, Optional ToInteger As Boolean) As Long


If ToInteger Then
' converte in un intero, se possibile
' Utilizzare CInt() se si desidera convertire *sempre* in un Integer
HexToDec = Val("&H" & HexValue)
Else
' converte sempre in un Long. È anche possibile utilizzare la funzione CLng().
HexToDec = Val("&H" & HexValue & "&")
End If
End Function

Nuove funzioni matematiche per VB


' logaritmo in base 10

Function Log10(number As Double) As Double


Log10 = Log(number) / 2.30258509299405
End Function

' conversione da gradi in radianti

Function DegreesToRadians(ByVal degrees As Single) As Single


DegreesToRadians = degrees / 57.29578
End Function

' conversione da radianti in gradi

Function RadiansToDegrees(ByVal radians As Single) As Single


RadiansToDegrees = radians * 57.29578
End Function

' la parte frazionaria di un numero


' (argomenti negativi restituiscono valori negativi)

Function Fract(number As Variant) As Variant


Fract = number - Fix(number)
End Function

' Un numero casuale nell'intervallo [low,high]

Function Rnd2(low As Single, high As Single) As Single


Rnd2 = Rnd * (high - low) + low
End Function

' Cotangente di un angolo dato

Function Cot(radians As Double) As Double


Cot = 1 / Tan(radians)
End Function

' arcotangent di Y/X – a differenza di ATN()


' restituisce un angolo nei quattro quadranti

Function Atn2(x As Double, y As Double) As Double


If x = 0 Then
Atn2 = Sgn(y) * 1.5707963267949
ElseIf x > 0 Then
Atn2 = Atn(y / x)
Else
Atn2 = Atn(y / x) + 3.14159265358979 * Sgn(y)
End If
End Function

' Il fattoriale di un numero


' errore se l'argomento è negativo o maggiore di 170

Function Factorial(ByVal number As Long) As Double


Static result(170) As Double
' questa routine è molto veloce perché precalcola
' in anticipo tutti i possibili risultati
If result(0) = 0 Then
' questa è la prima esecuzione
Dim i As Long
result(0) = 1
For i = 1 To 170
result(i) = result(i - 1) * i
Next
End If
' ora occorre solo restituire un elemento del vettore
Factorial = result(number)
End Function

Contare i bit in un intero


Function BitCount (ByVal number As Long) As Integer
Dim bits As Integer, temp As Long
temp = number
Do While temp
temp = temp And (temp - 1)
bits = bits + 1
Loop
BitCount = bits
End Function

Manipolare i bit di un numero


' Testa il valore di un bit

Function BitTest(ByVal value As Long, ByVal bit As Long) As Boolean


BitTest = (value And Power2(bit))
End Function

' Imposta a True (1) il valore di un bit

Function BitSet(ByVal value As Long, ByVal bit As Long) As Long


BitSet = (value Or Power2(bit))
End Function

' Imposta a False(0) il valore di un bit

Function BitClear(ByVal value As Long, ByVal bit As Long) As Long


BitClear = (value And Not Power2(bit))
End Function

' Calcola la potenza di 2


' l'esponente deve essere nell'intervallo [0,31]

Function Power2(ByVal exponent As Long) As Long


Static res(0 To 31) As Long
Dim i As Long
If exponent < 0 Or exponent > 31 Then Err.Raise 5
' inizializza il vettore alla prima chiamata
If res(0) = 0 Then
res(0) = 1
For i = 1 To 30
res(i) = res(i - 1) * 2
Next
' questo è un caso speciale
res(31) = &H80000000
End If
' restituisce il risultato
Power2 = res(exponent)
End Function

Un semplice valutatore di espressioni


Function EvalExpression(ByVal expression As String) As Double
Dim result As Double
Dim operand As Double
Dim opcode As String
Dim index As Integer
Dim lastIndex As Integer

' il carattere null marca la fine della stringa


expression = expression & vbNullChar

For index = 1 To Len(expression) + 1


If InStr("+-*/" & vbNullChar, Mid$(expression, index, 1)) Then
If lastIndex = 0 Then
' questo è il primo operando dell'espressione
result = Val(Left$(expression, index - 1))
Else
' estrae il nuovo operando
operand = Val(Mid$(expression, lastIndex, index - lastIndex))
' esegue l'operazione in sospeso
Select Case opcode
Case "+"
result = result + operand
Case "-"
result = result - operand
Case "*"
result = result * operand
Case "/"
result = result / operand
End Select
End If
opcode = Mid$(expression, index, 1)
lastIndex = index + 1
End If
Next
EvalExpression = LTrim$(result)

End Function

L'età di una persona


Function Age(BirthDate As Date, Optional CurrentDate As Variant, _
Optional ExactAge As Boolean) As Integer
If IsMissing(CurrentDate) Then CurrentDate = Now
Age = Year(CurrentDate) - Year(BirthDate)

If ExactAge Then
' sottrae uno se la data del compleanno dell'anno corrente deve ancora arrivare
If DateSerial(Year(CurrentDate), Month(BirthDate), _
Day(BirthDate)) > CurrentDate Then
Age = Age - 1
End If
End If
End Property

Tecniche con DateSerial


' l'ultimo giorno di un dato mese
Function EndOfMonth(Year As Integer, Month As Integer) As Date
EndOfMonth = DateSerial(Year, Month + 1, 0)
End Function

' il numero di giorni in un mese


Function DaysInMonth(Year As Integer, Month As Integer) As Integer
DaysInMonth = Day(DateSerial(Year, Month + 1, 0))
End Function

' True se si tratta di un anno bisestile


Function IsLeapYear(Year As Integer) As Boolean
IsLeapYear = (Day(DateSerial(Year, 3, 0)) = 29)
End Function

Una versione migliorata della funzione DatePart


Enum DateTimePart
dtYear
dtQuarter
dtMonth
dtMonthName
dtShortMonthName
dtDay
dtDayOfTheYear
dtWeekday
dtWeekdayName
dtShortWeekdayName
dtWeekOfTheYear
dtHour
dtMinute
dtSecond
End Enum

' La funzione DatePart migliorata


Function DatePartEx(ByVal Interval As DateTimePart, newDate As Date, _
Optional FirstDayOfWeek As VbDayOfWeek = vbSunday, Optional FirstWeekOfYear _
As VbFirstWeekOfYear = vbFirstJan1) As Variant
' Seleziona la funzione data/ora corretta e più veloce
Select Case Interval
Case dtYear
DatePartEx = Year(newDate)
Case dtQuarter
DatePartEx = DatePart("q", newDate, FirstDayOfWeek, FirstWeekOfYear)
Case dtMonth
DatePartEx = Month(newDate)
Case dtMonthName
DatePartEx = MonthName(Month(newDate), False)
Case dtShortMonthName
DatePartEx = MonthName(Month(newDate), True)
Case dtDay
DatePartEx = Day(newDate)
Case dtDayOfTheYear
DatePartEx = DatePart("y", newDate, FirstDayOfWeek, FirstWeekOfYear)
Case dtWeekday
DatePartEx = Weekday(newDate, FirstDayOfWeek)
Case dtWeekdayName
DatePartEx = Format(newDate, "dddd")
Case dtShortWeekdayName
DatePartEx = Format(newDate, "ddd")
Case dtWeekOfTheYear
DatePartEx = DatePart("ww", newDate, FirstDayOfWeek, _
FirstWeekOfYear)
Case dtHour
DatePartEx = Hour(newDate)
Case dtMinute
DatePartEx = Minute(newDate)
Case dtSecond
DatePartEx = Second(newDate)
End Select

End Function

Benchmark accurati al millisecondo


Private Type SMPTE
hour As Byte
min As Byte
sec As Byte
frame As Byte
fps As Byte
dummy As Byte
pad(2) As Byte
End Type
Private Type MMTIME
wType As Long
units As Long
smpteVal As SMPTE
songPtrPos As Long
End Type
Private Declare Function timeGetSystemTime Lib "winmm.dll" (lpTime As MMTIME, _
ByVal uSize As Long) As Long

' restituisce l'ora attuale del sistema (in millisecondi)

Function GetCurrentTime() As Long


' assegna questo valore al campo wType per
' specificare le misure temporali in millisecondi
Const TIME_MS = 1
Dim mmt As MMTIME
mmt.wType = TIME_MS
timeGetSystemTime mmt, LenB(mmt)
GetCurrentTime = mmt.units
End Function

Estrarre le informazioni sul fuso orario


Private Type SYSTEMTIME
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type
Private Type TIME_ZONE_INFORMATION
Bias As Long
StandardName(32) As Integer
StandardDate As SYSTEMTIME
StandardBias As Long
DaylightName(32) As Integer
DaylightDate As SYSTEMTIME
DaylightBias As Long
End Type
Private Declare Function GetTimeZoneInformation Lib "kernel32" _
(lpTimeZoneInformation As TIME_ZONE_INFORMATION) As Long

' restituisce le differenze di fuso orario dall'ora di Greenwich


'
' ad esempio -5 per New York, +1 per Roma

Function GetTimeZone() As Single


Dim tzInfo As TIME_ZONE_INFORMATION
GetTimeZoneInformation tzInfo
GetTimeZone = tzInfo.Bias / 60
End Function

' ---- versione migliorata

Private Declare Function GetTimeZoneInformationAny Lib "kernel32" Alias _


"GetTimeZoneInformation" (buffer As Any) As Long

' restituisce la differenza di fuso orario dall'ora di Greenwich


'
' ad esempio -5 per New York, +1 per Roma

Function GetTimeZone() As Single


Dim buffer(0 To 44) As Long
GetTimeZoneInformationAny buffer(0)
GetTimeZone = buffer(0) / -60
End Function

' ---- versione che tiene conto dell'ora legale

Private Function GetTimeZone() As Single


Dim retval As Long
Dim buffer(0 To 42) As Long

Const TIME_ZONE_ID_INVALID = &HFFFFFFFF


Const TIME_ZONE_ID_UNKNOWN = 0
Const TIME_ZONE_ID_STANDARD = 1
Const TIME_ZONE_ID_DAYLIGHT = 2

retval = GetTimeZoneInformationAny(buffer(0))

Select Case lreturnval


Case TIME_ZONE_ID_INVALID, TIME_ZONE_ID_UNKNOWN
GetTimeZone = 0
Case TIME_ZONE_ID_STANDARD
GetTimeZone = (buffer(0) + buffer(21)) / 60
'oppure (tzinfo.bias+tzinfo.standardbias)/60
Case TIME_ZONE_ID_DAYLIGHT
GetTimeZone = (buffer(0) + buffer(42)) / 60
'oppure (tzinfo.bias+tzinfo.Daylightbias)/60
Case Else
GetTimeZone = 0
End Select

End Function

' ---- versione finale

Private Function GetTimeZone() As Single


Dim retval As Long
Dim buffer(0 To 42) As Long

Const TIME_ZONE_ID_INVALID = &HFFFFFFFF


Const TIME_ZONE_ID_UNKNOWN = 0
Const TIME_ZONE_ID_STANDARD = 1
Const TIME_ZONE_ID_DAYLIGHT = 2

retval = GetTimeZoneInformationAny(buffer(0))

Select Case retval


Case TIME_ZONE_ID_INVALID
GetTimeZone = 0
Case TIME_ZONE_ID_STANDARD, TIME_ZONE_ID_UNKNOWN
GetTimeZone = (buffer(0) + buffer(21)) / 60
'oppure (tzinfo.bias+tzinfo.standardbias)/60
Case TIME_ZONE_ID_DAYLIGHT
GetTimeZone = (buffer(0) + buffer(42)) / 60
'oppure (tzinfo.bias+tzinfo.Daylightbias)/60
Case Else
GetTimeZone = 0
End Select

End Function

Eseguire benchmark con la funzione API


QueryPerformanceCounter
Private Declare Function QueryPerformanceFrequencyAny Lib "kernel32" Alias _
"QueryPerformanceFrequency" (lpFrequency As Any) As Long
Private Declare Function QueryPerformanceCounterAny Lib "kernel32" Alias _
"QueryPerformanceCounter" (lpPerformanceCount As Any) As Long

Private Sub Command1_Click()


Dim frequency As Currency
Dim startTime As Currency
Dim endTime As Currency
Dim result As Double

' ottiene il contatore della frequenza


' restituisce zero se l'hardware non gestisce contatori di prestazioni ad alta risoluzione
If QueryPerformanceFrequencyAny(frequency) = 0 Then
MsgBox "This computer doesn't support high-res timers", vbCritical
Exit Sub
End If

' avvia la misurazione


QueryPerformanceCounterAny startTime

' inserire in questo punto il codice da misurare, ad esempio...


Dim i As Long
For i = 1 To 1000000
Next

' termina la misurazione


QueryPerformanceCounterAny endTime

' sia il dividendo che il divisore sono scalati di un fattore


' 10.000, pertanto non è necessario scalare il risultato
result = (endTime - startTime) / frequency

' show the result


MsgBox result
End Sub

Estrarre le word da un valore Long


Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As _
Any, source As Any, ByVal bytes As Long)

' restituisce la word più significativa di un valore a 32-bit

Function HiWord(ByVal value As Long) As Integer


CopyMemory HiWord, ByVal VarPtr(value) + 2, 2
End Function

' restituisce la word meno significativa di un valore a 32-bit

Function LowWord(ByVal value As Long) As Integer


CopyMemory LowWord, value, 2
End Function
Capitolo 3 - Stringhe
Convertire opportunamente maiuscole e minuscole
Function ProperCase(text As String) As String
Dim i As Integer

' prepara il risultato


ProperCase = StrConv(text, vbProperCase)

' ripristina tutti i caratteri che sono stati convertiti in minuscolo


For i = 1 To Len(text)
Select Case Asc(Mid$(text, i, 1))
Case 65 To 90 ' A-Z
Mid$(ProperCase, i, 1) = Mid$(text, i, 1)
End Select
Next
End Function

Varianti della funzione InStr


Function InstrLast(ByVal Start As Long, Source As String, search As String, _
Optional CompareMethod As VbCompareMethod = vbBinaryCompare) As Long
Do
' ricerca l'occorrenza successiva con InStr
Start = InStr(Start, Source, search, CompareMethod)
If Start = 0 Then Exit Do
' ne abbiamo trovata una
InstrLast = Start
Start = Start + 1
Loop
' il valore restituito è l'ultima occorrenza trovata
End Function

' se Include è True o omesso, restituisce la prima occorrenza di uno dei caratteri
' specificati in Table, oppure zero se la ricerca fallisce
' se Include è False, restituisce la prima occorrenza di un qualsiasi carattere
' che NON compare in Table

Function InstrTbl(ByVal Start As Long, Source As String, Table As String, _


Optional Include As Boolean = True, Optional CompareMethod As _
VbCompareMethod = vbBinaryCompare) As Long
Dim i As Long
For i = Start To Len(Source)
If InStr(1, Table, Mid$(Source, i, 1), CompareMethod) > 0 Xor Not _
Include Then
InstrTbl = i
Exit Function
End If
Next
End Function

Estrarre le parole con l'oggetto RegExp


' Ottiene una collezione di tutte le parole di una stringa
' Se il secondo argomento è True, vengono restituite solo le parole univoche.
'
' NOTA: richiede un riferimento alla type library
' Microsoft VBScript Regular Expression
Function GetWords(ByVal Text As String, Optional DiscardDups As Boolean) As _
Collection
Dim re As New RegExp
Dim ma As Match

' il pattern seguente significa che si ricerca una lettera (\w)


' ripetuta una o più volte (il suffisso +) che si trova agli estremi di una parola
' (le sequenze \b iniziale e finale)
re.Pattern = "\b\w+\b"
' ricerca *tutte* le occorrenze del pattern
re.Global = True

' inizializza il risultato


Set GetWords = New Collection

' è necessario ignorare gli errori, se bisogna eliminare i duplicati


On Error Resume Next

' il metodo Execute esegue la ricerca e restituisce un oggetto MatchCollection


For Each ma In re.Execute(Text)
If DiscardDups Then
' se bisogna eliminare i duplicati, basta aggiungere semplicemente una chiave
' alla collezione degli elementi
' ed il metodo Add si farà carico dell'operazione
GetWords.Add ma.Value, ma.Value
Else
' altrimenti basta aggiungere al risultato
GetWords.Add ma.Value
End If
Next

End Function

'--------- esempio -----------------

' Conta quanti articoli appaiono in una stringa sorgente


' contenuta nella casella di testo txtSource
Dim v As Variant
Dim count As Long

For Each v In GetWords(txtSource.Text)


Select Case LCase$(v)
Case "the", "a", "an"
count = count + 1
End Select
Next
MsgBox "Found " & count & " articles."

Ricercare più sottostringhe con l'oggetto RegExp


' NOTA: questo codice richiede un riferimento alla type library
' Microsoft VBScript Regular Expression

Dim re As New RegExp


Dim ma As Match

re.Pattern = "january|february|march|april|may|june|july|september|october" _
& "|november|december"

' le differenze tra maiuscole e minuscole non sono significative


re.IgnoreCase = True
' si vogliono tutte le occorrenze del pattern
re.Global = True

' si assume che la stringa da analizzare sia contenuta nella variabile sourceText
For Each ma In re.Execute(sourceText)
Print "Found '" & ma.Value & "' at index " & ma.FirstIndex
Next

' ricerca tutte le parole specificate nel vettore passato come secondo argomento
' restituisce una matrice bidimensionale di Variant, dove arr(0,n) è l'n-esima
' parola corrispondente ed arr(1,n) è l'indice al quale è stata trovata la parola

' NOTA: richiede un riferimento alla type library


' Microsoft VBScript Regular Expression

Function InstrAllWords(ByVal Text As String, words() As String, _


Optional IgnoreCase As Boolean) As Variant
Dim re As New RegExp
Dim ma As Match
Dim maCol As MatchCollection
Dim index As Long

' crea il pattern nella forma "\b(word1|word2|....|wordN)\b"


re.pattern = "\b(" & Join(words, "|") & ")\b"
' si vogliono tutte le occorrenze del pattern
re.Global = True
' le differenze tra maiuscole e minuscole non sono significative?
re.IgnoreCase = IgnoreCase

' ottiene il risultato


Set maCol = re.Execute(Text)

' ora è possibile dimensionare la matrice risultato


ReDim res(1, maCol.Count) As Variant

' sposta i risultato nella matrice


For Each ma In maCol
index = index + 1
res(0, index) = ma.Value
res(1, index) = ma.FirstIndex
Next

' return to caller


InstrAllWords = res
End Function

'------- esempio di utilizzo ---------


' popola una matrice con le parole desiderate
Dim words(2) As String
words(0) = "Visual": words(1) = "Basic": words(2) = "Windows"

Dim arr() as Variant


arr = InstrAllWords(txtSource.Text, words())
For i = 1 To UBound(arr, 2)
Print "'" & arr(0, i) & "' at index " & arr(1, i)
Next
Capitolo 4 - Array e Collection
Le variabili semplici sono sempre più veloci degli elementi dei
vettori
Function AnyDuplicates(intArray() As Integer) As Boolean
' restituisce True se il vettore contiene valori duplicati
Dim i As Long, j As Long,
Dim lastItem As Long
Dim value As Integer

' calcola UBound() una sola volta


lastItem = UBound(intArray)
For i = LBound(intArray) To lastItem
' memorizza intArray(i) in una variabile semplice (non vettore)
' risparmia un'operazione di indicizzazione all'interno del ciclo interno
value = intArray(i)
For j = i + 1 To lastItem
If value = intArray(j) Then
AnyDuplicates = True
Exit Function
End If
Next
Next
' non sono stati trovati duplicati
AnyDuplicates = False
End Function

Eliminare i duplicati in un vettore


' Filter out duplicate values in an array and compact
' the array by moving items to "fill the gaps".
' Returns the number of duplicate values
'
' it works with arrays of any type, except objects
'
' The array is not REDIMed, but you can do it easily using
' the following code:
' a() is a string array
' dups = FilterDuplicates(a())
' If dups Then
' ReDim Preserve a(LBound(a) To UBound(a) - dups) As String
' End If

Function FilterDuplicates(arr As Variant) As Long


Dim col As Collection, index As Long, dups As Long
Set col = New Collection

On Error Resume Next

For index = LBound(arr) To UBound(arr)


' build the key using the array element
' an error occurs if the key already exists
col.Add 0, CStr(arr(index))
If Err Then
' we've found a duplicate
arr(index) = Empty
dups = dups + 1
Err.Clear
ElseIf dups Then
' if we've found one or more duplicates so far
' we need to move elements towards lower indices
arr(index - dups) = arr(index)
arr(index) = Empty
End If
Next

' return the number of duplicates


FilterDuplicates = dups

End Function

'-------- esempio ----------------

' a() è un vettore di stringhe che contiene duplicati


Dups = FilterDuplicates(a())
If dups Then
ReDim Preserve a(Lbound(a) To Ubound(a) – dups) As String
End If
Una tecnica non documentata per velocizzare le funzioni che
restituiscono vettori
' restituisce un vettore di N elementi casuali
' e memorizza la rispettiva media in AVG
Function GetRandomArray(ByVal n As Long, avg As Single) As Single()
Dim i As Long, sum As Single
ReDim res(1 To n) As Single
' popola il vettore con valori casuali e ne calcola il totale
Randomize Timer
For i = 1 To n
res(i) = Rnd
sum = sum + res(i)
Next
' assegna il vettore al risultato quindi calcola la media
GetRandomArray = res
avg = sum / n
End Function

'------- variante ottimizzata -----------

' restituisce un vettore di N elementi casuali


' e memorizza la rispettiva media in AVG
Function GetRandomArray(ByVal n As Long, avg As Single) As Single()
Dim i As Long, sum As Single
ReDim res(1 To n) As Single
' popola il vettore con valori casuali e ne calcola il totale
Randomize Timer
For i = 1 To n
res(i) = Rnd
sum = sum + res(i)
Next
' calcola la media QUINDI assenga la matrice risultato
avg = sum / n
GetRandomArray = res
End Function

Una routine per il sort di un vettore


Sub ShellSort(arr As Variant, Optional lastEl As Variant, _
Optional descending As Boolean)
Dim value As Variant
Dim index As Long, index2 As Long
Dim firstEl As Long
Dim distance As Long
Dim numEls As Long
' gestisci il parametro opzionale
If IsMissing(lastEl) Then lastEl = UBound(arr)
firstEl = LBound(arr)
numEls = lastEl - firstEl + 1
' trova il valore migliore per distance
Do
distance = distance * 3 + 1
Loop Until distance > numEls

Do
distance = distance \ 3
For index = distance + firstEl To lastEl
value = arr(index)
index2 = index
Do While (arr(index2 - distance) > value) Xor descending
arr(index2) = arr(index2 - distance)
index2 = index2 - distance
If index2 - distance < firstEl Then Exit Do
Loop
arr(index2) = value
Next
Loop Until distance = 1
End Sub

CountSort, un caso particolare di ordinamento indicizzato


Sub NdxCountSortI(arr() As Integer, ndx() As Long, Optional ByVal numEls As _
Variant)
Dim index As Long
Dim inverseOrder As Boolean
Dim value As Integer
Dim minValue As Integer
Dim maxValue As Integer
Dim saveCount As Integer
Dim newIndex As Long
' tiene conto degli argomenti ordinati
If IsMissing(numEls) Then numEls = UBound(arr)

' cerca i valori minimo e massimo


minValue = arr(LBound(arr))
maxValue = minValue
For index = LBound(arr) To numEls
value = arr(index)
If value < minValue Then
minValue = value
ElseIf value > maxValue Then
maxValue = value
End If
Next

' dichiara la matrice di indici temporanea


ReDim Count(minValue To maxValue) As Long
' popola ogni elemento della matrice temporanea con
' il numero di occorrenze del valore in questione
For index = LBound(arr) To numEls
value = arr(index)
Count(value) = Count(value) + 1
Next

' analizza la matrice count() per sostituire il valore di count


' con la posizione definitiva
' nella matrice ordinata
newIndex = LBound(ndx)
For index = minValue To maxValue
saveCount = Count(index)
Count(index) = newIndex
newIndex = newIndex + saveCount
Next

' infine crea l'indice


For index = LBound(arr) To numEls
' ottiene la posizione dell'elemento nell'indice
newIndex = Count(arr(index))
' memorizza la posizione originale
ndx(newIndex) = index
' la volta successiva memorizza il valore nell'elemento seguente
Count(arr(index)) = newIndex + 1
Next
End Sub

Ordinare rispetto a più chiavi


Type TEmployees
name As String * 40
dept As String * 12
salary As Currency
End Type

' carica il vettore dal disco


ReDim employees(1 To 1) As TEmployees
Open "employees.dat" For Binary As #1
numEls = LOF(1) / Len(employees(1))
ReDim employees(1 To numEls) As TEmployees
Get #1, , employees()
Close #1

' crea un vettore temporaneo contenente i dati chiave


ReDim keys(1 To numEls) As String
For index = 1 To numEls
keys(index) = employees(index).dept & employees(index).name
Next
' esegue l'ordinamento indicizzato discendente
ReDim ndx(1 To numEls) As Long
' questa routine può essere scaricata dal seguente URL
' http://www.vb2themax.com/Item.asp?PageID=CodeBank&ID=78
NdxShellSort keys(), ndx(), , True

' visualizza il nome degli impiegati


' per ciascun dipartimento
dept = ""
For index = 1 To numEls
With employees(ndx(index))
If .dept <> dept Then
dept = .dept
Debug.Print "Dept: " & dept
End If
Debug.Print .name, .salary
End With
Next
Ricerca binaria in un vettore ordinato
Function BinarySearch(arr As Variant, search As Variant, _
Optional lastEl As Variant) As Long
Dim index As Long
Dim first As Long
Dim last As Long
Dim middle As Long
Dim inverseOrder As Boolean

' gestisci il parametro opzionale


If IsMissing(lastEl) Then lastEl = UBound(arr)
first = LBound(arr)
last = lastEl

' deduci la direzione dell'ordinamento


inverseOrder = (arr(first) > arr(last))
BinarySearch = first - 1
Do
middle = (first + last) \ 2
If arr(middle) = search Then
BinarySearch = middle
Exit Do
ElseIf ((arr(middle) < search) Xor inverseOrder) Then
first = middle + 1
Else
last = middle - 1
End If
Loop Until first > last
End Function

Velocizzare le ricerche con le tabelle di hash


Function Checksum(text As String) As Long
Dim sum As Long, i As Long
Dim bArray() As Byte
' trasforma in un vettore di byte
bArray() = StrConv(text, vbFromUnicode)

' calcola la somma degli elementi del vettore


For i = 0 To UBound(bArray)
sum = sum + bArray(i) * i
Next
Checksum = sum
End Function

' ----- lettura parole da disco

numEls = 10000
ReDim words(numEls) As String
ReDim hashTable(2 * numEls) As Integer
' legge le parole da un file
Open "words.dat" For Input As #1
For index = 1 To numEls
Line Input#1, words(index)
Next
Close #1

' ----- inserimento delle parole

For index = 1 To numEls


' ricerca l'indice di hash corretto
hashIndex = Checksum(words(index))
' cicla fino a che non si trova uno slot libero
Do
hashIndex = (hashIndex Mod numEls) + 1
Loop While hashTable(hashIndex)
' Memorizza l'indice nella tabella di hash
hashTable(hashIndex) = index
Next

' ---------- ricerca ---------------

search = Text1.Text
hashIndex = Checksum(search)
Do
hashIndex = (hashIndex Mod numEls) + 1
Loop While hashTable(hashIndex) And words(hashTable(hashIndex)) <> search
If hashTable(hashIndex) Then
Print "Item found at index " & hashTable(hashIndex)
Else
Print "Item not found"
End If
Il numero di dimensioni di una matrice
Function ArrayDims(arr As Variant) As Integer
Dim i As Integer, bound As Long
On Error Resume Next
For i = 1 To 60
bound = LBound(arr, i)
If Err Then
ArrayDims = i - 1
Exit Function
End If
Next
End Function

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As _


Any, source As Any, ByVal bytes As Long)

Function ArrayDims(arr As Variant) As Integer


Dim ptr As Long
Dim VType As Integer

Const VT_BYREF = &H4000&

' ottiene il VarType effetivo dell'argomento


' questo è simile al VarType(), ma restituisce anche il bit VT_BYREF
CopyMemory VType, arr, 2

' esce se non si tratta di una matrice


If (VType And vbArray) = 0 Then Exit Function

' ottiene l'indirizzo del descrittore SAFEARRAY


' questo viene memorizzato nella seconda metà del parametro
' Variant che contiene la matrice
CopyMemory ptr, ByVal VarPtr(arr) + 8, 4

' vede se alla matrice è stato passato un Variant


' contenente una matrice, piuttosto che direttamente una matrice
' nel primo caso ptr punta già alla struttura SA.

If (VType And VT_BYREF) Then


' ptr è un puntatore a un puntatore
CopyMemory ptr, ByVal ptr, 4
End If

' ottiene l'indirizzo della struttura SAFEARRAY


' questo è memorizzato nel descrittore

' ottiene la prima parola della struttura SAFEARRAY


' che contiene il numero di dimensioni
' ...ma prima controlla che saAddr sia diverso da zero, altrimenti
' la routine non funziona quando la matrice viene deinizializzata
If ptr Then
CopyMemory ArrayDims, ByVal ptr, 2
End If
End Function
Capitolo 5 - Form e controlli
Spostare il focus con i tasti freccia su e freccia giù
Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
If KeyCode = vbKeyUp Then
If TypeOf ActiveControl Is TextBox Then
' freccia su – passa al controllo precedente
SendKeys "+{Tab}"
KeyCode = 0
End If
ElseIf KeyCode = vbKeyDown Then
If TypeOf ActiveControl Is TextBox Then
' freccia giù – passa al controllo successivo
SendKeys "{Tab}"
KeyCode = 0
End If
End If
End Sub

Modificare aspetto delle TextBox e ComboBox disabilitate


' abilita/disabilita una casella di testo o una casella combinata
' (modifica anche i relativi sfondi)

Sub EnableTextBox(tb As Control, ByVal Enabled As Boolean)


tb.Enabled = Enabled
If Enabled Then
tb.BackColor = vbWindowBackground
Else
tb.BackColor = vbInactiveBorder
End If
End Sub

Assicurarsi che i controlli TextBox e ComboBox di una form


abbiano la stessa altezza
' modifica l'altezza di tutte le caselle di testo a riga singola di una form

Sub SetTextboxHeight(frm As Form, ByVal Height As Single)


Dim ctrl As Control

For Each ctrl In frm.Controls


If TypeOf ctrl Is TextBox Then
If ctrl.MultiLine = False Then
ctrl.Height = Height
End If
End If
Next
End Sub

Private Sub Form_Load()


SetTextboxHeight Me, Combo1.Height
End Sub

Verificare se una form è caricata o meno


' restituisce il numero delle istanze di una form
' attualmente caricate

Function FormCount(ByVal frmName As String) As Long


Dim frm As Form
For Each frm In Forms
If StrComp(frm.Name, frmName, vbTextCompare) = 0 Then
FormCount = FormCount + 1
End If
Next
End Function

Modificare lo stile di un controllo CheckBox o OptionButton a


runtime
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal newValue As Long) As Long

Const GWL_STYLE = (-16)


Const BS_PUSHLIKE = &H1000&

Sub SetButtonStyle(Ctrl As Control, ByVal Graphical As Boolean)


If Graphical Then
SetWindowLong Ctrl.hWnd, GWL_STYLE, GetWindowLong(Ctrl.hWnd, _
GWL_STYLE) Or BS_PUSHLIKE
Else
SetWindowLong Ctrl.hWnd, GWL_STYLE, GetWindowLong(Ctrl.hWnd, _
GWL_STYLE) And Not BS_PUSHLIKE
End If
Ctrl.Refresh
End Sub

Modificare le dimensioni del cursore testo e l'intermittenza del


lampeggio
Declare Function CreateCaret Lib "user32" (ByVal hWnd As Long, _
ByVal hBitmap As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Declare Function ShowCaret Lib "user32" (ByVal hWnd As Long) As Long
Declare Function SetCaretBlinkTime Lib "user32" (ByVal wMSeconds As Long) As _
Long
Declare Function GetCaretBlinkTime Lib "user32" () As Long

Sub ShowCustomCaret(ByVal width As Integer, ByVal height As Integer)


On Error Resume Next
With Screen.ActiveForm.ActiveControl
CreateCaret .hWnd, 0, width, height
ShowCaret .hWnd
End With
End Sub

Modificare la proprietà ShowInTaskbar a runtime


Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Const GWL_EXSTYLE = (-20)
Private Const WS_EX_APPWINDOW = &H40000

Private Sub Form_Load()


' nasconde la form sulla taskbar
SetWindowLong Me.hWnd, GWL_EXSTYLE, (GetWindowLong(hWnd, _
GWL_EXSTYLE) And Not WS_EX_APPWINDOW)
End Sub

Private Sub Form_Load()


' mostra la form sulla taskbar
SetWindowLong Me.hWnd, GWL_EXSTYLE, (GetWindowLong(hWnd, _
GWL_EXSTYLE) Or WS_EX_APPWINDOW)
End Sub

Modificare la larghezza dell'elenco a discesa di una


ComboBox
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const CB_SETDROPPEDWIDTH = &H160

' imposta la larghezza dell'area dell'elenco di una ComboBox (in pixel)

Sub SetComboDropDownWidth(ComboBox As ComboBox, ByVal lWidth As Long)


SendMessage ComboBox.hWnd, CB_SETDROPPEDWIDTH, lWidth, ByVal 0&
End Sub

Clonare un oggetto Font


Function CloneFont(Font As StdFont) As StdFont
Set CloneFont = New StdFont
CloneFont.Name = Font.Name
CloneFont.Size = Font.Size
CloneFont.Bold = Font.Bold
CloneFont.Italic = Font.Italic
CloneFont.Underline = Font.Underline
CloneFont.Strikethrough = Font.Strikethrough
End Function

Function CloneFont(Font As StdFont) As StdFont


Dim pb As New PropertyBag
' copia le proprietà Font in un oggetto PropertyBag
pb.WriteProperty "Font", Font
' e quindi crea da questo un nuovo oggetto Font
Set CloneFont = pb.ReadProperty("Font")
End Function

Function CloneFont(Font As IFont) As StdFont


Font.Clone CloneFont
End Function

Impostare correttamente la larghezza e l'altezza delle barre di


scorrimento
Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) _
As Long

Sub ResizeAllScrollbars(frm As Object)


Dim hsbHeight As Single
Dim vsbWidth As Single
Dim ctrl As Control

Const SM_CXVSCROLL = 2
Const SM_CYHSCROLL = 3

' determina le dimensioni suggerite per le barre di scorrimento (in twips)


hsbHeight = GetSystemMetrics(SM_CYHSCROLL) * Screen.TwipsPerPixelY
vsbWidth = GetSystemMetrics(SM_CXVSCROLL) * Screen.TwipsPerPixelX

' itera su tutti i controlli dalla form


For Each ctrl In frm.Controls
Select Case TypeName(ctrl)
Case "HScrollBar"
ctrl.Height = hsbHeight
Case "VScrollBar"
ctrl.Width = vsbWidth
Case "FlatScrollBar"
If ctrl.Orientation = 1 Then
ctrl.Height = hsbHeight
Else
ctrl.Width = vsbWidth
End If
End Select
Next

End Sub

Aggiungere una barra di scorrimento orizzontale ad un


controllo ListBox
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const LB_SETHORIZONTALEXTENT = &H194
Const LB_GETHORIZONTALEXTENT = &H193

' imposta l'estensione orizzontale del controllo (in pixel).


' Se questo valore è maggiore della larghezza attuale del controllo
' apparirà una barra di scorrimento orizzontale.

Sub SetHorizontalExtent(lb As ListBox, ByVal newWidth As Long)


SendMessage lb.hwnd, LB_SETHORIZONTALEXTENT, newWidth, ByVal 0&
End Sub

' restituisce l'estensione orizzontale del controllo (in pixel).

Function GetHorizontalExtent(lb As ListBox) As Long


GetHorizontalExtent = SendMessage(lb.hwnd, LB_GETHORIZONTALEXTENT, 0, _
ByVal 0&)
End Function

Creare pulsanti di comando colorati


Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As _
Integer

Private Sub Form_Load()


With Option1
' sposta il controllo CommandButton sotto l'OptionButton,
Command1.Move .Left, .Top, .Width, .Height
' ma restringe le dimensioni di quest'ultimo in modo da rendere visibile il bordo
.Move .Left + ScaleX(1, vbPixels, ScaleMode), .Top + ScaleY(1, vbPixels, _
ScaleMode), .Width - 2 * ScaleX(1, vbPixels, ScaleMode), _
.Height - 2 * ScaleY(1, vbPixels, ScaleMode)
' verifica che l'OptionButton sia in primo piano
.ZOrder
' l'utente non può spostarsi con il tab sull'OptionButton
.TabStop = False
End With
End Sub

Private Sub Option1_Click()


If GetAsyncKeyState(vbKeyLButton) = 0 And Option1.Value Then
' se l'Optionbutton viene attivato tramite una combinazione di tasti
' o l'utente ha premuto il tasto del mouse su di esso e l'ha quindi rilasciato
' è necessario ripristinare il suo aspetto
Option1.Value = False
' e quindi scatenare l'evento Click del pulsante vero e proprio
Command1.SetFocus
Command1.Value = True
End If
End Sub

Private Sub Option1_MouseUp(Button As Integer, Shift As Integer, X As Single, _


Y As Single)
' scatena nuovamente l'evento Click quando il pulsante del mouse viene rilasciato
Option1_Click
End Sub

Private Sub Command1_Click()


' qui va inserito il codice che si desidera eseguire
' quando si preme il pulsante
End Sub

Creare controlli ListBox dotati di pulsanti di spostamento


Private Sub cmdUp_Click()
' se il primo elemento non è quello corrente
If List1.ListIndex >= 0 Then
' aggiunge un elemento duplicato all'inizio
List1.AddItem List1.Text, List1.ListIndex - 1
' lo rende l'elemento corrente
List1.ListIndex = List1.ListIndex - 2
' elminina la vecchia occorrenza di questo elemento
List1.RemoveItem List1.ListIndex + 2
End If
End Sub

Private Sub cmdDown_Click()


' solo se l'ultimo elemento non è quello corrente
If List1.ListIndex <> -1 And List1.ListIndex < List1.ListCount - 1 Then
' aggiunge un elemento duplicato alla fine
List1.AddItem List1.Text, List1.ListIndex + 2
' lo rende l'elemento corrente
List1.ListIndex = List1.ListIndex + 2
' elimina la vecchia occorrenza di questo elemento
List1.RemoveItem List1.ListIndex - 2
End If
End Sub

Private Sub List1_Click()


' abilita/disabilita i pulsanti quando l'elemento corrente cambia
cmdUp.Enabled = (List1.ListIndex > 0)
cmdDown.Enabled = (List1.ListIndex <> -1 And List1.ListIndex < _
List1.ListCount - 1)
End Sub

Private Sub Form_Load()


' dal momento che non vi è alcun elemento corrente (ListIndex = -1)
' è necessario invocare manualmente la procedura Click
Call List1_Click
End Sub

Tagliare, copiare ed incollare con le funzioni API di Windows


Private Const WM_CUT = &H300
Private Const WM_COPY = &H301
Private Const WM_PASTE = &H302
Private Const WM_CLEAR = &H303
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long

' copia il contenuto di un controllo nella Clipboard


Sub ControlCopy(ByVal hWnd As Long)
SendMessage hWnd, WM_COPY, 0, ByVal 0&
End Sub

' taglia il contenuto di un controllo e lo copia nella Clipboard


Sub ControlCut(ByVal hWnd As Long)
SendMessage hWnd, WM_CUT, 0, ByVal 0&
End Sub

' incolla il contenuto della Clipboard in un controllo


Sub ControlPaste(ByVal hWnd As Long)
SendMessage hWnd, WM_PASTE, 0, ByVal 0&
End Sub

' elimina il contenuto selezionato in un controllo


Sub ControlDelete(ByVal hWnd As Long)
SendMessage hWnd, WM_CLEAR, 0, ByVal 0&
End Sub

Determinare in che modo un controllo ha ottenuto il focus


Private Declare Function GetKeyState Lib "user32" Alias "GetKeyState" (ByVal _
nVirtKey As Long) As Integer

Function GotFocusMethod() As Integer


If GetKeyState(vbKeyTab) < 0 Then
If (GetKeyState(vbKeyShift) < 0) Then
' è stata premuta la combinazione di tasti Shift-Tab
GotFocusMethod = 4
Else
' è stato premuto il tasto Tab
GotFocusMethod = 1
End If
ElseIf GetKeyState(vbKeyMenu) < 0 Then
' è stato premuto il tasto Alt (attivazione della sequenza di tasti)
GotFocusMethod = 2
ElseIf GetKeyState(vbLeftButton) < 0 Then
' è stato premuto il pulsante sinistro del mouse
GotFocusMethod = 3
End If
End Function

Determinare la riga corrente in una casella di testo multiriga


Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const EM_LINEFROMCHAR = &HC9

Function GetTextBoxCurrentLine(TB As TextBox) As Long


GetTextBoxCurrentLine = SendMessage(TB.hWnd, EM_LINEFROMCHAR, -1, _
ByVal 0&) + 1
End Function

Determinare se un controllo possiede una barra di


scorrimento
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long) As Long
Const GWL_STYLE = (-16)
Const WS_VSCROLL = &H200000
Const WS_HSCROLL = &H100000

Function HasHorizontalScrollbar(ctrl As Control) As Boolean


HasHorizontalScrollbar = (GetWindowLong(ctrl.hwnd, _
GWL_STYLE) And WS_HSCROLL)
End Function

Function HasVerticalScrollbar(ctrl As Control) As Boolean


HasVerticalScrollbar = (GetWindowLong(ctrl.hwnd, GWL_STYLE) And WS_VSCROLL)
End Function

Creare una form padre con le funzioni API


Private Declare Function SetWindowLong& Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long)
Const GWL_HWNDPARENT = (-8)

Function SetOwner(ByVal HwndtoUse, ByVal HwndofOwner) As Long


SetOwner = SetWindowLong(HwndtoUse, GWL_HWNDPARENT, HwndofOwner)
End Function

' ------ esempio ---------

Dim frm As Form2


Dim oldOwner As Long

Private Sub cmdShowForm_Click()


' mostra la form
Set frm = Form2
frm.Show
' la rende figlia dela form corrente
oldOwner = SetOwner(frm.hwnd, Me.hwnd)
End Sub

Private Sub cmdUnloadForm_Click()


' ripristina la form padre originale
SetOwner frm.hwnd, oldOwner
' scarica la form
Unload frm
End Sub

Ricevere eventi da un timer mentre è attiva una message box


Private Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal _
hWnd As Long, ByVal lpText As String, ByVal lpCaption As String, _
ByVal wType As Long) As Long

Private Sub Command1_Click()


MsgBox "The Timer STOPS!", vbOKOnly, "Regular MsgBox"
End Sub

Private Sub Command2_Click()


MessageBox Me.hWnd, "The Timer DOESN'T STOP", "MessageBox API function", _
vbOKOnly + vbExclamation
End Sub

Private Sub Timer1_Timer()


Label1.Caption = Time
End Sub

Function MsgBox2(Prompt As String, Optional Buttons As VbMsgBoxStyle, _


Optional Title As Variant) As VbMsgBoxResult
If IsMissing(Title) Then Title = App.Title
MsgBox2 = MessageBox(Screen.ActiveForm.hWnd, Prompt, Title, Buttons)
End Function

Abilitare e disabilitare completamente o in parte la barra di


scorrimento
Private Declare Function EnableScrollBar Lib "user32" (ByVal hwnd As Long, _
ByVal wSBflags As Long, ByVal wArrows As Long) As Long
Private Const SB_HORZ = 0 ' barra di scorrimento orizzontale
Private Const SB_VERT = 1 ' barra di scorrimento veriticale
Private Const SB_CTL = 2 ' controllo Scrollbar
Private Const SB_BOTH = 3 ' entrambe le barre

Private Const ESB_ENABLE_BOTH = &H0 ' abilita entrambe le frecce


Private Const ESB_DISABLE_LTUP = &H1 ' disabilita le frecce verso sinistra/verso l'alto
Private Const ESB_DISABLE_RTDN = &H2 ' disabilita le frecce verso destra/verso il basso
Private Const ESB_DISABLE_BOTH = &H3 ' disabilita entrambe le frecce

Private Sub VScroll1_Change()


SetScrollbarArrows VScroll1
End Sub

Sub SetScrollbarArrows(sbar As Control)


Dim barType As Long
' prima riabilita entrambe le frecce
EnableScrollBar sbar.hWnd, SB_CTL, ESB_ENABLE_BOTH
' quindi ne disabilita una se necessario
If sbar.Value = sbar.Min Then
EnableScrollBar sbar.hWnd, SB_CTL, ESB_DISABLE_LTUP
ElseIf sbar.Value = sbar.Max Then
EnableScrollBar sbar.hWnd, SB_CTL, ESB_DISABLE_RTDN
End If
End Sub

Verificare che il caret di una TextBox sia visibile


Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const EM_SCROLLCARET = &HB7

' assicura che il caret sia visibile.

Sub ScrollCaret(tb As TextBox)


SendMessage tb.hwnd, EM_SCROLLCARET, 0, ByVal 0&
End Sub
Attivare l'interfaccia utente estesa per i controlli ComboBox
Const CB_SETEXTENDEDUI = &H155
Const CB_GETEXTENDEDUI = &H156

Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As _


Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long

' ottiene lo stato della modalità ExtendedUI


' Esempio:
' MsgBox "ExtendedUI ?: " & GetComboExtendedUIMode(Combo1)

Function GetComboExtendedUIMode(ComboCtl As ComboBox) As Boolean


GetComboExtendedUIMode = SendMessage(ComboCtl.hwnd, CB_GETEXTENDEDUI, 0, _
ByVal 0)
End Function

' determina se abilitare la modalitò ExtendedUI


'
' Esempio: abilita la modalità ExntendedUI
' SetComboExtendedUIMode Combo1, True
' Esempio: disabilita la modalità ExntendedUI
' SetComboExtendedUIMode Combo1, False

Function SetComboExtendedUIMode(ComboCtl As ComboBox, ByVal bState As Boolean)


SendMessage ComboCtl.hwnd, CB_SETEXTENDEDUI, Abs(bState), ByVal 0
End Function

Ottenere un riferimento ad una form dato il suo nome


' estrae un riferimento ad una form dato il suo nome
' se vi sono più occorrenze della stessa form, estrae
' la prima che appare nella collezione Forms

Function GetForm(ByVal formName As String) As Form


Dim frm As Form
For Each frm In Forms
If StrComp(frm.Name, formName, vbTextCompare) = 0 Then
Set GetForm = frm
Exit Function
End If
Next
End Function

Implementare la proprietà MaxLength del controllo


ComboBox
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const CB_LIMITTEXT = &H141

' Imposta il numero massimo di caratteri che possono essere immessi in un controllo ComboBox

Sub SetComboMaxLength(ComboBox As ComboBox, ByVal lMaxLength As Long)


SendMessageLong ComboBox.hWnd, CB_LIMITTEXT, lMaxLength, Byval 0&)
End Sub

Ricerche incrementali all'interno dei controlli ListBox


Private Declare Function SendMessage Lib "User32" Alias "SendMessageA" (ByVal _
hWnd As Long, ByVal msg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const LB_FINDSTRING = &H18F

' questa è una variabile a livello di form


Dim DoSearch As Integer

Sub List1_Click()
' l'utente ha selezionato un nuovo elemento
' (attivato anche da Text1_KeyDown)
Text1.text = List1.text
End Sub

Sub List1_MouseDown(Button As Integer, Shift As Integer, X As Single, _


Y As Single)
' mantiene sincronizzati i due controlli
Text1.text = List1.text
End Sub

Sub List1_MouseMove(Button As Integer, Shift As Integer, X As Single, _


Y As Single)
' mantiene sincronizzati i due controlli
Text1.text = List1.text
End Sub

Sub Text1_Change()
Static active As Boolean
Dim index As Integer
Dim search As String
Dim found As Integer

' impedisce chiamate ricorsive


If active Or DoSearch = False Then Exit Sub
active = True
' la ricerca non dipende dalle differenze tra maiuscole e minuscole
search = UCase$(Text1.text)
found = -1

' cerca il primo elemento della casella di riepilogo


' che corrisponde alla stringa di ricerca immessa nella casella di testo
found = SendMessage(List1.hWnd, LB_FINDSTRING, -1, ByVal search)
If found >= 0 Then
' rende il valore trovato l'elemento corrente
List1.ListIndex = found
Text1.text = List1
' seleziona i caratteri rimanenti
Text1.SelStart = Len(search)
Text1.SelLength = 999
End If
active = False
End Sub

Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer)


If Shift Then
' non fa nulla se viene premuto un tasto Shift
ElseIf KeyCode = vbKeyUp Then
' si sposta sull'elemento precedente
If List1.ListIndex > 0 Then
List1.ListIndex = List1.ListIndex - 1
End If
KeyCode = 0
ElseIf KeyCode = vbKeyDown Then
' si sposta sull'elemento successivo
If List1.ListIndex < List1.ListCount - 1 Then
List1.ListIndex = List1.ListIndex + 1
End If
KeyCode = 0
End If
End Sub

Sub Text1_KeyPress(KeyAscii As Integer)


If KeyAscii = 8 And Text1.SelStart > 0 Then
' se è stato premuto il BackSpace, elimina un carattere dalla
' stringa di ricerca
DoSearch = False
Text1.text = Left$(Text1.text, Text1.SelStart)
Text1.SelStart = Len(Text1.text)
Else
DoSearch = True
End If
End Sub

Ottenere l'handle dell'area di modifica di una ComboBox


Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal _
hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, _
ByVal lpsz2 As String) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long) As Long

Const GWL_STYLE = (-16)


Const ES_UPPERCASE = &H8&

Dim editHWnd As Long, style As Long

' ottiene l'handle del controllo Edit interno


editHWnd = FindWindowEx(Combo1.hwnd, 0&, vbNullString, vbNullString)
' ottiene la relativa proprietà Style corrente
style = GetWindowLong(editHWnd, GWL_STYLE)
' imposta il bit ES_UPPERCASE
SetWindowLong editHWnd, GWL_STYLE, style Or ES_UPPERCASE

Rendere a sola lettura un controllo Checkbox


Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Const GWL_STYLE = -16
Const BS_AUTOCHECKBOX = 1

' rende una casella di controllo di sola lettura (o ne ripristina il


' comportamento standard).
' Non funziona correttamente se la proprietà Style è impostata a 1-Graphical

Sub MakeCheckBoxReadOnly(CheckBox As CheckBox, ByVal bReadOnly As Boolean)


Dim lStyle As Long
lStyle = GetWindowLong(CheckBox.hWnd, GWL_STYLE)
If bReadOnly Then
lStyle = lStyle Or BS_AUTOCHECKBOX
Else
lStyle = lStyle And (Not BS_AUTOCHECKBOX)
End If
SetWindowLong CheckBox.hWnd, GWL_STYLE, lStyle
End Sub

Mostrare una form come se fosse disabilitata


Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Const GWL_STYLE = (-16)
Private Const WS_CHILD = &H40000000

Private Sub Command1_Click()


' imposta il bit WS_CHILD
SetWindowLong Me.hwnd, GWL_STYLE, GetWindowLong(Me.hwnd, _
GWL_STYLE) Or WS_CHILD
End Sub

Aprire l'elenco a discesa di un controllo ComboBox


Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const CB_SHOWDROPDOWN = &H14F
Const CB_GETDROPPEDSTATE = &H157

' Mostra o nasconde l'elenco a discesa di una casella combinata

Sub ComboBoxOpenList(cbo As ComboBox, Optional showIt As Boolean = True)


SendMessage cbo.hwnd, CB_SHOWDROPDOWN, showIt, ByVal 0&
End Function

' restituisce True se l'elenco a discesa di una casella combinata è visibile

Function ComboBoxIsListVisible(cbo As ComboBox) As Boolean


ComboBoxIsListVisible = SendMessage(cbo.hwnd, CB_GETDROPPEDSTATE, 0, _
ByVal 0&)
End Function

Attivare la modalità di sovrascrittura per i controlli TextBox


' variabile a livello di form
Dim overwriteMode As Boolean

Sub Text1_KeyPress (KeyAscii As Integer)


If overwriteMode And KeyAscii >= 32 And Text1.SelLength = 0 Then
' la modalità è quella di sovrascrittura, l'utente non ha premuto nessun
' tasto e non c'è testo evidenziato
If Mid$(Text1.Text, Text1.SelStart + 1, 1) <> vbCr Then
' non si è arrivati alla fine della riga di testo corrente
' seleziona il carattere successivo in modo che venga sostituito
' da quello digitato dall'utente finale
Text1.SelLength = 1
End If
End If
End Sub

Sub Text1_KeyDown (KeyCode As Integer, Shift As Integer)


If KeyCode = 45 And Shift = 0 Then
overwriteMode = Not overwriteMode
End If
End Sub

Rimuovere il comando Close dal menu di sistema


Private Declare Function RemoveMenu Lib "user32" (ByVal hMenu As Long, _
ByVal nPosition As Long, ByVal wFlags As Long) As Long
Private Declare Function GetMenuItemCount Lib "user32" (ByVal hMenu As Long) As _
Long
Private Declare Function GetSystemMenu Lib "user32" (ByVal hWnd As Long, _
ByVal bRevert As Long) As Long
Private Const MF_BYPOSITION = &H400&
Private Const MF_REMOVE = &H1000&

Private Sub Form_Load()


Dim hMenu As Long
Dim itemCount As Long
' ottiene l'handle del menu di sistema
hMenu = GetSystemMenu(Me.hWnd, 0)
' ottiene il numero di elementi del menu
itemCount = GetMenuItemCount(hMenu)
' elimina il commando Close dal menu di sistema
RemoveMenu hMenu, itemCount - 1, MF_REMOVE Or MF_BYPOSITION
' elimina la riga di separazione nel menu di sistema
RemoveMenu hMenu, itemCount - 2, MF_REMOVE Or MF_BYPOSITION
End Sub

Rimuovere i pulsanti Max e Min nelle form MDI


Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long) As Long
Const WS_MINIMIZEBOX = &H20000
Const WS_MAXIMIZEBOX = &H10000
Const GWL_STYLE = (-16)

Private Sub MDIForm_Load()


SetWindowLong Me.hwnd, GWL_STYLE, GetWindowLong(Me.hwnd, _
GWL_STYLE) And Not (WS_MAXIMIZEBOX Or WS_MINIMIZEBOX)
End Sub

Salvare e ricaricare le impostazioni di una form dal Registry


' salva le impostazioni della form

Function SaveFormSettings(ByVal AppName As String, frm As Object)


SaveSetting AppName, frm.Name, "Left", frm.Left
SaveSetting AppName, frm.Name, "Top", frm.Top
SaveSetting AppName, frm.Name, "Width", frm.Width
SaveSetting AppName, frm.Name, "Height", frm.Height
SaveSetting AppName, frm.Name, "WindowState", frm.WindowState
End Function

' ripristina le impostazioni della form

Function LoadFormSettings(ByVal AppName As String, frm As Object)


Dim currWindowState As Integer

' nel caso il Registry non contenga alcun valore


On Error Resume Next

' se la form è attualmente minimizzata o massimizzata, riporta lo stato a quello originale,


' altrimenti il comando Move fallisce
currWindowState = frm.WindowState
If currWindowState <> 0 Then frm.WindowState = 0

' utilizza un metodo Move per impedire più eventi Resize e Paint
frm.Move GetSetting(AppName, frm.Name, "Left", frm.Left), _
GetSetting(AppName, frm.Name, "Top", frm.Top), GetSetting(AppName, _
frm.Name, "Width", frm.Width), GetSetting(AppName, frm.Name, "Height", _
frm.Height)
frm.WindowState = GetSetting(AppName, frm.Name, "WindowState", _
currWindowState)
End Function

' elimina le impostazioni della form

Sub DeleteFormSettings(ByVal AppName As String, frm As Object)


DeleteSetting AppName, frm.name
End Sub

' ------------- esempio d'uso --------

Private Sub Form_Load()


LoadFormSettings "MyApp", Me
End Sub

Private Sub Form_Unload(Cancel As Integer)


SaveFormSettings "MyApp", Me
End Sub
Mostrare un menu di popup personalizzato per un controllo
TextBox senza ricorrere al subclassing
Private Sub Text1_MouseDown(Button As Integer, Shift As Integer, X As Single, _
Y As Single)
If Button = vbRightButton Then
' disabilita il controllo TextBox
Text1.Enabled = False
DoEvents
' riabilita il controllo, in modo che non appaia in grigio
Text1.Enabled = True
' mostra il menu personalizzato
PopupMenu mnuMyPopupMenu
End If
End Sub

Attirare l'attenzione dell'utente con un titolo lampeggiante


Private Declare Function FlashWindow Lib "user32" (ByVal hWnd As Long, _
ByVal bInvert As Long) As Long

Private Sub cmdStartFlashing_Click()


' avvia il lampeggio abilitando il timer
Timer1.Interval = 1000
End Sub

Private Sub cmdStopFlashing_Click()


' disabilita il timer per interrompere il lampeggio e ripristina il titolo originale
Timer1.Interval = 0
FlashWindow Me.hWnd, 0
End Sub

Private Sub Timer1_Timer()


' modifica lo stato del titolo
FlashWindow Me.hWnd, 1
End Sub

Leggere e modificare l'area di formattazione di una TextBox


Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type

Const EM_GETRECT = &HB2


Const EM_SETRECT = &HB3

' ottiene l'area di formattazione

Sub TextBoxGetRect(tb As TextBox, Left As Long, Top As Long, Right As Long, _


Bottom As Long)
Dim lpRect As RECT
SendMessage tb.hwnd, EM_GETRECT, 0, lpRect
Left = lpRect.Left
Top = lpRect.Top
Right = lpRect.Right
Bottom = lpRect.Bottom
End Sub

' imposta l'are di formattazione ed esegue il refresh del controllo

Sub TextBoxSetRect(tb As TextBox, ByVal Left As Long, ByVal Top As Long, _


ByVal Right As Long, ByVal Bottom As Long)
Dim lpRect As RECT
lpRect.Left = Left
lpRect.Top = Top
lpRect.Right = Right
lpRect.Bottom = Bottom
SendMessage tb.hwnd, EM_SETRECT, 0, lpRect
End Sub

Limitare il tipo di caratteri inseriti in un controllo TextBox


Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Sub ForceTextBoxNumeric(TextBox As TextBox, Optional Force As Boolean = True)
Dim style As Long
Const GWL_STYLE = (-16)
Const ES_NUMBER = &H2000
' leggi lo stile corrente
style = GetWindowLong(TextBox.hWnd, GWL_STYLE)
If Force Then
style = style Or ES_NUMBER
Else
style = style And Not ES_NUMBER
End If
' applica il nuovo stile
SetWindowLong TextBox.hWnd, GWL_STYLE, style
End Sub

' modifica lo stile di un TextBox in modo che converta automaticamente


' i caratteri inseriti in minuscolo o maiuscolo
' ConvertCase = 0 => comportamento normale
' ConvertCase = 1 => converti in maiuscolo
' ConvertCase = 2 => converti in minuscolo

Sub ForceTextBoxCase(TextBox As TextBox, Optional ConvertCase As Integer)


Dim style As Long
Const GWL_STYLE = (-16)
Const ES_UPPERCASE = &H8&
Const ES_LOWERCASE = &H10&
' leggi lo stile corrente
style = GetWindowLong(TextBox.hWnd, GWL_STYLE)
Select Case ConvertCase
Case 0
style = style And Not (ES_UPPERCASE Or ES_LOWERCASE)
Case 1
style = style Or ES_UPPERCASE
Case 2
style = style Or ES_LOWERCASE
End Select
' applica il nuovo stile
SetWindowLong TextBox.hWnd, GWL_STYLE, style
End Sub

Annullare le modifiche in un controllo TextBox


Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const EM_CANUNDO = &HC6
Const EM_UNDO = &HC7
Const EM_EMPTYUNDOBUFFER = &HCD

Private Sub mnuEdit_Click()


If TypeOf ActiveControl Is TextBox Then
' verifica se le modifiche alla casella di testo
' corrente possono essere annullate
mnuEditUndo.Enabled = SendMessage(ActiveControl.hWnd, EM_CANUNDO, 0, _
ByVal 0&)
Else
' in tutti gli altri casi, disabilita questa voce di menu
mnuEditUndo.Enabled = False
End If
End Sub

Private Sub mnuEditUndo_Click()


' annulla le modifiche più recenti apportate al controllo attivo
' (non è necessario utilizzare TypeOf...Is per verificare il tipo di controllo)
SendMessage ActiveControl.hWnd, EM_UNDO, 0, ByVal 0&
End Sub

Elaborazione intelligente del tasto Tab nelle TextBox multiriga


Sub DisableTabStops(Optional restoreIt As Variant)
Static saveTabStop(255) As Boolean
Dim index As Integer
Dim currForm As Form

' non tutti i controlli espongono la proprietà TabStop


On Error Resume Next
Set currForm = Screen.ActiveForm
If IsMissing(restoreIt) Then restoreIt = False

If restoreIt = False Then


' salva il valore corrente della proprietà TabStop
' prima di impostarla a False
For index = 0 To currForm.Controls.Count - 1
saveTabStop(index) = currForm.Controls(index).TabStop
currForm.Controls(index).TabStop = False
Next
Else
' ripristina le impostazioni precedenti
For index = 0 To currForm.Count - 1
currForm.Controls(index).TabStop = saveTabStop(index)
saveTabStop(index) = False
Next
End If

End Sub

Private Sub Text1_GotFocus()


' disabilita la proprietà TabStop per tutti i controlli
DisableTabStops
End Sub

Private Sub Text1_LostFocus()


' ripristina le impostazioni precedenti
DisableTabStops True
End Sub

Impostare le posizioni dei tabulatori per una TextBox


multiriga
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const EM_SETTABSTOPS = &HCB

' imposta i tabulatori in corrispondenza dei caratteri 5, 10 e 20


Dim tabs(2) As Long
tabs(0) = 5 * 4
tabs(1) = 10 * 4
tabs(2) = 20 * 4
SendMessage Text1.hWnd, EM_SETTABSTOPS, 3, tabs(0)

' Imposta i tabulatori. Ogni elemento della matrice viene espresso in unità dialog,
' dove ogni unità dialog è pari ad 1/4 della larghezza media del carattere.

Sub TextBoxSetTabStops(tb As TextBox, tabStops() As Long)


Dim numEls As Long
numEls = UBound(tabStops) - LBound(tabStops) + 1
SendMessage tb.hwnd, EM_SETTABSTOPS, numEls, tabStops(LBound(tabStops))
End Sub

' imposta la distanza del tabulatore, espresso in unità dialog

Sub TextBoxSetTabStopDistance(tb As TextBox, ByVal distance As Long)


SendMessage tb.hwnd, EM_SETTABSTOPS, 1, distance
End Sub

Far apparire una finestra in primo piano


Private Declare Function SetWindowPos Lib "user32" Alias "SetWindowPos" (ByVal _
hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, _
ByVal y As Long, ByVal cx As Long, ByVal cy As Long, _
ByVal wFlags As Long) As Long

Sub SetTopmostWindow(ByVal hWnd As Long, Optional topmost As Boolean = True)


Const HWND_NOTOPMOST = -2
Const HWND_TOPMOST = -1
Const SWP_NOMOVE = &H2
Const SWP_NOSIZE = &H1
SetWindowPos hWnd, IIf(topmost, HWND_TOPMOST, HWND_NOTOPMOST), 0, 0, 0, 0, _
SWP_NOMOVE + SWP_NOSIZE
End Sub

Creare colonne di dati in un controllo ListBox


Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const LB_SETTABSTOPS = &H192

' imposta le posizioni dei tabulatori per un controllo ListBox

' ogni elemento della matrice è espresso in unità dialog,


' dove ogni unità dialog è pari ad 1/4 della larghezza media del carattere.

Sub ListBoxSetTabStops(lb As ListBox, tabStops() As Long)


Dim numEls As Long
numEls = UBound(tabStops) - LBound(tabStops) + 1
SendMessage lb.hwnd, LB_SETTABSTOPS, numEls, tabStops(LBound(tabStops))
End Sub

Trascinare form prive di titolo


Private Declare Function SendMessage Lib "User32" Alias "SendMessageA" (ByVal _
hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Private Declare Sub ReleaseCapture Lib "User32" ()

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, _


Y As Single)
Const WM_NCLBUTTONDOWN = &HA1
Const HTCAPTION = 2
If Button = 1 Then
ReleaseCapture
SendMessage Me.hWnd, WM_NCLBUTTONDOWN, HTCAPTION, 0&
End If
End Sub

Impostare le dimensioni e la posizione di una form


massimizzata
' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip
'
Const WM_GETMINMAXINFO = &H24
Private Type POINTAPI
X As Long
Y As Long
End Type
Private Type MINMAXINFO
ptReserved As POINTAPI
ptMaxSize As POINTAPI
ptMaxPosition As POINTAPI
ptMinTrackSize As POINTAPI
ptMaxTrackSize As POINTAPI
End Type
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As Any, _
source As Any, ByVal numBytes As Long)

Dim WithEvents FormHook As MsgHook

Private Sub Form_Load()


' attiva il subclassing
Set FormHook = New MsgHook
FormHook.StartSubclass hWnd
End Sub

Private Sub FormHook_AfterMessage(ByVal uMsg As Long, ByVal wParam As Long, _


ByVal lParam As Long, retValue As Long)
If uMsg = WM_GETMINMAXINFO Then
' Windows chiede alla form le relative
' dimensioni minime e massime e la posizione.
Dim mmInfo As MINMAXINFO
' Legge il contenuto della struttura a cui punta lParam.
CopyMemory mmInfo, ByVal lParam, Len(mmInfo)
With mmInfo
' ptMaxSize sono le dimensioni della form massimizzata
.ptMaxSize.X = 600
.ptMaxSize.Y = 400
' ptMaxPosition è la posizione della form massimizzata
.ptMaxPosition.X = ((Screen.Width / Screen.TwipsPerPixelX) - 600) \ _
2
.ptMaxPosition.Y = ((Screen.Height / Screen.TwipsPerPixelY) - 400) \ _
2
' ptMinTrackSize sono le dimensioni minime di una form quando viene
' ridimensionata con il mouse.
.ptMinTrackSize.X = 300
.ptMinTrackSize.Y = 200
' ptMinTrackSize sono le dimensioni massime di una form quando viene
' ridimensionata con il mouse
' (generalmente è uguale a ptMaxSize)
.ptMaxTrackSize.X = 600
.ptMaxTrackSize.Y = 400
End With
' copia nella struttura originale in memoria
CopyMemory ByVal lParam, mmInfo, Len(mmInfo)
' deve restituire zero per informare che la struttura è stata modificata
retValue = 0
End If
End Sub
Ottimizzare le procedure Paint con il subclassing
' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip

' la seguente definizione di costante può essere omessa,


' in quanto definita nella type library MsgHook
Private Const WM_PAINT = &HF

Private Type RECT


Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Declare Function GetUpdateRect Lib "user32" (ByVal hWnd As Long, lpRect As RECT, _
ByVal bErase As Long) As Long

Dim WithEvents FormHook As MsgHook

Private Sub Form_Load()


' inizia il subclassing della form
Set FormHook = New MsgHook
FormHook.StartSubclass hWnd
End Sub

Private Sub FormHook_BeforeMessage(uMsg As Long, wParam As Long, lParam As Long, _


retValue As Long, Cancel As Boolean)
If uMsg = WM_PAINT Then
' Windows chiede alla finestra di ridisegnarsi
Dim lpRect As RECT
GetUpdateRect Me.hWnd, lpRect, False
' ora lpRect contiene le dimensioni e la posizione (in pixel)
' del rettangolo da aggiornare
' ...
' ... (scrivere qui la procedura per ridisegnare la form) ...
' ...
End Select
End Sub

Impedire il ripristino di una finestra iconizzata


' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip
'
' la seguente definizione di costante può essere omessa,
' in quanto contenuta nella type library MsgHook
Const WM_QUERYOPEN = &H13

Dim WithEvents FormHook As MsgHook

Private Sub Form_Load()


' inizia il subclassing della form
Set FormHook = New MsgHook
FormHook.StartSubclass hWnd
End Sub

Private Sub FormHook_AfterMessage(ByVal uMsg As Long, ByVal wParam As Long, _


ByVal lParam As Long, retValue As Long)
If uMsg = WM_QUERYOPEN Then
' la form iconizzata viene ripristinata
' ...
' decommentare la riga successiva per impedire che la form venga ripristinata
' retValue = False
End If
End Sub

Fornire una breve descrizione della voce di menu che viene


evidenziata
' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip

Const WM_MENUSELECT = &H11F


Private Declare Function GetMenuString Lib "user32" Alias "GetMenuStringA" _
(ByVal hMenu As Long, ByVal wIDItem As Long, ByVal lpString As String, _
ByVal nMaxCount As Long, ByVal wFlag As Long) As Long

Dim WithEvents FormHook As MsgHook

Private Sub Form_Load()


' inizia il subclassing della form
Set FormHook = New MsgHook
FormHook.StartSubclass hWnd
End Sub

Private Sub FormHook_AfterMessage(ByVal uMsg As Long, ByVal wParam As Long, _


ByVal lParam As Long, retValue As Long)
If uMsg = WM_MENUSELECT Then
' è stato evidenziato (ma non ancora selezionato) un menu.
' L'identificatore della voce di menu è contenuto nella low word di wParam.
' L'handle del menu è contenuto in lParam
Dim menuId As Long, menuCaption As String
Dim length As Long, menuDescr As String
menuId = (wParam And &HFFFF&)
' ottiene il titolo del menu
menuCaption = Space$(256)
length = GetMenuString(lParam, menuId, menuCaption, Len(menuCaption), 0)
menuCaption = Left$(menuCaption, length)

' confronta la stringa con i titoli di tutte le voci di menu


Select Case menuCaption
Case "&New"
menuDescr = "Create a new file"
Case "&Open"
menuDescr = "Open an existing file"
Case "&Save"
menuDescr = "Save a file to disk"
Case "E&xit"
menuDescr = "Exit the program"
End Select
' visualizza la descrizione del menu in un controllo Label
lblStatus = menuDescr
End If
End Sub

Determinare quando l'utente scorre il contenuto di un


controllo TextBox
' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip
'
' le seguenti dichiarazioni di costanti possono essere omesse,
' in quanto sono contenute nella type library MsgHook
Const WM_COMMAND = &H111
Const EN_HSCROLL = &H601
Const EN_VSCROLL = &H602

Dim WithEvents FormHook As MsgHook

Private Sub Form_Load()


' inizia il subclassing della form
Set FormHook = New MsgHook
FormHook.StartSubclass hWnd
End Sub

Private Sub FormHook_AfterMessage(ByVal uMsg As Long, ByVal wParam As Long, _


ByVal lParam As Long, retValue As Long)
If uMsg = WM_COMMAND Then
' l'utente seleziona una voce di comando da un menu,
' o un controllo invia un messaggio di notifica alla propria form padre,
' o viene utilizzata una sequenza di hot-key
'
' se questo è una notifica da parte di un controllo, lParam contiene il relativo handle.
If lParam = Text1.hWnd Then
' In questo caso, il messaggio di notifica è nella
' la high word di wParam.
' NOTA: questo messaggio non arriva se viene utilizzato l'indicatore della barra
' di scorrimento
Select Case (wParam \ &H10000)
Case EN_HSCROLL
' il controllo TextBox è stato scorso orizzontalmente
Case EN_VSCROLL
' il controllo TextBox è stato scorso verticalmente
End Select
End If
End If
End Sub

Utilizzare il subclassing per eliminare il menu di popup


predefinito Edit dalle TextBox
' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip

' è possibile omettere la seguente definizione di costante,


' in quanto è contenuta nella type library MsgHook
Const WM_CONTEXTMENU = &H7B
Dim WithEvents TextBoxHook As MsgHook

Private Sub Form_Load()


' effettua il subclassing del controllo Text1
Set TextBoxHook = New MsgHook
TextBoxHook.StartSubclass Text1.hWnd
End Sub

Private Sub TextBoxHook_BeforeMessage(uMsg As Long, wParam As Long, _


lParam As Long, retValue As Long, Cancel As Boolean)
If uMsg = WM_CONTEXTMENU Then
' mostra un menu di popup personalizzato.
PopupMenu mnuPopup
' cancella l'elaborazione predefinita
' (ossia non visualizza il menu di contesto predefinito).
Cancel = True
End If
End Sub

Creare controlli TextBox con sfondo retinato


' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip

Private Declare Function SetBkMode Lib "gdi32" (ByVal hdc As Long, _


ByVal nBkMode As Long) As Long

Const WM_CTLCOLOREDIT = &H133


Const TRANSPARENT = 1

Dim WithEvents FormHook As MsgHook


Dim TextHWnd As Long

Private Sub Form_Load()


Set FormHook = New MsgHook
FormHook.StartSubclass Me
TextHWnd = Text1.hWnd
End Sub

Private Sub FormHook_BeforeMessage(uMsg As Long, wParam As Long, lParam As Long, _


retValue As Long, Cancel As Boolean)
If uMsg = WM_CTLCOLOREDIT And lParam = TextHWnd Then
SetBkMode wParam, TRANSPARENT
End If
End Sub

Determinare quando l'area di una ComboBox viene chiusa


' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip

Const WM_COMMAND = &H111


Const CBN_DROPDOWN = 7
Const CBN_CLOSEUP = 8

Dim WithEvents FormHook As MsgHook

Private Sub Form_Load()


' esegue il subclassing degli eventi della form
Set FormHook = New MsgHook
' bisognerebbe passare l'hWnd del contenitore della ComboBox
FormHook.StartSubclass Me.hWnd
End Sub

Private Sub FormHook_AfterMessage(ByVal uMsg As Long, ByVal wParam As Long, _


ByVal lParam As Long, retValue As Long)
Dim Ctrl As Control
Dim ctrlIndex As Long
Dim cmdCode As Long

If uMsg = WM_COMMAND Then


' in entrata, lParam contiene l'hWnd del controllo
' la high word di wParam contiene il codice del comando attuale
' la low word di wParam contiene l'ID del controllo
' (ossia l'indice del controllo nella collezione Controls + 1)
ctrlIndex = (wParam And &HFFFF&) - 1
Set Ctrl = Me.Controls(ctrlIndex)
cmdCode = (wParam And &HFFFF0000) \ &H10000

If Not TypeOf Ctrl Is ComboBox Then


' questo non dovrebba mai accadere
ElseIf cmdCode = CBN_DROPDOWN Then
' l'area dell'elenco viene aperta
Debug.Print Ctrl.Name & " is being opened"
ElseIf cmdCode = CBN_CLOSEUP Then
' l'area dell'elenco viene chiusa
Debug.Print Ctrl.Name & " is being closed"
End If
End If
End Sub

Caselle di testo per l'inserimento della password realmente


sicure
' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip
'
Dim WithEvents TextHook As MsgHook

Private Sub Form_Load()


Set TextHook = New MsgHook
' Text1 è il controllo nel quale viene immessa la password
TextHook.StartSubclass Text1
End Sub

Private Sub TextHook_BeforeMessage(uMsg As Long, wParam As Long, lParam As Long, _


retValue As Long, Cancel As Boolean)
' filtra il messaggio WM_GETTEXT
If uMsg = WM_GETTEXT Then Cancel = True
End Sub

Creare form non rettangolari


Type POINTAPI
X As Long
Y As Long
End Type
Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type

Declare Function CreateEllipticRgn Lib "gdi32" (ByVal X1 As Long, _


ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long
Declare Function CreatePolygonRgn Lib "gdi32" (lpPoint As POINTAPI, _
ByVal nCount As Long, ByVal nPolyFillMode As Long) As Long
Declare Function CreateRoundRectRgn Lib "gdi32" (ByVal X1 As Long, _
ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long, ByVal X3 As Long, _
ByVal Y3 As Long) As Long
Declare Function GetWindowRect Lib "user32" (ByVal hWnd As Long, _
lpRect As RECT) As Long
Declare Function SetWindowRgn Lib "user32" (ByVal hWnd As Long, _
ByVal hRgn As Long, ByVal bRedraw As Long) As Long
Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long

Sub SetWindowShape(ByVal hWnd As Long, ByVal Shape As Long)


Dim lpRect As RECT
Dim wi As Long, he As Long
Dim hRgn As Long

' leggi le dimensioni del form in pixel


GetWindowRect hWnd, lpRect
wi = lpRect.Right - lpRect.Left
he = lpRect.Bottom - lpRect.Top

' crea una regione


Select Case Shape
Case 0 ' cerchio o ellisse
hRgn = CreateEllipticRgn(0, 0, wi, he)
Case 1 ' rettangolo arrotondato
hRgn = CreateRoundRectRgn(0, 0, wi, he, 20, 20)
Case 2 ' rombo
Dim lpPoints(3) As POINTAPI
lpPoints(0).X = wi \ 2
lpPoints(0).Y = 0
lpPoints(1).X = 0
lpPoints(1).Y = he \ 2
lpPoints(2).X = wi \ 2
lpPoints(2).Y = he
lpPoints(3).X = wi
lpPoints(3).Y = he \ 2
hRgn = CreatePolygonRgn(lpPoints(0), 4, 1)
End Select
' applica la regione alle form
SetWindowRgn hWnd, hRgn, True
DeleteObject hRgn
End Sub
Private Sub Form_Load()
' mostra il form con i bordi arrotondato
SetWindowShape Me.hWnd, 1
End Sub
Capitolo 6 - Altri controlli
Scaricare e salvare una pagina HTML con l'Internet Transfer
Control
' restituisce il contenuto di una pagina HTML ad un determinato URL
' ed eventualmente lo salva in un file
'
' utilizza un Internet Transfer Control,
' che deve essere passato come primo argomento

Function GetHTMLPage(INet As INet, ByVal URL As String, _


Optional FileName As String) As String
Dim fnum As Integer

' annulla qualsiasi operazione in sospeso


INet.Cancel
' imposta il protocollo ad HTTP
INet.Protocol = icHTTP
' ottiene la pagina
GetHTMLPage = INet.OpenURL(URL)

' salva in un file, se richiesto


If FileName <> "" Then
fnum = FreeFile
Open FileName For Output As #fnum
Print #fnum, GetHTMLPage;
Close #fnum
End If

End Function

Determinare il numero di elementi visibili in un controllo


ListView
Private Const LVM_FIRST = &H1000
Private Const LVM_GETCOUNTPERPAGE = (LVM_FIRST + 40)
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long

' Restituisce il numero di elementi visibili in un controllo ListView

Private Function ListViewGetVisibleCount(LV As ListView) As Long


ListViewGetVisibleCount = SendMessage(LV.hwnd, LVM_GETCOUNTPERPAGE, 0&, _
ByVal 0&)
End Function

Ottenere o impostare l'altezza dei nodi di un controllo


TreeView
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long

Private Const TV_FIRST = &H1100


Private Const TVM_SETITEMHEIGHT = (TV_FIRST + 27)
Private Const TVM_GETITEMHEIGHT = (TV_FIRST + 28)

' restituisce l'altezza dei singoli elementi di un TreeView


NodeHeight = SendMessage(TreeView1.hWnd, TVM_GETITEMHEIGHT, 0, ByVal 0&)

' imposta l'altezza dei singoli elementi di un TreeView


' utilizza il valore -1 per ripristinare l'altezza predefinita
SendMessage TreeView1.hWnd, TVM_SETITEMHEIGHT, newNodeHeight, ByVal 0&

Riempire un controllo TreeView con dati casuali


' MaxChildren è il numero Massimo di nodi figlio ad ogni livello
' MaxLevel è il livello massimo di annidamento che si vuole creare

Sub AddRandomNodes(TV As TreeView, Node As Node, MaxChildren As Integer, _


MaxLevel As Integer)

Dim i As Integer
Dim child As Node

' Aggiunge un numero di nodi figlio minore o uguale a MaxChildren


For i = 1 To CInt(Rnd * MaxChildren)
Set child = TV.Nodes.Add(Node.index, tvwChild, , _
"Node #" & (TV.Nodes.Count + 1))
' per ogni nodi figlio, se MaxLevel è maggiore di 0
' aggiunge casualmente un insieme di nodi figlio
If CInt(Rnd * MaxLevel) Then
AddRandomNodes TV, child, MaxChildren, MaxLevel - 1
End If
Next
End Sub

Eliminare i tooltip del controllo TreeView


Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long) As Long

Const TVS_NOTOOLTIPS = &H80


Const GWL_STYLE = (-16)

' disattiva i tooltip


SetWindowLong TreeView1.hwnd, GWL_STYLE, GetWindowLong(TreeView1.hwnd, _
GWL_STYLE) Or TVS_NOTOOLTIPS

' attiva i tooltip


SetWindowLong TreeView1.hwnd, GWL_STYLE, GetWindowLong(TreeView1.hwnd, _
GWL_STYLE) And (Not TVS_NOTOOLTIPS)

Inserire un'immagine in un controllo RichTextBox


Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Private Const WM_PASTE = &H302

Sub InsertPictureInRichTextBox(RTB As RichTextBox, Picture As StdPicture)


' copia l'immagine nella Clipboard.
Clipboard.Clear
Clipboard.SetData Picture
' incolla nel controllo RichTextBox
SendMessage RTB.hwnd, WM_PASTE, 0, 0
End Sub

Limitare la lunghezza di un elemento di un controllo ListView


Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const LVM_FIRST = &H1000
Const LVM_GETEDITCONTROL = (LVM_FIRST + 24)
Const EM_LIMITTEXT = &HC5

' limita a 10 caratteri il testo che può essere immesso in un elemento di un ListView

Private Sub ListView1_BeforeLabelEdit(Cancel As Integer)


Dim editHWnd As Long
' ottiene l'handle del controllo Edit del ListView
editHWnd = SendMessage(ListView1.hWnd, LVM_GETEDITCONTROL, 0, ByVal 0&)
' gli invia un messaggio EM_LIMITTEXT per limitarne la lunghezza
SendMessage editHWnd, EM_LIMITTEXT, 10, 0
End Sub

' ------- secondo esempio

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _


hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Const GWL_STYLE = (-16)

Const LVM_FIRST = &H1000


Const LVM_GETEDITCONTROL = (LVM_FIRST + 24)
Const ES_UPPERCASE = &H8&

' converte in maiuscolo tutto il testo digitato in un nodo di un ListView

Private Sub ListView1_BeforeLabelEdit(Cancel As Integer)


Dim editHWnd As Long
' ottiene l'handle del controllo Edit del ListView
editHWnd = SendMessage(ListView1.hWnd, LVM_GETEDITCONTROL, 0, ByVal 0&)
' applica lo stile "tutto in maiuscolo"
SetWindowLong editHWnd, GWL_STYLE, GetWindowLong(editHWnd, _
GWL_STYLE) Or ES_UPPERCASE
End Sub

Leggere e impostare il primo nodo visibile in un controllo


TreeView
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Private Const TV_FIRST = &H1100
Private Const TVM_GETNEXTITEM = (TV_FIRST + 10)
Private Const TVM_SELECTITEM = (TV_FIRST + 11)
Private Const TVGN_CARET = 9
Private Const TVGN_FIRSTVISIBLE = &H5

' restituisce il primo Node visibile in un TreeView

Function GetTreeViewFirstVisibleNode(ByVal TV As TreeView) As Node


Dim hItem As Long
Dim selNode As Node
' ricorda quale node è selezionato
Set selNode = TV.SelectedItem
' ottieni l'handle del primo nodo visibile usando le API
hItem = SendMessage(TV.hWnd, TVM_GETNEXTITEM, TVGN_FIRSTVISIBLE, ByVal 0&)
' rendilo il node selezionato
SendMessage TV.hWnd, TVM_SELECTITEM, TVGN_CARET, ByVal hItem
' imposta il risultato leggendo la proprietà SelectedItem
Set GetTreeViewFirstVisibleNode = TV.SelectedItem
' ripristina il nodo precedentemente selezionato
Set TV.SelectedItem = selNode
End Function

' imposta il primo nodo visibile in un TreeView

Sub SetTreeViewFirstVisibleNode(ByVal TV As TreeView, ByVal Node As Node)


Dim hItem As Long
Dim selNode As Node
' ricorda quale node è selezionato
Set selNode = TV.SelectedItem
' rendi il nodo prescelto il nodo selezionato
Set TV.SelectedItem = Node
' ora possiamo leggere il suo handle
hItem = SendMessage(TV.hWnd, TVM_GETNEXTITEM, TVGN_CARET, ByVal 0&)
' ripristina il nodo precedentemente selezionato
Set TV.SelectedItem = selNode
' rendi il nodo passato come argomento il primo visibile nel controllo
SendMessage TV.hWnd, TVM_SELECTITEM, TVGN_FIRSTVISIBLE, ByVal hItem
End Sub

Limitare la lunghezza del testo in un nodo di un TreeView


Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const TV_FIRST = &H1100
Const TVM_GETEDITCONTROL = TV_FIRST + 15
Const EM_LIMITTEXT = &HC5

' Limita a 20 caratteri il testo che può essere immesso in un nodo di TreeView1

Private Sub TreeView1_BeforeLabelEdit(Cancel As Integer)


Dim editHWnd As Long
' ottiene l'handle del controllo Edit del TreeView
editHWnd = SendMessage(TreeView1.hWnd, TVM_GETEDITCONTROL, 0, ByVal 0&)
' gli invia un messaggio EM_LIMITTEXT per limitarne la lunghezza
SendMessage editHWnd, EM_LIMITTEXT, 20, 0
End Sub

' -------- secondo esempio -----------

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _


hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Const GWL_STYLE = (-16)

Const TV_FIRST = &H1100


Const TVM_GETEDITCONTROL = TV_FIRST + 15
Const ES_UPPERCASE = &H8&
' converte in maiuscolo il testo digitato in un nodo di un TreeView

Private Sub TreeView1_BeforeLabelEdit(Cancel As Integer)


Dim editHWnd As Long

' ottiene l'handle del controllo Edit del TreeView


editHWnd = SendMessage(TreeView1.hWnd, TVM_GETEDITCONTROL, 0, ByVal 0&)
' applica lo stile "tutto in maiuscolo"
SetWindowLong editHWnd, GWL_STYLE, GetWindowLong(editHWnd, _
GWL_STYLE) Or ES_UPPERCASE
End Sub

Ottenere il testo puro o in formato HTML del contenuto di un


controllo WebBrowser
Sub SaveWebBrowser(WB As WebBrowser, ByVal FileName As String, _
Optional SaveAsPlainText As Boolean)
Dim Text As String, fnum As Integer

If SaveAsPlainText Then
Text = BW.Document.Body.innerHTML
Else
Text = BW.Document.documentElement.OuterHTML
End If

' salva in un file


fnum = FreeFile
Open FileName For Output As #fnum
Print #fnum, Text;
Close #fnum
End Sub

Ottenere il pieno controllo sul testo inserito in un elemento di


un ListView
' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _


hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Const LVM_FIRST = &H1000
Const LVM_GETEDITCONTROL = (LVM_FIRST + 24)

Dim WithEvents TVHook As MsgHook

' inizia il subclassing del controllo Edit utilizzato in modalità di modifica

Private Sub ListView1_BeforeLabelEdit(Cancel As Integer)


Dim editHWnd As Long
' ottiene l'handle del controllo Edit del TreeView
editHWnd = SendMessage(ListView1.hWnd, LVM_GETEDITCONTROL, 0, ByVal 0&)
' esegue il subclassing
LVHook.StartSubclass editHWnd
End Sub

' interrompe il subclassing quando esce dalla modalità di modifica

Private Sub ListView1_AfterLabelEdit(Cancel As Integer, NewString As String)


LVHook.StopSubclass
End Sub

Private Sub Form_Load()


Set LVHook = New MsgHook
End Sub

' elimina le cifre non appena vengono immesse dall'utente


' e converte tutto il testo in maiuscolo

Private Sub LVHook_BeforeMessage(uMsg As Long, wParam As Long, lParam As Long, _


retValue As Long, Cancel As Boolean)
If uMsg = WM_CHAR Then
' wParam contiene il codice dei caratteri
If wParam >= 48 And wParam <= 57 Then
' elimina le cifre
Cancel = True
ElseIf wParam >= Asc("a") And wParam <= Asc("z") Then
' converte in maiuscolo
wParam = wParam - 32
End If
End If
End Sub
Ottenere il pieno controllo sul testo inserito in un nodo di un
TreeView
' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip

Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _


hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Private Const TV_FIRST = &H1100
Private Const TVM_GETEDITCONTROL = TV_FIRST + 15

Dim WithEvents TVHook As MsgHook

' inizia il subclassing del controllo Edit utilizzato in modalità di modifica

Private Sub TreeView1_BeforeLabelEdit(Cancel As Integer)


Dim editHWnd As Long
' ottiene l'handle del controllo Edit del TreeView
editHWnd = SendMessage(TreeView1.hWnd, TVM_GETEDITCONTROL, 0, ByVal 0&)
' esegue il subclassing
TVHook.StartSubclass editHWnd
End Sub

Private Sub TreeView1_AfterLabelEdit(Cancel As Integer, NewString As String)


' interrompe il subclassing quando esce dalla modalità di modifica
TVHook.StopSubclass
End Sub

Private Sub Form_Load()


' crea un'istanza del controllo che esegue il subclassing
Set TVHook = New MsgHook
End Sub

' elimina gli spazi non appena vengono immessi dall'utente

Private Sub TVHook_BeforeMessage(uMsg As Long, wParam As Long, lParam As Long, _


retValue As Long, Cancel As Boolean)
If uMsg = WM_CHAR Then
' wParam contiene il codice del carattere
If wParam = 32 Then Cancel = True
End If
End Sub

Impedire il trascinamento degli elementi di un controllo


ListView
' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip

Const WM_NOTIFY = &H4E


Const LVN_FIRST = -100&
Const LVN_BEGINDRAG = (LVN_FIRST - 9)

Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As _


Any, source As Any, ByVal bytes As Long)

Private Type NMHDR


hwndFrom As Long
idFrom As Long
code As Long
End Type

Dim WithEvents FormHook As MsgHook

Private Sub Form_Load()


' inizia il subclassing della form corrente
Set FormHook = New MsgHook
FormHook.StartSubclass Me

' popola il controllo ListView1 con i dati


' ... (omesso) ...
End Sub

' questo evento viene scatenato quando alla form si invia un messaggio

Private Sub FormHook_BeforeMessage(uMsg As Long, wParam As Long, lParam As Long, _


retValue As Long, Cancel As Boolean)

' il ListView notifica qualcosa alla propria form padre


If uMsg = WM_NOTIFY Then
' copia la struttura MNHDR a cui punta
' lParam in un UDT locale
Dim nmh As NMHDR
CopyMemory nmh, ByVal lParam, Len(nmh)

' controlla se la notifica arriva dal controllo ListView1


' e se si è all'inizio dell'operazione di trascinamento
If nmh.hwndFrom = ListView1.hWnd And nmh.code = LVN_BEGINDRAG Then
' sì, cancella l'operazione
retValue = 1
Cancel = True
End If
End If
End Sub
Capitolo 7 - Grafica e Stampa
Visualizzare una GIF animata utilizzando il controllo
WebBrowser
Private Declare Function GetSystemMetrics Lib "user32" Alias "GetSystemMetrics" _
(ByVal nIndex As Long) As Long
Private Const SM_CXVSCROLL = 2
Private Const SM_CYHSCROLL = 3

' crea una nuova PictureBox


Dim picBox As PictureBox
Set picBox = Controls.Add("VB.PictureBox", "picBox")

' ridimensiona la PictureBox in modo che nasconda


' le barre di scorrimento del controllo WebBrowser
With WebBrowser1
picBox.Move .Left, .Top, .Width - ScaleX(GetSystemMetrics(SM_CXVSCROLL), _
vbPixels), .Height - ScaleY(GetSystemMetrics(SM_CYHSCROLL), vbPixels)
' sposta il WebBrowser all'interno della PictureBox
Set WebBrowser1.Container = picBox
' controlla che il bordo del controllo WebBrowser non sia visibile
.Move -ScaleX(2, vbPixels), -ScaleY(2, vbPixels)
End With
' tutti i controlli vengono creati invisibili
picBox.Visible = True

Verificare sempre che una stampante sia installata


Function PrinterIsInstalled() As Boolean
Dim dummy As String

On Error Resume Next


dummy = Printer.DeviceName

If Err.Number Then
MsgBox "No default printer installed." & vbCrLf & _
"To install and select a default printer, select the " & _
"Setting / Printers command in the Start menu, and then " & _
"double-click on the Add Printer icon.", vbExclamation, _
"Printer Error"
PrinterIsInstalled = False
Else
PrinterIsInstalled = True
End If
End Function

Estrarre le componenti Red, Green, Blue di un colore


Function GetRed(ByVal lColor As Long) As Long
GetRed = lColor Mod 256
End Function

Function GetGreen(ByVal lColor As Long) As Long


GetGreen = (lColor \ &H100) Mod 256
End Function

Function GetBlue(ByVal lColor As Long) As Long


GetBlue = (lColor \ &H10000) Mod 256
End Function

Impostare il colore di un pixel


Declare Function GetPixel Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, _
ByVal y As Long) As Long
Declare Function SetPixel Lib "gdi32" (ByVal hdc As Long, ByVal x As Long, _
ByVal y As Long, ByVal crColor As Long) As Long

Private Sub Form_Click()


Dim x As Long, y As Long, h As Long
h = Me.hdc

' assume che la ScaleMode della form


' sia impostata a 3 - Pixels
For y = 0 To ScaleHeight - 1
For x = 0 To ScaleWidth - 1
If GetPixel(h, x, y) = vbRed Then
SetPixel h, x, y, vbYellow
End If
Next
Next
End Sub

Modificare il colore dei pixel in un'area


Private Declare Function ExtFloodFill Lib "GDI32" (ByVal hDC As Long, _
ByVal X As Long, ByVal Y As Long, ByVal colorCode As Long, _
ByVal fillType As Long) As Long
Const FLOODFILLBORDER = 0
Const FLOODFILLSURFACE = 1

Sub AreaFill(obj As Object, ByVal X As Long, ByVal Y As Long, _


ByVal colorCode As Long, Optional borderColor As Variant)

Dim x2 As Long, y2 As Long


Dim saveFillStyle As Long
Dim saveFillColor As Long

With obj
' converte le coordinate in pixel
x2 = .ScaleX(X, .ScaleMode, vbPixels)
y2 = .ScaleY(Y, .ScaleMode, vbPixels)
' salva le proprietà FillStyle e FillColor
saveFillStyle = .FillStyle
saveFillColor = .FillColor
' imposta valori opportuni per queste proprietà
.FillStyle = 0
.FillColor = colorCode

If IsMissing(borderColor) Then
' leggi il colore alle coordinate date
borderColor = .Point(X, Y)
' modifica tutti i pixel di quel colore
ExtFloodFill .hDC, x2, y2, borderColor, FLOODFILLSURFACE
Else
' modifica tutti i pixel fino ad incontrare il bordo
ExtFloodFill .hDC, x2, y2, borderColor, FLOODFILLBORDER
End If
' ripristina le proprietà
.FillStyle = saveFillStyle
.FillColor = saveFillColor
End With
End Sub

Convertire il valore di un colore in toni di grigio


Function GetGreyScale(ByVal lColor As Long)
lColor = 0.33 * (lColor Mod 256) + 0.59 * ((lColor \ 256) Mod 256) + 0.11 * _
((lColor \ 65536) Mod 256)
GetGreyScale = RGB(lColor, lColor, lColor)
End Function

Ottenere le proprietà dei Bitmap


Private Type BITMAP
bmType As Long ' questo deve essere zero
bmWidth As Long ' larghezza del bitmap
bmHeight As Long ' altezza del bitmap
bmWidthBytes As Long ' byte nella linea raster orizzontale
bmPlanes As Integer ' numero dei piani di colore
bmBitsPixel As Integer ' numero di bit per pixel
bmBits As Long ' indirizzo dei pixel di dati in memoria
End Type
Private Declare Function GetObjectAPI Lib "gdi32" Alias "GetObjectA" (ByVal _
hObject As Long, ByVal nCount As Long, lpObject As Any) As Long

Sub GetBitmapInfo(ByVal handle As Long, width As Long, height As Long, _


Optional colorPlanes As Long, Optional bitsPerPixel As Long )
Dim bmp As BITMAP
GetObjectAPI handle, Len(bmp), bmp
width = bmp.bmWidth
height = bmp.bmHeight
colorPlanes = bmp.bmPlanes
bitsPerPixel = bmp.bmBitsPixel
End Sub

' ------- esempio

GetBitmapInfo Picture1.Picture, width, height, colorPlanes, bitsPerPixel


Label1 = "Width = " & width & vbCrLf & "Height = " & height & vbCrLf & _
"Color Planes = " & colorPlanes & vbCrLf & "Bits per Pixel = " & _
bitsPerPixel
Copiare il contenuto dello schermo o della finestra attiva
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, _
ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Const KEYEVENTF_KEYUP = &H2

' Restituisce il contenuto corrente dello schermo o della finestra attiva


'
' Funziona simulando la pressione del tasto Print-Screen
' (e del tasto Alt se ActiveWindow è True) che immette il contenuto dello schermo nella
' Clipboard. Il contenuto originale della Clipboard viene quindi ripristinato
' ma questa azione potrebbe influenzare il comportamento di altre applicazioni
' che effettuano il monitoraggio della Clipboard.

Function GetScreenBitmap(Optional ActiveWindow As Boolean) As Picture


' salva l'immagine corrente nella Clipboard, se c'è
Dim pic As StdPicture
Set pic = Clipboard.GetData(vbCFBitmap)

' Alt-Print Screen cattura solamente il contenuto della finestra attiva


If ActiveWindow Then
' Preme il tasto Alt
keybd_event vbKeyMenu, 0, 0, 0
End If
' Preme il tasto Print Screen
keybd_event vbKeySnapshot, 0, 0, 0
DoEvents

' Rilascia il tasto Print Screen


keybd_event vbKeySnapshot, 0, KEYEVENTF_KEYUP, 0
If ActiveWindow Then
' Rilascia il tasto Alt
keybd_event vbKeyMenu, 0, KEYEVENTF_KEYUP, 0
End If
DoEvents

' restituisce il bitmap attualmente nella Clipboard


Set GetScreenBitmap = Clipboard.GetData(vbCFBitmap)
' ripristina il contenuto originale della Clipboard
Clipboard.SetData pic, vbCFBitmap
End Function
Capitolo 8 - Tastiera e Mouse
Determinare il numero dei pulsanti del mouse
Const SM_CMOUSEBUTTONS = 43
Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) _
As Long

Function GetNumMouseButtons() As Long


GetNumMouseButtons = GetSystemMetrics(SM_CMOUSEBUTTONS)
End Function

Private Command1_Click()
MsgBox "Your mouse has " & GetNumMouseButtons & " buttons."
End Sub

Determinare lo stato dei pulsanti del mouse


Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As _
Integer

Function LeftButton() As Boolean


LeftButton = (GetAsyncKeyState(vbKeyLButton) And &H8000)
End Function

Function RightButton() As Boolean


RightButton = (GetAsyncKeyState(vbKeyRButton) And &H8000)
End Function

Function MiddleButton() As Boolean


MiddleButton = (GetAsyncKeyState(vbKeyMButton) And &H8000)
End Function

Function MouseButton() As Integer


If GetAsyncKeyState(vbKeyLButton) < 0 Then
MouseButton = 1
End If
If GetAsyncKeyState(vbKeyRButton) < 0 Then
MouseButton = MouseButton Or 2
End If
If GetAsyncKeyState(vbKeyMButton) < 0 Then
MouseButton = MouseButton Or 4
End If
End Function

Leggere lo stato dei tasti Shift


Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As _
Integer

' Lo stato del tasto Ctrl


Function CtrlKey() As Boolean
CtrlKey = (GetAsyncKeyState(vbKeyControl) And &H8000)
End Function

' Lo stato di entrambi i tasti Shift


Function ShiftKey() As Boolean
ShiftKey = (GetAsyncKeyState(vbKeyShift) And &H8000)
End Function

' Lo stato del tasto Alt


Function AltKey() As Boolean
AltKey = (GetAsyncKeyState(vbKeyMenu) And &H8000)
End Function

Leggere la posizione del mouse


Private Type POINTAPI
X As Long
Y As Long
End Type
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare Function ScreenToClient Lib "user32" (ByVal hWnd As Long, _
lpPoint As POINTAPI) As Long

Function MouseX(Optional ByVal hWnd As Long) As Long


Dim lpPoint As POINTAPI
GetCursorPos lpPoint
If hWnd Then ScreenToClient hWnd, lpPoint
MouseX = lpPoint.X
End Function
Function MouseY(Optional ByVal hWnd As Long) As Long
Dim lpPoint As POINTAPI
GetCursorPos lpPoint
If hWnd Then ScreenToClient hWnd, lpPoint
MouseY = lpPoint.Y
End Function

Spostare il mouse al centro di una form o controllo


Private Declare Function SetCursorPos Lib "user32" (ByVal X As Long, _
ByVal Y As Long) As Long
Private Declare Function GetWindowRect Lib "user32" (ByVal hWnd As Long, _
lpRect As RECT) As Long

Sub SnapMouseToWindow(ByVal hWnd As Long)


Dim lpRect As RECT
' ottieni le coordinate della form/controllo in pixel
GetWindowRect hWnd, lpRect
' sposta il cursore del mouse al suo centro
SetCursorPos lpRect.Left + (lpRect.Right - lpRect.Left) \ 2, _
lpRect.Top + (lpRect.Bottom - lpRect.Top) \ 2
End Sub

Ripristinare correttamente il cursore del mouse


' la classe CMouseCursor
Private oldMousePointer As Variant

' imposta un nuovo cursore


Sub SetCursor(Optional NewCursor As MousePointerConstants = vbHourGlass)
If IsEmpty(oldMousePointer) Then
' salva il cursore originale, ma solo la prima volta
' che il metodo viene invocato su questa istanza
oldMousePointer = Screen.MousePointer
End If
Screen.MousePointer = NewCursor
End Sub

Private Sub Class_Terminate()


' ripristina il cursore originale, se è stato modificato
If Not IsEmpty(oldMousePointer) Then
Screen.MousePointer = oldMousePointer
End If
End Sub

Simulare gli eventi MouseEnter e MouseExit


Private Declare Function SetCapture Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseCapture Lib "user32" () As Long
Private Declare Function GetCapture Lib "user32" () As Long

Private Sub Command1_MouseMove(Button As Integer, Shift As Integer, X As Single, _


Y As Single)

If (X < 0) Or (Y < 0) Or (X > Command1.Width) Or (Y > Command1.Height) Then


' lo pseudo-evento MOUSELEAVE
ReleaseCapture
' in questo esempio riporta il titolo allo stile normale
Command1.Font.Bold = False

ElseIf GetCapture() <> Command1.hwnd Then


' lo pseudo-evento MOUSEENTER
SetCapture Command1.hwnd
' in questo esempio, trasforma il titolo in grassetto
Command1.Font.Bold = True
End If

End Sub

Private Sub Text1_MouseMove(Button As Integer, Shift As Integer, X As Single, _


Y As Single)
If (X Or Y) < 0 Or (X > Text1.Width) Or (Y > Text1.Height) Then
ReleaseCapture
Text1.SelLength = 0
ElseIf GetCapture() <> Text1.hWnd Then
SetCapture Text1.hWnd
Text1.SelStart = 0
Text1.SelLength = Len(Text1)
Text1.SetFocus
End If
End Sub
Leggere lo stato dei tasti di lock
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As Long

' leggi lo stato corrente del tasto CapsLock

Function GetCapsLockKey() As Boolean


GetCapsLockKey = GetKeyState(vbKeyCapital)
End Function

' leggi lo stato corrente del tasto NumLock

Function GetNumLockKey() As Boolean


GetNumLockKey = GetKeyState(vbKeyNumlock)
End Function

' leggi lo stato corrente del tasto ScrollLock

Function GetScrollLockKey() As Boolean


GetScrollLockKey = GetKeyState(vbKeyScrollLock)
End Function

Modificare lo stato dei tasti di lock


Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, _
ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Declare Function GetKeyState Lib "user32" (ByVal nVirtKey As Long) As _
Long

' imposta lo stato del tasto CapsLock

Sub SetCapsLockKey(ByVal newState As Boolean)


Const KEYEVENTF_KEYUP = &H2
' se lo stato corrente deve essere modificato
If CBool(GetKeyState(vbKeyCapital)) <> newState Then
' premi e rilascia il tasto CapsLock via codice
keybd_event vbKeyCapital, 0, 0, 0
keybd_event vbKeyCapital, 0, KEYEVENTF_KEYUP, 0
End If
End Sub

' imposta lo stato del tasto NumLock

Sub SetNumLockKey(ByVal newState As Boolean)


Const KEYEVENTF_KEYUP = &H2
' se lo stato corrente deve essere modificato
If CBool(GetKeyState(vbKeyNumlock)) <> newState Then
' premi e rilascia il tasto NumLock via codice
keybd_event vbKeyNumlock, 0, 0, 0
keybd_event vbKeyNumlock, 0, KEYEVENTF_KEYUP, 0
End If
End Sub

' imposta lo stato del tasto ScrollLock

Sub SetScrollLockKey(ByVal newState As Boolean)


Const KEYEVENTF_KEYUP = &H2
' se lo stato corrente deve essere modificato
If CBool(GetKeyState(vbKeyScrollLock)) <> newState Then
' premi e rilascia il tasto ScrollLock via codice
keybd_event vbKeyScrollLock, 0, 0, 0
keybd_event vbKeyScrollLock, 0, KEYEVENTF_KEYUP, 0
End If
End Sub
Capitolo 9 - File e directory
Caricare un file di testo in un'unica operazione
Function FileText (filename$) As String
Dim handle As Integer
handle = FreeFile
Open filename$ For Input As #handle
FileText = Input$(LOF(handle), handle)
Close #handle
End Function

Function FileText(ByVal filename As String) As String


Dim handle As Integer

' controlla che il file esista


If Len(Dir$(filename)) = 0 Then
Err.Raise 53 ' file non trovato
End If

' lo apre in modalità binaria


handle = FreeFile
Open filename$ For Binary As #handle
' legge la stringa e chiude il file
FileText = Space$(LOF(handle))
Get #handle, , FileText
Close #handle
End Function

Controllare che un file o una directory esista


Function FileExists(FileName As String) As Boolean
On Error Resume Next
' leggi l'attributo assicurati che non sia una directory
FileExists = (GetAttr(FileName) And vbDirectory) = 0
' se avviene un errore la routine restituisce False
End Function

Function DirExists(DirName As String) As Boolean


On Error Resume Next
' leggi l'attributo assicurati che non sia un file
DirExists = GetAttr(DirName) And vbDirectory
' se avviene un errore la routine restituisce False
End Function

Ottenere informazioni su tutti i drive disponibili


' NOTA: questo codice richiede un riferimento alla type library
' Microsoft Scripting Runtime

Dim fso As New Scripting.FileSystemObject


Dim drv As Scripting.Drive
Dim info As String

For Each drv In fso.Drives


' visualizza il nome ed il tipo di drive
info = "Drive " & drv.DriveLetter & vbCrLf
info = info & " Type: "
' è necessario decodificare questo valore
Select Case drv.DriveType
Case Removable: info = info & "Removable" & vbCrLf
Case Fixed: info = info & "Fixed" & vbCrLf
Case CDRom: info = info & "CDRom" & vbCrLf
Case Remote: info = info & "Remote" & vbCrLf
Case RamDisk: info = info & "RamDisk" & vbCrLf
Case Else: info = info & "Unknown" & vbCrLf
End Select

If Not drv.IsReady Then


' se il drive non è pronto non è possibile fare molto di più
info = info & " Not Ready" & vbCrLf
Else
' estrae tutte le informazioni aggiuntive
info = info & " File System: " & drv.FileSystem & vbCrLf
info = info & " Label: " & drv.VolumeName & vbCrLf
info = info & " Serial number: " & drv.SerialNumber & vbCrLf
info = info & " Total space: " & drv.TotalSize & vbCrLf
info = info & " Free space: " & drv.FreeSpace & vbCrLf
End If
' esegue qualcosa con le informazioni ottenute
' (in questo caso le visualizza in una casella di testo)
Text1.Text = Text1.Text & info
Next

Contare i caratteri in un file


Sub CountFileCharacters(filename As String, charCount() As Long)
Dim filenum As Integer
Dim index As Long, acode As Integer

On Error Resume Next


filenum = FreeFile()
Open filename For Binary As #filenum
' legge il contenuto del file in una matrice di Byte temporanea
ReDim bArr(0 To LOF(filenum)) As Byte
Get #filenum, , bArr()
Close #filenum

' analizza la matrice, aggiorna charCount()


For index = 0 To UBound(bArr)
acode = bArr(index)
charCount(acode) = charCount(acode) + 1
Next
End Sub

Cercare un file in un albero di directory utilizzando la DLL


Imagehlp
Private Declare Function SearchTreeForFile Lib "imagehlp.dll" (ByVal sRootPath _
As String, ByVal InputPathName As String, ByVal OutputPathBuffer As String) _
As Boolean

' cerca un file in un albero di sottodirectory


'
' restituisce il percorso+nome completo di filename
' oppure una stringa nulla se filename non è stato trovato
' viene restituita solo la prima occorrenza del file
'
' ROOTDIR può essere la directory radice di un drive (ad esempio, "C:\") o una sottodirectory
' ("C:\DOCS")

Function SearchFileInDirTree(ByVal rootDir As String, ByVal Filename As String) _


As String
' questa è la lunghezza massima per filename
Dim buffer As String * 260
On Error Goto Unsupported

If SearchTreeForFile(rootDir, Filename, buffer) Then


' un valore di ritorno diverso da zero significa successo
SearchFileInDirTree = Left$(buffer, InStr(buffer, vbNullChar) - 1)
End If
Exit Sub

Unsupported:
' non tutte le versioni di Windows supportano questa funzione
MsgBox "SearchTreeForFile non supportata su questo sistema"
End Function

Convertire nomi lunghi di file nel formato 8.3 e viceversa


Private Declare Function GetShortPathName Lib "kernel32" Alias _
"GetShortPathNameA" (ByVal lpszLongPath As String, _
ByVal lpszShortPath As String, ByVal cchBuffer As Long) As Long
Const MAX_PATH = 260

Public Function ShortPathName(ByVal FileName As String) As String


Dim length As Long, res As String
res = String$(MAX_PATH, 0)
length = GetShortPathName(FileName, res, Len(res))
If length Then
ShortPathName = Left$(res, length)
End If
End Function

Private Declare Function GetLongPathName Lib "kernel32" Alias _


"GetLongPathNameA" (ByVal lpszShortPath As String, _
ByVal lpszLongPath As String, ByVal cchBuffer As Long) As Long
Const MAX_PATH = 260

Public Function LongPathName(ByVal FileName As String) As String


Dim length As Long, res As String
On Error Resume Next
res = String$(MAX_PATH, 0)
length = GetLongPathName(FileName, res, Len(res))
If length And Err = 0 Then
LongPathName = Left$(res, length)
End If
End Function

Determinare se una directory è condivisa


Type SHFILEINFO
hIcon As Long
iIcon As Long
dwAttributes As Long
szDisplayName As String * MAX_PATH
szTypeName As String * 80
End Type

Public Const SHGFI_ATTRIBUTES = &H800


Public Const SFGAO_SHARE = &H20000

Public Declare Function SHGetFileInfo Lib "shell32.dll" Alias "SHGetFileInfoA" _


( ByVal pszPath As String, ByVal dwFileAttributes As Long, _
psfi As SHFILEINFO, ByVal cbFileInfo As Long, ByVal uFlags As Long) As Long

Function IsFolderShared(ByVal folderName As String) As Boolean


Dim sfi As SHFILEINFO
SHGetFileInfo foldernName, 0, sfi, Len(sfi), SHGFI_ATTRIBUTES
IsFolderShared = (sfi.dwAttributes And SFGAO_SHARE)
End Function

Formattare un drive utilizzando una funzione non


documentata
Private Declare Function SHFormatDrive Lib "Shell32.dll" (ByVal hWnd As Long, _
ByVal Drive As Integer, ByVal fmtID As Integer, ByVal Options As Integer) _
As Long

Private Sub btnFormatDrive_Click()


Dim ret As Long
' formattazione completa di A:
ret = SHFormatDrive(Me.hWnd, 0,-1, 1)

Select Case ret


Case -1
MsgBox "Error during format operation"
Case -2
MsgBox "Operation canceled by user"
Case -3
MsgBox "This drive cannot be formatted"
Case Else
MsgBox "Done"
End Select
End Sub
Capitolo 10 - Programmazione ad oggetti
Implementare proprietà Variant che possono contenere
oggetti
' alla proprietà Tag può essere assegnato sia un oggetto
' sia un valore di altro tipo
Dim m_Tag As Variant

Property Get Tag() As Variant


If IsObject(m_Tag) Then
Set Tag = m_Tag
Else
Tag = m_Tag
End If
End Property

' questa procedura viene invocata quando si assegna


' un oggetto alla proprietà Tag
Property Set Tag(newValue As Variant)
Set m_Tag = newValue
End Property

' questa procedura viene invocata quando si assegna


' un valore di altro tipo alla proprietà Tag
Property Let Tag(newValue As Variant)
m_Tag = newValue
End Property

Un template per creare moduli di classe di tipo collection


VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
Persistable = 0 'NotPersistable
DataBindingBehavior = 0 'vbNone
DataSourceBehavior = 0 'vbNone
MTSTransactionMode = 0 'NotAnMTSObject
END
Attribute VB_Name = "CollectionClassName"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False

'----------------------------------------------------
' Un template per le classi Collection
'----------------------------------------------------
'
' Cosa fare dopo aver aggiunto questo template
' al programma corrente:
'
' 1) modificare il nome della classe a seconda delle necessità
' 2) utilizzare il comando "search and replace" per sostituire tutte le
' istanze di "BaseClassName" (senza virgolette) con il nome effettivo
' della classe di base
' 3) modificare eventualmente il metodo ADD in modo da inizializzare
' le proprietà dell'oggetto NEWITEM prima di aggiungerlo
' alla collezione privata
' 4) eliminare questi commenti
'------------------------------------------------------

Option Explicit

' La collezione privata utilizzata per contenere i dati veri e propri


Private m_PrivateCollection As Collection

Private Sub Class_Initialize()


' l'assegnazione esplicita è leggermente più veloce dell'auto-istanziazione
Set m_PrivateCollection = New Collection
End Sub

' Aggiunge un nuovo elemento BaseClassName alla collezione

Public Sub Add(newItem As BaseClassName, Optional Key As Variant)


Attribute Add.VB_Description = "Adds a member to a Collection object"
' DA FARE: inizializzare in questo punto le proprietà del nuovo elemento
' ...
' aggiungere alla collezione privata
m_PrivateCollection.Add newItem, Key
End Sub
' Rimuove un elemento dalla collezione

Public Sub Remove(index As Variant)


Attribute Remove.VB_Description = "Removes a member from a Collection object"
m_PrivateCollection.Remove index
End Sub

' Restituisce un elemento BaseClassName della collezione

Function Item(index As Variant) As BaseClassName


Attribute Item.VB_Description = "Returns a specific member of a Collection " _
& "object either by position or key"
Attribute Item.VB_UserMemId = 0
Set Item = m_PrivateCollection.Item(index)
End Function

' Restituisce il numero di elementi della collezione

Property Get Count() As Long


Attribute Count.VB_Description = "Returns the number of members in a collection"
Count = m_PrivateCollection.Count
End Property

' Rimuove tutti gli elementi dalla collezione

Public Sub Clear()


Attribute Clear.VB_Description = "Removes all members from a Collection object"
Set m_PrivateCollection = New Collection
End Sub

' Implementa la gestione dell'enumerazione (For Each)

Function NewEnum() As IUnknown


Attribute NewEnum.VB_UserMemId = -4
Attribute NewEnum.VB_MemberFlags = "40"
' delega alla collezione privata
Set NewEnum = m_PrivateCollection.[_NewEnum]
End Function
Capitolo 11 - Oggetti COM
Creare un GUID
Declare Function CoCreateGuid_Alt Lib "OLE32.DLL" Alias "CoCreateGuid" (pGuid _
As Any) As Long
Declare Function StringFromGUID2_Alt Lib "OLE32.DLL" Alias "StringFromGUID2" _
(pGuid As Any, ByVal address As Long, ByVal Max As Long) As Long

Function CreateGUID() As String


Dim res As String, resLen As Long, guid(15) As Byte
res = Space$(128)
CoCreateGuid_Alt guid(0)
resLen = StringFromGUID2_Alt(guid(0), ByVal StrPtr(res), 128)
CreateGUID = Left$(res, resLen - 1)
End Function

Registrare e deregistrare le componenti con i menu di


contesto
REGEDIT4

; ActiveX DLLs

[HKEY_CLASSES_ROOT\.dll]
@="dllfile"

[HKEY_CLASSES_ROOT\dllfile\shell\regdll]
@="Register ActiveX DLL"

[HKEY_CLASSES_ROOT\dllfile\shell\regdll\command]
@="regsvr32.exe \"%L\""

[HKEY_CLASSES_ROOT\dllfile\shell\unregdll]
@="Unregister ActiveX DLL"

[HKEY_CLASSES_ROOT\dllfile\shell\unregdll\command]
@="regsvr32.exe /u \"%L\""

; ActiveX Controls

[HKEY_CLASSES_ROOT\.ocx]
@="ocxfile"

[HKEY_CLASSES_ROOT\ocxfile\shell\regocx]
@="Register OCX Control"

[HKEY_CLASSES_ROOT\ocxfile\shell\regocx\command]
@="regsvr32.exe \"%L\""

[HKEY_CLASSES_ROOT\ocxfile\shell\unregocx]
@="Unregister OCX Control"

[HKEY_CLASSES_ROOT\ocxfile\shell\unregocx\command]
@="regsvr32.exe /u \"%L\""

; ActiveX EXEs

[HKEY_CLASSES_ROOT\.exe]
@="exefile"

[HKEY_CLASSES_ROOT\exefile\shell\regexe]
@="Register ActiveX EXE"

[HKEY_CLASSES_ROOT\exefile\shell\regexe\command]
@="\"%L\" /regserver"

[HKEY_CLASSES_ROOT\exefile\shell\unregexe]
@="Unregister Active EXE"

[HKEY_CLASSES_ROOT\exefile\shell\unregexe\command]
@="\"%L\" /unregserver"

Individuare quando un nuovo controllo viene aggiunto ad un


contenitore ActiveX
Private Sub Timer1_Timer()
Static ctrlCount As Integer
If ctrlCount <> ContainedControls.Count Then
ctrlCount = ContainedControls.Count
' un nuovo controllo è stato aggiunto o rimosso
' dallo UserControl
'
' ...aggiungere del codice in questo punto........
'
End If
End Sub

' ------ codice per vb6

Dim WithEvents tmrNotifier As Timer

Private Sub UserControl_Initialize()


' crea dinamicamente un nuovo controllo Timer
Set tmrNotifier = Controls.Add("VB.Timer", "tmrNotifier")
tmrNotifier.Interval = 500
End Sub

Private Sub tmrNotifier_Timer()


Static ctrlCount As Integer

If ctrlCount <> ContainedControls.Count Then


ctrlCount = ContainedControls.Count
' un nuovo controllo è stato aggiunto o rimosso
' dallo UserControl
'
' ...aggiungere del codice in questo punto........
'
End If
End Sub

Registrare e deregistrare le type library


' Registra una type library

Sub RegisterTypeLib(ByVal TypeLibFile As String)


Dim TLI As New TLIApplication
' solleva un errore se non è in grado di registrare
' (ad esempio file not found or not a TLB)
TLI.TypeLibInfoFromFile(TypeLibFile).Register
End Sub

' Deregistra una type library

Sub UnregisterTypeLib(ByVal TypeLibFile As String)


Dim TLI As New TLIApplication
' solleva un errore se non è in grado di deregistrare
TLI.TypeLibInfoFromFile(TypeLibFile).UnRegister
End Sub

Determinare il CLSID di un oggetto COM


Function GetObjectGUID(Object As Object) As String
Dim TLI As New TLIApplication
GetObjectGUID = TLI.InterfaceInfoFromObject(Object).Guid
End Function

Ottenere il valore Command$ da una DLL ActiveX


Private Declare Function GetCommandLine Lib "kernel32" Alias "GetCommandLineA" _
() As Long
Private Declare Function lstrcpy Lib "kernel32" Alias "lstrcpyA" (ByVal _
lpString1 As String, ByVal lpString2 As Long) As Long

' all'interno della DLL ActiveX

Dim lpStr As Long, i As Long


Dim buffer As String
Dim exePath As String, cmdLine As String

' ottiene un puntatore alla riga di comando


lpStr = GetCommandLine()
' copia in un buffer locale
buffer = Space$(512)
lstrcpy buffer, lpStr
' estrae la stringa che termina con un null
buffer = Left$(buffer, InStr(buffer & vbNullChar, vbNullChar) - 1)

If Left$(buffer, 1) = """" Then


' se la stringa comincia con le doppie virgolette,
' trova quelle di chiusura
i = InStr(2, buffer, """")
exePath = Mid$(buffer, 2, i - 2)
' ciò che rimane è la riga di comando
cmdLine = LTrim$(Mid$(buffer, i + 1))
Else
' altrimenti trova lo spazio che separa
' il nome/percorso dell'EXE e la riga di comando
i = InStr(buffer, " ")
exePath = Left$(buffer, i - 1)
cmdLine = LTrim$(Mid$(buffer, i))
End If

Determinare se una DLL ActiveX viene utilizzata da un


programma interpretato
Private Declare Function EnumThreadWindows Lib "user32" (ByVal dwThreadId As _
Long, ByVal lpfn As Long, ByVal lParam As Long) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal _
hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long

' questa variabile viene condivisa dalle due routine che seguono
Dim m_ClientIsInterpreted As Boolean

' restituisce True se l'applicazione client di questa DLL


' è un programma Visual Basic interpretato in esecuzione all'interno dell'IDE
'
' NOTA: questo codice deve essere inserito in un modulo BAS
' all'interno di un progetto ActiveX DLL

Function ClientIsInterpreted() As Boolean


EnumThreadWindows App.ThreadID, AddressOf EnumThreadWindows_CBK, 0
ClientIsInterpreted = m_ClientIsInterpreted
End Function

' questa è una funzione di callback che viene eseguita per ogni
' finestra appartenente allo stesso thread della DLL

Public Function EnumThreadWindows_CBK(ByVal hWnd As Long, _


ByVal lParam As Long) As Boolean
Dim buffer As String * 512
Dim length As Long
Dim windowClass As String

' ottiene il nome della classe per questa finestra


length = GetClassName(hWnd, buffer, Len(buffer))
windowClass = Left$(buffer, length)

If windowClass = "IDEOwner" Then


' questa è la finestra principale dell'IDE di VB, pertanto
' l'applicazione client è interpretata
m_ClientIsInterpreted = True
' restituisce False per interrompere l'enumerazione
EnumThreadWindows_CBK = False
Else
' restituisce True per continuare l'enumerazione
EnumThreadWindows_CBK = True
End If
End Function

Determinare il CLSID associato a un ProgId


Private Declare Function CLSIDFromProgID Lib "ole32.dll" (ByVal lpszProgID As _
Long, pCLSID As Any) As Long
Private Declare Function StringFromCLSID Lib "ole32.dll" (pCLSID As Any, _
lpszProgID As Long) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (dest As _
Any, source As Any, ByVal bytes As Long)
Private Declare Sub CoTaskMemFree Lib "ole32.dll" (ByVal pv As Long)

Function ProgIdToCLSID(ByVal ProgID As String) As String


Dim pResult As Long, pChar As Long
Dim char As Integer, length As Long
Dim guid(15) As Byte
' ottieni il CLSID in forma binaria
CLSIDFromProgID StrPtr(ProgID), guid(0)
' converti ad una stringa, ottieni un puntatore al risultato
StringFromCLSID guid(0), pResult
' trova il carattere NULL finale
pChar = pResult - 2
Do
pChar = pChar + 2
CopyMemory char, ByVal pChar, 2
Loop While char
' leggi l'intera stringa in un'unica operazione
length = pChar - pResult
' no need for a temporary string
ProgIdToCLSID = Space$(length \ 2)
CopyMemory ByVal StrPtr(ProgIdToCLSID), ByVal pResult, length
' rilascia la memoria associata alla stringa
CoTaskMemFree pResult
End Function
Capitolo 12 - Database e oggetti ADO
Consentire all'utente di creare una stringa di connessione
Dim dataLink As New MSDASC.DataLinks
Dim connString As String
Dim cn As New ADODB.Connection

' l'istruzione seguente rende la proprietà modale rispetto alla form corrente
dataLink.hWnd = Me.hWnd
' visualizza la finestra di dialogo
On Error Resume Next
connString = dataLink.PromptNew
If Err = 0 Then
' Crea un oggetto Connection su questa stringa di connessione
cn.ConnectionString = connString
Else
' L'utente ha cancellato l'operazione
End If

Esportare in un file di testo con campi delimitati da virgolette


Dim rs As New ADODB.Recordset, tmp As String
rs.Open "authors", "DSN=pubs", adOpenForwardOnly, , adCmdTable

Open "c:\authors.dat" For Output As #1

' genera le virgolette di apertura per il primo


' campo del primo record
Print #1, """"

' genera il resto del recordset


Do Until rs.EOF
' blocchi da 100 record ciascuno
tmp = rs.GetString(, 100, """,""", """" & vbCrLf & """", "")

If rs.EOF Then
' elimina le doppie virgolette in eccesso
tmp = Left$(tmp, Len(tmp) - 1)
End If
Print #1, tmp;
Loop
Close #1

Creare velocemente una copia di un Recordset di ADO


Dim rsSource As New ADODB.Recordset
Dim rsFiltered As ADODB.Recordset
Dim rsSorted As ADODB.Recordset
Dim pb As New PropertyBag

' apre un Recordset lato client


rsSource.CursorLocation = adUseClient
rsSource.Open "Authors", "DSN=Pubs", adOpenStatic

' applica un filtro


rsSource.Filter = "author like 'J*'"

' crea una copia del Recordset filtrato


pb.WriteProperty "filtered", rsSource
Set rsFiltered = pb.ReadProperty("filtered")

' ordina il Recordset


rsSource.Filter = ""
rsSource.Sort = "Author"

' crea una copia del Recordset ordinato


pb.WriteProperty "sorted", rsSource
Set rsSorted = pb.ReadProperty("sorted")

' libera la memoria


Set pb = Nothing

Minor overhead con i Recordset disconnessi


' Apre un recordset vuoto da un file serializzato. Se il file non esiste,
' utilizza gli ultimi due argomenti per crearlo.
' ConnString dovrebbe essere una stringa di connessione valida.
' Source dovrebbe essere una clausola di SELECT che restituisce zero record
'
' Esempio: Dim rs As ADODB.Recordset
' Set rs = GetEmptyRecordset("c:\empty.rs", "DSN=Pubs",
' ' "SELECT * FROM Authors WHERE 0=1"

Function GetEmptyRecordset(ByVal Filename As String, ByVal ConnString As String, _


ByVal Source As String) As ADODB.Recordset

Dim rs As New ADODB.Recordset

' controlla se il file esiste già


If Len(Dir$(Filename)) = 0 Then
' il file non è stato trovato, si connette al database
rs.Open Source, ConnString, adOpenStatic, adLockBatchOptimistic
' quindi lo salva per le sessioni future
rs.Save Filename
rs.Close
End If

' apre il file


rs.Open Filename, , , , adCmdFile
Set GetEmptyRecordset = rs
End Function

' Riconnette un recordset al database ed esegue


' un aggiornamento batch

Sub ReconnectRecordset(rs As ADODB.Recordset, ByVal ConnString As String)


Dim cn As New ADODB.Connection
' apre la connessione
cn.Open ConnString
' esegue l'aggiornamento batch
Set rs.ActiveConnection = cn
rs.UpdateBatch
' disconnette il recordset e chiude la connessione
Set rs.ActiveConnection = Nothing
cn.Close
End Sub

' --------- esempio di utilizzo -------

' Aggiunge nuovi record alla tabella Authors del database Pubs
' utilizzando un Recordset disconnesso e senza una connessione iniziale
' (più precisamente, questo codice eseguirà una connessione iniziale extra
' solo quando viene eseguito per la prima volta).

Const CONN_STRING = "Provider=SQLOLEDB.1;User ID=sa;" & _


"Initial Catalog=pubs;Data Source=P2"
Const SOURCE_STRING = "SELECT * FROM Authors WHERE 1=0"

Dim rs As New ADODB.Recordset

' Ottiene la struttura del Recordset


Set rs = GetEmptyRecordset("c:\empty.rs", CONN_STRING, SOURCE_STRING)

' Aggiunge un record


rs.AddNew
rs("au_id") = "978-43-6543"
rs("au_fname") = "Francesco"
rs("au_lname") = "Balena"
rs("city") = "Menlo Park"
rs("State") = "CA"
rs("Zip") = "94025"
rs("Contract") = 1
rs.Update

' Aggiungere qui altri record, se lo si desidera


' .....

' Aggiorna il database


ReconnectRecordset rs, CONN_STRING
rs.Close

Leggere e scrivere un campo binario (BLOB)


Sub FileToBlob(fld As ADODB.Field, filename As String)
Const ChunkSize As Long = 8192
Dim fnum As Integer, bytesLeft As Long, bytes As Long
Dim tmp() As Byte

' errore se il field non supporta il metodo GetChunk.


If (fld.Attributes And adFldLong) = 0 Then
Err.Raise 1001, , "Field doesn't support the GetChunk method."
End If
' Errore se non esiste il file
If Dir$(filename) = "" Then Err.Raise 53, , "File not found"

fnum = FreeFile
Open filename For Binary As fnum
' Leggi il file in blocchi di 8K e appendi il contenuto al Field
bytesLeft = LOF(fnum)
Do While bytesLeft
bytes = bytesLeft
If bytes > ChunkSize Then bytes = ChunkSize
ReDim tmp(1 To bytes) As Byte
Get #1, , tmp
fld.AppendChunk tmp
bytesLeft = bytesLeft - bytes
Loop

Close #fnum
End Sub

Sub BlobToFile(fld As ADODB.Field, filename As String)


Const ChunkSize As Long = 8192
Dim fnum As Integer, bytesLeft As Long, bytes As Long
Dim tmp() As Byte
' errore se il field non supporta il metodo GetChunk.
If (fld.Attributes And adFldLong) = 0 Then
Err.Raise 1001, , "Field doesn't support the GetChunk method."
End If
' Cancella il file se esiste, e creane uno nuovo
If Dir$(filename) <> "" Then Kill filename
fnum = FreeFile
Open filename For Binary As fnum
' Leggi il contenuto del Field e scrivi i dati ad un file
' per evitare problemi di memoria il file viene letto a blocchi di 8K
bytesLeft = fld.ActualSize
Do While bytesLeft
bytes = bytesLeft
If bytes > ChunkSize Then bytes = ChunkSize
tmp = fld.GetChunk(bytes)
Put #fnum, , tmp
bytesLeft = bytesLeft - bytes
Loop
Close #fnum
End Sub

Ottenere l'elenco dei driver ODBC


' popola una casella di riepilogo con l'elenco di tutti i DSN disponibili
Dim odbcTool As New ODBCTool.Dsn
Dim Dsn() As String, i As Long
If odbcTool.GetOdbcDriverList(Dsn()) Then
' un valore di ritorno True significa successo
lstOdbcDrivers.Clear
For i = 0 To UBound(dsn)
lstOdbcDrivers.AddItem dsn(i)
Next
Else
' un valore di ritorno False significa errore
MsgBox "Unable to read ODBC driver list", vbExclamation
End If

Ottenere l'elenco dei DSN ODBC


' popola una casella di riepilogo con l'elenco di tutti i DSN disponibili
Dim odbcTool As New ODBCTool.Dsn
Dim Dsn() As String, i As Long
If odbcTool.GetDataSourceList(Dsn()) Then
' un valore di ritorno True significa successo
lstDataSources.Clear
For i = 0 To UBound(dsn)
lstDataSources.AddItem dsn(i)
Next
Else
' un valore di ritorno False significa errore
MsgBox "Unable to read DSN list", vbExclamation
End If

Creare ed eliminare i DSN a runtime


' Le funzioni API per il Registry
Private Declare Function SQLConfigDataSource Lib "ODBCCP32.DLL" (ByVal _
hwndParent As Long, ByVal fRequest As Long, ByVal lpszDriver As String, _
ByVal lpszAttributes As String) As Long
Private Declare Function RegCloseKey Lib "advapi32" (ByVal hKey As Long) As Long
Private Declare Function RegOpenKeyEx Lib "advapi32" Alias "RegOpenKeyExA" _
(ByVal hKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, _
ByVal samDesired As Long, ByRef phkResult As Long) As Long
Private Declare Function RegQueryValueEx Lib "advapi32" Alias _
"RegQueryValueExA" (ByVal hKey As Long, ByVal lpValueName As String, _
ByVal lpReserved As Long, ByRef lpType As Long, ByVal lpData As String, _
ByRef lpcbData As Long) As Long
Const REG_SZ = 1
Const KEY_ALL_ACCESS = &H2003F
Const HKEY_CURRENT_USER = &H80000001

Public Const ODBC_ADD_DSN = 1 ' Aggiunge una sorgente dati


Public Const ODBC_REMOVE_DSN = 3 ' Elimina la sorgente dati

Sub MakeDSN(ByVal sDSN As String, ByVal sDriver As String, _


ByVal sDBFile As String, ByVal lAction As Long)

Dim sAttributes As String


Dim sDBQ As String
Dim lngRet As Long

Dim hKey As Long


Dim regValue As String
Dim valueType As Long

' interroga il Registry per controllare se il DSN è già stato installato


' apre la chiave
If RegOpenKeyEx(HKEY_CURRENT_USER, "Software\ODBC\ODBC.INI\" & sDSN, 0, _
KEY_ALL_ACCESS, hKey) = 0 Then
' zero significa nessun errore => estrae il valore della chiave "DBQ"
regValue = String$(1024, 0)
' alloca lo spazio per la variabile
If RegQueryValueEx(hKey, "DBQ", 0, valueType, regValue, _
Len(regValue)) = 0 Then
' zero significa OK, pertanto è possibile estrarre il valore
If valueType = REG_SZ Then
sDBQ = Left$(regValue, InStr(regValue, vbNullChar) - 1)
End If
End If
' chiude la chiave
RegCloseKey hKey
End If

' Esegue l'azione solo se viene aggiunto un DSN che non esiste
' oppure rimuove un DSN esistente
If (sDBQ = "" And lAction = ODBC_ADD_DSN) Or (sDBQ <> "" And lAction = _
ODBC_REMOVE_DSN) Then

' controlla che il file esista effettivamente


If Len(Dir$(sDBFile)) = 0 Then
MsgBox "Database file doesn't exist!", vbOKOnly + vbCritical
Exit Sub
End If
sAttributes = "DSN=" & sDSN & vbNullChar & "DBQ=" & sDBFile & vbNullChar
lngRet = SQLConfigDataSource(0&, lAction, sDriver, sAttributes)
End If
End Sub

' --- esempio -----

sDriver = "Microsoft Access Driver (*.mdb)"


sName = "DSN Creation Test"
sFile = App.Path & "\MyDatabase.mdb"
MakeDSN sName, sDriver, sFile, ODBC_ADD_DSN

Leggere e scrivere i file ODBC di definizione delle sorgenti


dati
' crea un File DSN che punta ad un database di SQL Server
'
' se ServerName viene omesso, si connette all'istanza locale di SQL Server

Sub CreateSQLServerDSN(ByVal DSNFile As String, ByVal DatabaseName As String, _


ByVal UserName As String, ByVal Password As String, _
Optional ByVal ServerName As String, Optional ByVal Description As String)
Dim fnum As Integer
Dim isOpen As Boolean

On Error GoTo ErrorHandler

fnum = FreeFile
Open DSNFile For Output As #fnum
isOpen = True

Print #fnum, "[ODBC]"


Print #fnum, "DRIVER=SQL Server"
Print #fnum, "UID=" & UserName
Print #fnum, "DATABASE=" & DatabaseName
Print #fnum, "SERVER=" & IIf(ServerName = "", "(local)", ServerName)
If Not IsMissing(Description) Then
Print #fnum, "DESCRIPTION=" & Description
End If
Close #fnum
Exit Sub

ErrorHandler:
If isOpen Then Close #fnum
Err.Raise Err.Number, , Err.Description

End Sub

Estrarre l'elenco delle stored procedure di SQL Server


Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim connString As String, sql As String

' Apre una connessione – sostituire "myserver" con il nome del SQL Server da usare
connString = "Provider=SQLOLEDB;Data Source=myserver;Initial Catalog=Pubs"
cn.Open connString, "sa", ""
' Apre il recordset
sql = "Select * from sysobjects where type = 'P' and category = 0"
rs.Open sql, cn, adOpenStatic, adLockReadOnly, adCmdText
' Carica i risultati in un controllo DataGrid
Set DataGrid1.DataSource = rs
DataGrid1.Refresh

' ------ secondo listato --------

' Questo codice assume che il recordset RS punti effettivamente


' alla stored procedure della quale si desiderano
' estrarre i parametri
Dim rs2 As New ADODB.Recordset, sql2 As String
sql2 = "SELECT syscolumns.Name, systypes.Name, syscolumns.length " & _
"FROM syscolumns, systypes " & "WHERE syscolumns.xtype = systypes.type AND " _
& "syscolumns.id = " & rs("ID")
' apre un secondo recordset
rs2.Open sql2, cn, adOpenStatic, adLockReadOnly, adCmdText
' Lo assegna ad una seconda griglia
Set DataGrid2.DataSource = rs2
DataGrid2.Refresh

Salvare una matrice in una tabella di SQL Server con VB ed


ADO
' Nome della classe: CDBVariant

Dim mCn As ADODB.Connection

' imposta la connessione che deve essere utilizzata dalla classe


Public Property Set DataSource(newVal As ADODB.Connection)
Set mCn = newVal
End Property

' legge il Variant dal DB


' tblName = nome della tabella sorgente
' Name = nome della variabile da leggere
Public Property Get DBVariant(tblName As String, Name As String) As Variant
Dim strFilter As String, pb As New PropertyBag, vtTemp As Variant
Dim rs As New ADODB.Recordset

strFilter = "Name = '" & Name & "'"

rs.Open "SELECT * FROM " & tblName & " WHERE " & strFilter, mCn, _
adOpenStatic, adLockPessimistic

If Not rs.EOF And Not rs.BOF Then


vtTemp = rs!Data
pb.Contents = vtTemp
If pb.ReadProperty("Size", 0) = 0 Then
DBVariant = pb.ReadProperty("Value", vbNull)
Else
Dim vtTemp2() As Variant
ReDim vtTemp2(pb.ReadProperty("Size", 0)) As Variant
For i = 0 To UBound(vtTemp2)
vtTemp2(i) = pb.ReadProperty(CStr(i))
Next
DBVariant = vtTemp2
End If
End If

Set rs = Nothing

End Property

' scrive il Variant nel DB


' tblName = nome della tabella di destinazione
' Name = nome della variabile
Public Property Let DBVariant(tblName As String, Name As String, _
newValue As Variant)
Dim strFilter As String, pb As New PropertyBag
Dim rs As New ADODB.Recordset
Dim bRemove As Boolean

strFilter = "Name = '" & Name & "'"

rs.Open "SELECT * FROM " & tblName & " WHERE " & strFilter, mCn, _
adOpenStatic, adLockPessimistic

If VarType(newValue) = vbObject Then


bRemove = newValue Is Nothing
Else
bRemove = False
End If

If Not bRemove Then

If rs.EOF And rs.BOF Then


rs.AddNew
rs!Name = Name
End If

If (VarType(newValue) And vbArray) = 0 Then


' valore scalare
pb.WriteProperty "Size", 0
pb.WriteProperty "Value", newValue
Else
' preparazione di uno stream per array
pb.WriteProperty "Size", UBound(newValue)
For i = 0 To UBound(newValue)
pb.WriteProperty CStr(i), newValue(i)
Next
End If

rs!Data = CVar(pb.Contents)
rs.Update
Else
If Not rs.EOF And Not rs.BOF Then
rs.Delete
End If
End If

Set rs = Nothing

End Property

' ---------- esempio d'utilizzo ------------

Dim t As New cDBVariant


Dim cn As New ADODB.Connection

Dim vArray(100) As Integer


Dim vArray2 As Variant

' apre una connessione


cn.Open "file name=c:\udl\master.udl"

For i = 1 To UBound(vArray)
vArray(i) = i
Next

' assegna la connessione alla classe


Set t.DataSource = cn

' scrive la matrice


t.DBVariant("tblArrays", "vArray") = vArray

' legge la matrice


vArray2 = t.DBVariant("tblArrays", "vArray")

' scarica i risultati


Debug.Print UBound(vArray2)

For i = 1 To UBound(vArray2)
Debug.Print vArray2(i)
Next

Connettere un Recordset stand-alone ad un database


utilizzando l'XML
' Richiede IE 5 ed un riferimento a
' ADO 2.5 e alla type library MSXML2
Sub LinkRsToDB(ByVal rs As ADODB.Recordset, ByVal BaseCatalog As String, _
ByVal BaseTable As String)
Dim DOMDoc As New DOMDocument
Dim Root As IXMLDOMNode
Dim Schema As IXMLDOMNode
Dim Node As IXMLDOMNode
Dim Item As IXMLDOMNode
Dim NewItem As IXMLDOMAttribute

' apre il recordset se necessario


If (rs.State And adStateOpen) = 0 Then rs.Open

' salva il recordset direttamente nel parser XML


rs.Save DOMDoc, adPersistXML

' Schema è il primo nodo sotto la radice


Set Root = DOMDoc.childNodes.Item(0)
Set Schema = Root.childNodes(0)

For Each Node In Schema.childNodes(0).childNodes


If UCase(Node.baseName) = "ATTRIBUTETYPE" Then
For Each Item In Node.Attributes
If Item.baseName = "write" Then
' Rimuove questo attributo, che non verrà gestito
' all'atto del caricamento del Recordset
Node.Attributes.removeQualifiedItem Item.baseName, _
Item.namespaceURI
Exit For
End If
Next

' Crea gli attributi mancanti per il Recordset


Set NewItem = DOMDoc.createAttribute("rs:basecatalog")
NewItem.Value = BaseCatalog
Node.Attributes.setNamedItem NewItem

Set NewItem = DOMDoc.createAttribute("rs:basetable")


NewItem.Text = BaseTable
Node.Attributes.setNamedItem NewItem

Set NewItem = DOMDoc.createAttribute("rs:basecolumn")


' Assume che il nome logico sia uguale al nome fisico
NewItem.Text = Node.Attributes(0).Text
Node.Attributes.setNamedItem NewItem

' questo attributo è necessario in ADO 2.5


Set NewItem = DOMDoc.createAttribute("rs:writeunknown")
NewItem.Text = "true"
Node.Attributes.setNamedItem NewItem
End If
Next

' ricarica il recordset dal parser


rs.Close
rs.Open DOMDoc
End Sub

' ------- usempio di utilizzo

Dim rs As New ADODB.Recordset


Dim cn As New ADODB.Connection

rs.Fields.Append "au_id", adVarChar, 11


rs.Fields.Append "au_lname", adVarChar, 40
rs.Fields.Append "au_fname", adVarChar, 20
rs.Fields.Append "phone", adChar, 12
rs.Fields.Append "address", adVarChar, 40
rs.Fields.Append "city", adVarChar, 20
rs.Fields.Append "state", adChar, 2
rs.Fields.Append "zip", adChar, 5
rs.Fields.Append "contract", adBoolean

' Aggiunge un nuovo record al recordset


rs.Open
rs.AddNew
rs("au_id") = "978-43-6543"
rs("au_fname") = "Francesco"
rs("au_lname") = "Balena"
rs("city") = "Menlo Park"
rs("State") = "CA"
rs("Zip") = "94025"
rs("Contract") = 1
rs.Update

' utilizza la routine LinkRsToDB per modificare il Recordset


' e fa in modo che possa connettersi ad un database:
LinkRsToDB rs, BaseCatalog:="Pubs", BaseTable:="Authors"
' apre la connessione vera e propria e la associa al recordset
cn.Open "DSN=Pubs"
Set rs.ActiveConnection = cn

' Aggiorna il database


' non eseguendo la procedura LinkRsToDB, si otterrebbe un errore
' "Insufficient base table information for updating or refreshing.")
rs.UpdateBatch

' ------- versione per ADO 2.1

' Richiede ADO 2.1 e la type library MSXML 2.0

Sub LinkRsToDB(ByVal rs As ADODB.Recordset, ByVal BaseCatalog As String, _


ByVal BaseTable As String)
Dim DOMDoc As New DOMDocument
Dim Root As IXMLDOMNode
Dim Schema As IXMLDOMNode
Dim Node As IXMLDOMNode
Dim Item As IXMLDOMNode
Dim NewItem As IDOMAttribute
Dim XMLSource As String
Dim tempFileName As String
Dim fileNum As Integer

' modificare questo nome di file o utilizzare la routine GetTempFile, che


' può essere scaricata da questo URL:
' http://www.vb2themax.com/Item.asp?PageID=CodeBank&ID=48
tempFileName = "C:\Empty.xml"
' verifica che il file non ci sia già
On Error Resume Next
Kill tempFileName
On Error GoTo 0

' apre il recorset se necessario


If (rs.State And adStateOpen) = 0 Then rs.Open

' salva il recordset nel file temporaneo in formato XML


rs.Save tempFileName, adPersistXML

' ricarica il testo XML nel parser


DOMDoc.Load tempFileName
Set Root = DOMDoc.childNodes.Item(0)
Set Schema = Root.childNodes(0)

For Each Node In Schema.childNodes


If UCase(Node.baseName) = "ATTRIBUTETYPE" Then
For Each Item In Node.Attributes
If Item.baseName = "write" Then
' Rimuove questo attributo, che non verrà gestito
' all'atto del caricamento del Recordset
Node.Attributes.removeNamedItem Item.nodeName
Exit For
End If
Next

' Crea gli attributi mancanti per il Recordset


Set NewItem = DOMDoc.createAttribute("rs:basecatalog")
NewItem.Value = BaseCatalog
Node.Attributes.setNamedItem NewItem

Set NewItem = DOMDoc.createAttribute("rs:basetable")


NewItem.Value = BaseTable
Node.Attributes.setNamedItem NewItem

Set NewItem = DOMDoc.createAttribute("rs:basecolumn")


' Assume che il nome logico sia uguale al nome fisico
NewItem.Value = Node.Attributes(0).nodeValue
Node.Attributes.setNamedItem NewItem
End If
Next

' i Recordset di ADO conoscono solo il carattere apostrofo


XMLSource = Replace(DOMDoc.xml, Chr(34), "'")

' salva l'XML modificato nel file temporaneo


Kill tempFileName
fileNum = FreeFile
Open tempFileName For Output As #fileNum
Print #fileNum, XMLSource
Close #fileNum

' ricarica il recordset da qui


rs.Close
rs.Open tempFileName, , , , adCmdFile

' elimina il file temporaneo


Kill tempFileName
End Sub

' --------- ultimo listato ----------

Dim rs As New ADODB.Recordset


Dim cn As New ADODB.Connection

' solo un campo in questo esempio


rs.Fields.Append "Author", adVarChar, 50
rs.Open

' Aggiunge un nuovo record al recordset


rs.AddNew
rs("author") = "Francesco Balena"
rs.Update

' fa in modo che il recordset sia in grado di connettersi


LinkRsToDB rs, BaseCatalog:="", BaseTable:="Authors"

' questo data link punta a Biblio.mdb


cn.Open "File Name=C:\DataLinks\Biblio.udl"
Set rs.ActiveConnection = cn

' invia gli aggiornamenti al database


rs.UpdateBatch
Capitolo 13 - Linguaggio SQL e programmazione con
Microsoft SQL Server
Elencare tutti gli indici di un database di SQL Server
/* SP sp_help_db_indexes: elenca tutti gli indici di tutte le tabelle del database
*/

CREATE procedure sp_help_db_indexes


AS

declare @empty varchar(1)


select @empty = ''

-- 35 è la lunghezza del nome del campo della tabella master.dbo.spt_values


declare @IgnoreDuplicateKeys varchar(35),
@Unique varchar(35),
@IgnoreDuplicateRows varchar(35),
@Clustered varchar(35),
@Hypotethical varchar(35),
@Statistics varchar(35),
@PrimaryKey varchar(35),
@UniqueKey varchar(35),
@AutoCreate varchar(35),
@StatsNoRecompute varchar(35)

select @IgnoreDuplicateKeys = name from master.dbo.spt_values


where type = 'I' and number = 1 --ignore duplicate keys
select @Unique = name from master.dbo.spt_values
where type = 'I' and number = 2 --unique
select @IgnoreDuplicateRows = name from master.dbo.spt_values
where type = 'I' and number = 4 --ignore duplicate rows
select @Clustered = name from master.dbo.spt_values
where type = 'I' and number = 16 --clustered
select @Hypotethical = name from master.dbo.spt_values
where type = 'I' and number = 32 --hypotethical
select @Statistics = name from master.dbo.spt_values
where type = 'I' and number = 64 --statistics
select @PrimaryKey = name from master.dbo.spt_values
where type = 'I' and number = 2048 --primary key
select @UniqueKey = name from master.dbo.spt_values
where type = 'I' and number = 4096 --unique key
select @AutoCreate = name from master.dbo.spt_values
where type = 'I' and number = 8388608 --auto create
select @StatsNoRecompute = name from master.dbo.spt_values
where type = 'I' and number = 16777216 --stats no recompute
select o.name,
i.name,
'index description' = convert(varchar(210), --bits 16 off, 1, 2, 16777216 on
case when (i.status & 16)<>0 then @Clustered else 'non'+@Clustered end
+ case when (i.status & 1)<>0 then ', '+@IgnoreDuplicateKeys else @empty end
+ case when (i.status & 2)<>0 then ', '+@Unique else @empty end
+ case when (i.status & 4)<>0 then ', '+@IgnoreDuplicateRows else @empty end
+ case when (i.status & 64)<>0 then ', '+@Statistics else
case when (i.status & 32)<>0 then ', '+@Hypotethical else @empty end end
+ case when (i.status & 2048)<>0 then ', '+@PrimaryKey else @empty end
+ case when (i.status & 4096)<>0 then ', '+@UniqueKey else @empty end
+ case when (i.status & 8388608)<>0 then ', '+@AutoCreate else @empty end
+ case when (i.status & 16777216)<>0 then ', '+@StatsNoRecompute else @empty end),
'index column 1' = index_col(o.name,indid, 1),
'index column 2' = index_col(o.name,indid, 2),
'index column 3' = index_col(o.name,indid, 3)
from sysindexes i, sysobjects o
where i.id = o.id and
indid > 0 and indid < 255 -all the clustered (=1), non clusterd (>1 and <251), and text or
image (=255)
and o.type = 'U' -user table
--ignore the indexes for the autostat
and (i.status & 64) = 0 -index with duplicates
and (i.status & 8388608) = 0 -auto created index
and (i.status & 16777216)= 0 --stats no recompute
order by o.name

Elencare i permessi degli utenti sugli oggetti di un database


/* SP sp_get_object_permissions: elenca i permessi di ogni utenti sulle tabelle, le viste
e le stored procedure del database attivo
*/
CREATE PROCEDURE sp_get_object_permissions
AS
DECLARE @obj_name VARCHAR(30)
DECLARE @obj_type CHAR(2)
DECLARE @message VARCHAR(75)
DECLARE tblnames CURSOR FOR
SELECT name, type
FROM sysobjects
WHERE type IN ('U','P','V')
ORDER BY 2 DESC

OPEN tblnames
FETCH NEXT FROM tblnames INTO @obj_name, @obj_type
WHILE (@@fetch_status <> -1)
BEGIN
IF (@@fetch_status <> -2)
BEGIN
IF @obj_type = 'U'
SELECT @message = 'Checking the rights for the Table '
IF @obj_type = 'V'
SELECT @message = ' Checking the rights for the Vable '
IF @obj_type = 'P'
SELECT @message = ' Checking the rights for the Stored Procedure '
SELECT @message = @message + RTRIM(UPPER(@obj_name))
PRINT @message
EXEC ('sp_helprotect ' + @obj_name )
END
FETCH NEXT FROM tblnames INTO @obj_name, @obj_type
END
CLOSE tblnames
DEALLOCATE tblnames

Condivisione di connessioni
/* Begin of script InstallCS.Sql */

-- seleziona del database master


USE master
go

IF EXISTS (SELECT name FROM sysobjects


WHERE name = 'sp_CSUnpublish' AND type = 'P')
DROP PROCEDURE sp_CSUnpublish
go

IF EXISTS (SELECT name FROM sysobjects


WHERE name = 'sp_CSPublish' AND type = 'P')
DROP PROCEDURE sp_CSPublish
go

IF EXISTS (SELECT name FROM sysobjects


WHERE name = 'sp_CSInitialize' AND type = 'P')
DROP PROCEDURE sp_CSInitialize
go

IF EXISTS (SELECT name FROM sysobjects


WHERE name = 'sp_CSSubscribe' AND type = 'P')
DROP PROCEDURE sp_CSSubscribe
go

IF EXISTS (SELECT name FROM sysobjects


WHERE name = 'sp_CSUnsubscribe' AND type = 'P')
DROP PROCEDURE sp_CSUnsubscribe
go

-- Stored procedure di inizializzazione


CREATE PROCEDURE sp_CSInitialize AS
BEGIN
-- Elimina la tabella precedente the previous table
drop table IsolationTemp
-- Crea una nuova copia della tabella di supporto
create table IsolationTemp(SysUser varchar(64) primary key, BindToken varchar(4096), SysSPID
integer)
-- Consente agli utenti del gruppo public di utilizzare completamente la tabella
GRANT ALL ON IsolationTemp TO public
END
go

-- Imposta la stored procedura sp_CSInitialize in modo che parta automaticamente


sp_procoption 'sp_CSInitialize', 'startup', true
go

-- Termina la pubblicazione del proprio livello di isolamento


CREATE PROCEDURE sp_CSUnpublish AS
BEGIN
-- Elimina i record dell'utente corrente dalla tabella di supporto
DELETE FROM master.dbo.IsolationTemp where sysUser = system_user
END
go
-- Pubblica il proprio livello di isolamento
CREATE PROCEDURE sp_CSPublish AS
BEGIN
DECLARE @bind_token varchar(255)
-- Ottiene il token del proprio livello di isolamento
EXECUTE sp_getbindtoken @bind_token OUTPUT
-- Elimina le voci precedenti
exec sp_CSUnpublish
-- Crea le nuove voci dal livello di isolamento corrente
INSERT INTO master.dbo.IsolationTemp (SysUser, BindToken, SysSPID) VALUES (system_user,
@bind_token, @@SPID)
END
go

-- Sottoscrive un livello di isolamento pubblicato


CREATE PROCEDURE sp_CSSubscribe AS
BEGIN
declare @BindToken varchar(4096)
-- Ottiene il token
select @BindToken = (SELECT TOP 1 BindToken From master.dbo.IsolationTemp (NOLOCK) Where SysUser =
SYSTEM_USER)
-- Aggancia il livello di isolamento
exec sp_BindSession @BindToken
END
go

-- Rifiuta il livello di isolamento sottoscritto


CREATE PROCEDURE sp_CSUnsubscribe AS
BEGIN
exec sp_BindSession NULL
END
go

grant all on sp_CSUnpublish to public


grant all on sp_CSPublish to public
grant all on sp_CSInitialize to public
grant all on sp_CSSubscribe to public
grant all on sp_CSUnsubscribe to public

-- Prima esecuzione della stored procedure di inizializzazione


exec sp_CSInitialize
go

/* End of script InstallCS.Sql */

Effettuare il backup di un database MSDE utilizzando T-SQL


-- Creazione del job di backup
USE msdb
EXEC sp_add_job @job_name = 'BackupJob',
@enabled = 1,
@description = 'BackupJob',
@owner_login_name = 'sa',
@notify_level_eventlog = 2,
@notify_level_email = 2,
@notify_level_netsend =2,
@notify_level_page = 2
@notify_email_operator_name = 'myMailAddress'
go
-- Backup dei dati
USE msdb
EXEC sp_add_jobstep @job_name = 'BackupJob',
@step_name = 'msdb database backup',
@subsystem = 'TSQL',
@command = 'BACKUP DATABASE msdb TO DISK = ''c:\msdb.dat_bak''',
@on_success_action = 3,
@retry_attempts = 5,
@retry_interval = 5
go
-- Backup del file di log
USE msdb
EXEC sp_add_jobstep @job_name = 'myTestBackupJob',
@step_name = 'msdb Log backup',
@subsystem = 'TSQL',
@command = 'BACKUP LOG msdb TO DISK = ''c:\msdb.log_bak''',
@on_success_action = 1,
@retry_attempts = 5,
@retry_interval = 5
go
-- Specifica del server
USE msdb
EXEC sp_add_jobserver @job_name = 'BackupJob', @server_name = N'(local)'

-- Esecuzione immediate del job


USE msdb
EXEC sp_start_job @job_name = 'myTestBackupJob'
Eseguire query distrubuite utilizzando un linked server
Dim cn As ADODB.Connection

Set cn = New ADODB.Connection


cn.Open "Provider=SQLOLEDB;Data Source=(local);Initial Catalog=master;User " _
& "Id=sa;Password=;"
cn.Execute "EXEC sp_addlinkedserver 'MyLinkedServerName', 'Jet 4.0', " _
& "'Microsoft.Jet.OLEDB.4.0', 'c:\program files\microsoft " _
& "office\office\samples\northwind.mdb'"
cn.Execute "EXEC sp_addlinkedsrvlogin ' MyLinkedServerName ', FALSE, NULL, " _
& "'admin', ''"
cn.Close
Set cn = Nothing

' -------- secondo listato

Dim rs As ADODB.Recordset
Dim cn As ADODB.Connection
Dim SQL As String

Set cn = New ADODB.Connection


Set rs = New ADODB.Recordset

' la funzione OPENQUERY


SQL = " Select a.* from OPENQUERY(MyLinkedServerName, 'Select * from " _
& "Customers') a"

' la funzione OPENROWSET


SQL = "SELECT * From OPENROWSET('Microsoft.Jet.OLEDB.4.0','c:\program " _
& "files\microsoft office\office\samples\northwind.mdb'; 'Admin';'', " _
& "Customers)"

' la sintassi a quattro parti


SQL = "Select * from MyLinkedServerName...Customers"
cn.CursorLocation = adUseClient
cn.Open "Provider=SQLOLEDB;Data Source=(local);Initial Catalog=master;User " _
& "Id=sa;Password=;"
rs.Open SQL, cn, adOpenStatic, adLockReadOnly
' ...
rs.Close
set rs = Nothing
cn.Close
Set cn = Nothing

Inviare Fax da Microsoft SQL Server utilizzando Microsoft


Word
DECLARE @WordDocument int
DECLARE @WordApplication int
DECLARE @Content int
DECLARE @visible int
DECLARE @hr int
DECLARE @text varchar(4096)

-- Set WordDocument = CreateObject("Word.Document")


EXEC @hr = sp_OACreate 'word.Document', @WordDocument OUT

-- Set Application = WordDocument.Application


IF @hr = 0
EXEC @hr = sp_OAGetProperty @WordDocument, 'Application', @WordApplication OUT

-- Set Content = WordDocument.Content


IF @hr = 0
EXEC @hr = sp_OAGetProperty @WordDocument, 'Content', @Content OUT

-- Content.Text = "Word Document " + vbNewLine + "generated by SQL Server"


IF @hr = 0
BEGIN
set @text = 'Word Document' + char(10) + 'generated by SQL Server'
EXEC @hr = sp_OASetProperty @Content, 'Text', @text
END

-- WordApplication.Visible = True
IF @hr = 0
BEGIN
EXEC @hr = sp_OASetProperty @WordApplication, 'Visible', 1
waitfor delay '00:00:10'
END

-- WordDocument.SendFax "", "Send a fax from SQL Server"


IF @hr = 0
EXEC @hr = sp_OAMethod @WordDocument, 'SendFax', NULL, '', 'Invio fax da SQL Server'
IF @hr <> 0
BEGIN
print "ERROR OCCURRED: " + cast(@hr as varchar(128))
RETURN
END

Elencare tutte le installazioni di SQL Server esistenti


' NOTA: questo codice assume che sia stato aggiunto un riferimento alla type library
' SQL-DMO nella finestra di dialogo References
Dim sqlApp As New SQLDMO.Application
Dim NameList As SQLDMO.NameList
Dim index As Long

Set NameList = sqlApp.ListAvailableSQLServers


cboServers.Clear
' l'installazione locale di SQL Server deve essere aggiunta manualmente
cboServers.AddItem "."
For index = 1 To NameList.Count
cboServer.AddItem NameList.Item(index)
Next

Lanciare ed interrompere programmaticamente il servizio


principale di SQL Server
' NOTA: questo codice assume che sia stato aggiunto un riferimento
' alla type library SQL-DMO nella finestra di dialogo References

' lancia, sospende, riavvia ed interrompe il servizio principale di SQL Server


Dim SQLServer As New SQLDMO.SQLServer

' per lanciare il servizio è necessario specificare


' il nome del server e le credenziali dell'utente
SQLServer.Start False, "MyServer", "sa", "mypwd"

' per sospendere, riavviare ed interrompere il servizio


' non occorre alcun argomento aggiuntivo
SQLServer.Pause
SQLServer.Continue
SQLServer.Stop

' mostra lo stato corrente del servizio


Select Case SQLServer.Status
Case SQLDMOSvc_Paused: lblStatus = "Paused"
Case SQLDMOSvc_Running: lblStatus = "Running"
Case SQLDMOSvc_Starting: lblStatus = "Starting"
Case SQLDMOSvc_Stopped: lblStatus = "Stopped"
End Select

Monitoraggio avanzato dei lock


/* Start of sp_ExBlockingLocks.sql stored procedure*/
/* Original version: Ron Soukup, Kalen Delany */

use master
go
IF EXISTS( SELECT name FROM sysobjects WHERE name = 'sp_ExBlockingLocks' AND type = 'P')
drop proc sp_ExBlockingLocks
go
create procedure sp_ExBlockingLocks
as

set nocount on

select DISTINCT
sp.hostname HostName,
sp.program_name "Prg Name",
sp.hostprocess "PID",
sp.nt_domain "DOMAIN",
sp.nt_username "Usr Name",
sp.net_address "Net Addr",
sp.loginame "Login",
convert (smallint, l1.req_spid) As spid,
l1.rsc_dbid As dbid,
(SELECT name from master.dbo.sysdatabases (nolock) where dbid = l1.rsc_dbid) As Db,
l1.rsc_objid As ObjId,
(select name from sysobjects (nolock) where id = l1.rsc_objid) As Obj,
l1.rsc_indid As IndId,
substring (v.name, 1, 4) As Type,
substring (l1.rsc_text, 1, 16) as Resource,
substring (u.name, 1, 8) As Mode,
substring (x.name, 1, 5) As Status

from master.dbo.syslockinfo l1,


master.dbo.syslockinfo l2,
master.dbo.spt_values v,
master.dbo.spt_values x,
master.dbo.spt_values u,
master.dbo.sysprocesses sp

where
l1.rsc_type = v.number
and v.type = 'LR'
and l1.req_status = x.number
and x.type = 'LS'
and l1.req_mode + 1 = u.number
and u.type = 'L'
and l1.rsc_type <>2 /* not a DB lock */
and l1.rsc_dbid = l2.rsc_dbid
and l1.rsc_bin = l2.rsc_bin
and l1.rsc_objid = l2.rsc_objid
and l1.rsc_indid = l2.rsc_indid
and l1.req_spid <> l2.req_spid
and l1.req_status <> l2.req_status
and sp.spid = l1.req_spid
order by
substring (l1.rsc_text, 1, 16),
substring (x.name, 1, 5)
return (0)

/* End of stored procedure */


Capitolo 14 – Multimedia
Determinare la durata complessiva di un CD audio
Private Sub cmdDisplayTime_Click()
Dim Value As Integer
Dim TotalTime As String

With MMControl1
' Inizializza il controllo Multimedia
.DeviceType = "CDAudio"
.TimeFormat = 10
.Command = "Open"
' i secondi sono memorizzati nel byte più significativo
Value = (.Length \ 256) And 255
TotalTime = Right$("0" & CStr(Value), 2) & """"
' i minuti sono memorizzati nel byte meno significativo
Value = (.Length And 255) Mod 60
TotalTime = Right$("0" & CStr(Value), 2) & "' " & TotalTime
Value = (.Length And 255) \ 60
If Value > 0 Then
' visualizza la durata solo se necessario
TotalTime = CStr(Value) & "h " & TotalTime
End If
MsgBox TotalTime
End With

End Sub
Capitolo 15 - Le funzioni API di Windows
Determinare l'utilizzo della memoria
Private Type MEMORYSTATUS
dwLength As Long
dwMemoryLoad As Long
dwTotalPhys As Long
dwAvailPhys As Long
dwTotalPageFile As Long
dwAvailPageFile As Long
dwTotalVirtual As Long
dwAvailVirtual As Long
End Type
Private Declare Sub GlobalMemoryStatus Lib "kernel32" Alias "GlobalMemoryStatus" _
(lpBuffer As MEMORYSTATUS)

Sub ShowMemory()
Dim ms As MEMORYSTATUS
Dim msg As String

' richiede i dati in memoria


ms.dwLength = Len(ms)
GlobalMemoryStatus ms
' crea un messaggio per il risultato.
msg = "Total physical memory = " & GetKbytes(ms.dwTotalPhys) & vbCrLf & _
"Available physical memory = " & GetKbytes(ms.dwAvailPhys) & vbCrLf & _
"Memory Load = " & ms.dwMemoryLoad & "%" & vbCrLf & _
"Total Page file = " & GetKbytes(ms.dwTotalPageFile) & vbCrLf & _
"Available Page file = " & GetKbytes(ms.dwAvailPageFile) & vbCrLf & _
"Total Virtual memory = " & GetKbytes(ms.dwTotalVirtual) & vbCrLf & _
"Available Virtual memory = " & GetKbytes(ms.dwAvailVirtual)
MsgBox msg, vbInformation, "Memory information"
End Sub

' formatta una certa quantità di memoria come Kbyte o Megabyte

Function GetKbytes(ByVal amount As Long) As String


' converte in Kbyte
amount = amount \ 1024
GetKbytes = Format(amount, "###,###,###K")
End Function

Verificare la disponibilità di una porta seriale o parallela


' Verifica se una data porta seriale COM è disponibile

Function IsComPortAvailable(ByVal portNum As Integer) As Boolean


Dim fnum As Integer
On Error Resume Next
fnum = FreeFile
Open "COM" & CStr(portNum) For Binary Shared As #fnum
If Err = 0 Then
Close #fnum
IsComPortAvailable = True
End If
End Function

' Verifica se una data porta parallela LPT è disponibile

Function IsLptPortAvailable(ByVal portNum As Integer) As Boolean


Dim fnum As Integer
On Error Resume Next
fnum = FreeFile
Open "LPT" & CStr(portNum) For Binary Shared As #fnum
If Err = 0 Then
Close #fnum
IsLptPortAvailable = True
End If
End Function

Estrarre stringhe ASCIIZ restituite da chiamate alla API di


Windows
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal _
hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
lParam As Any) As Long
Private Const WM_GETTEXT = &HD

Dim buffer As String, i As Long, winText As String


buffer = Space$(512)
SendMessage hWnd, WM_GETTEXT, Len(buffer), ByVal buffer
i = Instr(buffer, vbNullChar)
If i Then
' elimina i caratteri in eccesso
winText = Left$(winText, i - 1)
Else
' si è verificato un errore
winText = ""
End If

Determinare se una funzione API è disponibile


Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal _
lpLibFileName As String) As Long
Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, _
ByVal lpProcName As String) As Long
Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) _
As Long

Function IsProcedureAvailable(ByVal ProcedureName As String, _


ByVal DllFilename As String) As Boolean

Dim hModule As Long, procAddr As Long


' innanzitutto, tenta di caricare il modulo
hModule = LoadLibrary(DllFilename)
If hModule Then
' quindi, estrae l'indirizzo della routine
procAddr = GetProcAddress(hModule, ProcedureName)
' infine, decrementa il contatore di utilizzo della DLL
FreeLibrary hModule
End If
' se procAddr è diverso da zero, la funzione è disponibile
IsProcedureAvailable = (procAddr <> 0)
End Function

Visualizzare la finestra di dialogo per configurare una porta


Private Declare Function ConfigurePort Lib "winspool.drv" Alias _
"ConfigurePortA" (ByVal pName As String, ByVal hWnd As Long, _
ByVal pPortName As String) As Long

' visualizza la finestra di dialogo per la configurazione delle porte seriali


If ConfigurePort(vbNullString, 0&, "COM1:") = Then
MsgBox "Unable to display the system dialog", vbCritical
End If

' visualizza la finestra di dialogo per la configurazione delle porte parallele


If ConfigurePort(vbNullString, 0&, "LPT1:") = Then
MsgBox "Unable to display the system dialog", vbCritical
End If

Creare una utility per ottenere informazioni su una window


Private Type POINTAPI
x As Long
y As Long
End Type
Private Declare Function GetCursorPos Lib "user32" (lpPoint As POINTAPI) As Long
Private Declare Function WindowFromPoint Lib "user32" (ByVal xPoint As Long, _
ByVal yPoint As Long) As Long
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
(ByVal hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal _
hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long

Private Sub Timer1_Timer()


Dim lpPoint As POINTAPI
Dim wndHandle As Long
Dim wndCaption As String
Dim wndClass As String
Dim length As Long

' ottiene la posizione del cursore del mouse


GetCursorPos lpPoint
' ottiene l'handle della finestra che si trova sotto il cursore del mouse
wndHandle = WindowFromPoint(lpPoint.x, lpPoint.y)
' ottiene il titolo/testo della finestra
wndCaption = Space$(256)
length = GetWindowText(wndHandle, wndCaption, Len(wndCaption))
wndCaption = Left$(wndCaption, length)
' ottiene la classe della finestra
wndClass = Space$(256)
length = GetClassName(wndHandle, wndClass, Len(wndClass))
wndClass = Left$(wndClass, length)
' visualizza il risultato nell'etichetta
Label1 = "[" & Hex$(wndHandle) & "] " & wndClass & IIf(Len(wndCaption), _
" - """ & wndCaption & """", "")
End Sub

Realizzare effetti speciali con i cursori animati


Private Declare Function LoadCursorFromFile Lib "user32" Alias _
"LoadCursorFromFileA" (ByVal lpFileName As String) As Long
Private Declare Function GetCursor Lib "user32" () As Long
Private Declare Function CopyIcon Lib "user32" (ByVal hcur As Long) As Long
Private Declare Function SetSystemCursor Lib "user32" (ByVal hcur As Long, _
ByVal id As Long) As Long
Private Const OCR_NORMAL = 32512

Sub ALengthyTask()
Dim hNewCursor As Long
Dim hSavCursor As Long

' crea una copia del cursore corrente – questo è necessario


' affinchè il codice funzioni correttamente sotto NT
hSavCursor = CopyIcon(GetCursor())

' carica un nuovo cursore


hNewCursor = LoadCursorFromFile("h:\winnt4\cursors\appstart.ani")
' lo rende il cursore corrente
SetSystemCursor hNewCursor, OCR_NORMAL

' inserire qui il codice che richiede una lunga esecuzione


' ...
' (in questo esempio verrà utilizzata una casella di messaggio)
MsgBox "Press OK to continue"

' ripristina il vecchio cursore


SetSystemCursor hSavCursor, OCR_NORMAL
End Sub

Determinare se un programma è a 16 o a 32 bit


Type SHFILEINFO
hIcon As Long
iIcon As Long
dwAttributes As Long
szDisplayName As String * MAX_PATH
szTypeName As String * 80
End Type
Const WIN32_GUI = &H4550 ' PE, Win32
Const WIN16_GUI = &H454E ' NE, Win16
Const WIN16_DOS = &H5A4D ' MZ, MS-DOS
Const SHGFI_EXETYPE = &H2000

Public Declare Function SHGetFileInfo Lib "shell32.dll" Alias "SHGetFileInfoA" _


( ByVal pszPath As String, ByVal dwFileAttributes As Long, _
psfi As SHFILEINFO, ByVal cbFileInfo As Long, ByVal uFlags As Long ) As _
Long

dw = SHGetFileInfo(exename, 0, sfi, Len(sfi), SHGFI_EXETYPE)


lowWord = dw And &H7FFF

Disabilitare la combinazione di tasti Ctrl-Alt-Del in Windows


9x
Private Const SPI_SETSCREENSAVERRUNNING = 97
Private Declare Function SystemParametersInfo Lib "user32" Alias _
"SystemParametersInfoA" (ByVal uAction As Long, ByVal uParam As Long, _
ByRef lpvParam As Any, ByVal fuWinIni As Long) As Long

' disabilita la combinazione di tasti Ctrl-Alt-Del


SystemParametersInfo SPI_SETSCREENSAVERRUNNING, True, ByVal 0&, 0
' ....
' la riabilita
SystemParametersInfo SPI_SETSCREENSAVERRUNNING, False, ByVal 0&, 0

Determinare la modalità di avvio di Windows


Private Declare Function GetSystemMetrics Lib "user32" (ByVal nIndex As Long) _
As Long
Public Const SM_CLEANBOOT = 67

Private Sub Command1_Click()


Select Case GetSystemMetrics(SM_CLEANBOOT)
Case 0: MsgBox "Normal boot"
Case 1: MsgBox "Fail-safe boot"
Case 2: MsgBox "Fail-safe with network boot"
End Select
End Sub

Determinare la versione di Windows richiesta da un


determinato programma
Type SHFILEINFO
hIcon As Long
iIcon As Long
dwAttributes As Long
szDisplayName As String * MAX_PATH
szTypeName As String * 80
End Type

Public Const SHGFI_EXETYPE = &H2000

Public Declare Function SHGetFileInfo Lib "shell32.dll" Alias "SHGetFileInfoA" _


( ByVal pszPath As String, ByVal dwFileAttributes As Long, _
psfi As SHFILEINFO, ByVal cbFileInfo As Long, ByVal uFlags As Long ) As _
Long

dw = SHGetFileInfo(exename, 0, sfi, Len(sfi), SHGFI_EXETYPE)


hiWord = dw \ &H10000

Ottenere il codice d'uscita di un processo


Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As _
Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As _
Long, lpExitCode As Long) As Long
Const STILL_ACTIVE = &H103
Const PROCESS_QUERY_INFORMATION = &H400

Private Sub cmdRunNotepad_Click()


Dim hTask As Long
Dim hProcess As Long
Dim exitCode As Long

hTask = Shell("Notepad", vbNormalFocus)


hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, hTask)

' cicla fino a che il processo restituisce un codice d'uscita valido


Do
' rinuncia a questo intervallo di tempo della CPU
Sleep 100
DoEvents
' richiede il codice d'uscita
GetExitCodeProcess hProcess, exitCode
Loop While exitCode = STILL_ACTIVE

MsgBox "Exit code = " & exitCode, vbInformation

End Sub

Stabilire quando l'applicazione ottiene o perde il focus di


input
' Richiede un riferimento alla libreria MSGHOOKX.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip

' è possibile omettere la costante che segue, in quanto è


' definita nella type library MsgHookX
Const WM_ACTIVATEAPP = &H1C

Dim WithEvents FormHook As MsgHook

Private Sub Form_Load()


' esegue il subclassing degli eventi della form
Set FormHook = New MsgHook
FormHook.StartSubclass hWnd
End Sub

Private Sub FormHook_AfterMessage(ByVal uMsg As Long, ByVal wParam As Long, _


ByVal lParam As Long, retValue As Long)
If uMsg = WM_ACTIVATEAPP Then
If wParam Then
' l'applicazione è stata attivata
Else
' l'applicazione è stata disattivata
End If
End If
End Sub

Nascondere o disabilitare le icone del desktop


Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal _
lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, _
ByVal wCmd As Long) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, _
ByVal nCmdShow As Long) As Long
Private Declare Function EnableWindow Lib "user32" (ByVal hwnd As Long, _
ByVal fEnable As Long) As Long

Private Const GW_CHILD = 5


Private Const SW_HIDE = 0
Private Const SW_SHOW = 5

Sub ShowDesktopIcons(ByVal bVisible As Boolean)


Dim hWnd_DesktopIcons As Long
hWnd_DesktopIcons = GetWindow( FindWindow("Progman", "Program Manager"), _
GW_CHILD)
If bVisible Then
ShowWindow hWnd_DesktopIcons, SW_SHOW
Else
ShowWindow hWnd_DesktopIcons, SW_HIDE
End If
End Sub

Sub EnableDesktop(ByVal bEnable As Boolean)


Dim hWnd_Desktop As Long
hWnd_Desktop = GetWindow( FindWindow("Progman", "Program Manager"), _
GW_CHILD)
If bEnable Then
EnableWindow hWnd_Desktop, True
Else
EnableWindow hWnd_Desktop, False
End If
End Sub

' versione più compatta

Sub ShowDesktopIcons(ByVal bVisible As Boolean)


ShowWindow GetWindow( FindWindow("Progman", "Program Manager"), GW_CHILD), _
IIf(bVisible, SW_SHOW, SW_HIDE)
End Sub

Sub EnableDesktop(ByVal bEnable As Boolean)


EnableWindow GetWindow( FindWindow("Progman", "Program Manager"), GW_CHILD), _
bEnable
End Sub

Nascondere o disabilitare la barra delle applicazioni di


Windows
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal _
lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function ShowWindow Lib "user32" (ByVal hwnd As Long, _
ByVal nCmdShow As Long) As Long
Private Declare Function EnableWindow Lib "user32" (ByVal hwnd As Long, _
ByVal fEnable As Long) As Long
Private Const SW_HIDE = 0
Private Const SW_SHOW = 5

Sub ShowStartBar(ByVal bVisible As Boolean)


Dim hWnd_StartBar As Long
hWnd_StartBar = FindWindow("Shell_TrayWnd", "")
If bVisible Then
ShowWindow hWnd_StartBar, SW_SHOW
Else
ShowWindow hWnd_StartBar, SW_HIDE
End If
End Sub

Sub EnableStartBar(ByVal bEnable As Boolean)


Dim hWnd_StartBar As Long
hWnd_StartBar = FindWindow("Shell_TrayWnd", "")
If bEnable Then
EnableWindow hWnd_StartBar, True
Else
EnableWindow hWnd_StartBar, False
End If
End Sub
' versione più compatta

Sub ShowStartBar(ByVal bVisible As Boolean)


ShowWindow FindWindow("Shell_TrayWnd", ""), IIf(bVisible, SW_SHOW, SW_HIDE)
End Sub

Sub EnableStartBar(ByVal bEnable As Boolean)


EnableWindow FindWindow("Shell_TrayWnd", ""), bEnable
End Sub

Aprire il Pannello di controllo o una procedura guidata


Public Enum mbDialogType
mbNewHardware
mbAddRemove
mbDateTimeProp
mbDisplayProp
mbInternetProp
mbGameProp
mbKeyboardProp
mbModemProp
mbMouseProp
mbMultimediaProp
mbNetworkProp
mbPasswordProp
mbInternationalProp
mbSoundProp
mbSystemProp
End Enum

Sub OpenWindowsDialog(ByVal mbDialog As mbDialogType)


Dim s As String
Select Case mbDialog
Case mbNewHardware: s = "sysdm.cpl @1"
Case mbAddRemove: s = "appwiz.cpl,,1"
Case mbDateTimeProp: s = "timedate.cpl"
Case mbDisplayProp: s = "desk.cpl,,0"
Case mbInternetProp: s = "inetcpl.cpl,,0"
Case mbGameProp: s = "joy.cpl"
Case mbKeyboardProp: s = "main.cpl @1"
Case mbModemProp: s = "modem.cpl"
Case mbMouseProp: s = "main.cpl @0"
Case mbMultimediaProp: s = "mmsys.cpl,,0"
Case mbNetworkProp: s = "netcpl.cpl"
Case mbPasswordProp: s = "password.cpl"
Case mbInternationalProp: s = "intl.cpl,,0"
Case mbSoundProp: s = "mmsys.cpl @1"
Case mbSystemProp: s = "sysdm.cpl,,0"
End Select
Shell "rundll32.exe shell32.dll,Control_RunDLL " & s, 5
End Sub

Aprire il prompt di MS-DOS da qualsiasi directory all'interno


di Windows Explorer
REGEDIT4

[HKEY_CLASSES_ROOT\Directory\Shell\DosPrompt]
@="Run MS-DOS Prompt here"
[HKEY_CLASSES_ROOT\Directory\Shell\DosPrompt\Command]
@="Cmd /k CD \"%L\" "

Attivare il menu Start di Windows via codice


Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, _
ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Const KEYEVENTF_KEYUP = &H2

' simula la pressione dei tasti Ctrl-Esc


keybd_event vbKeyControl, 0, 0, 0
keybd_event vbKeyEscape, 0, 0, 0
DoEvents

' simula il rilascio dei due tasti


keybd_event vbKeyControl, 0, KEYEVENTF_KEYUP, 0
keybd_event vbKeyEscape, 0, KEYEVENTF_KEYUP, 0
DoEvents

Restituire un codice d'errore Dos in uscita


REM a Ms-Dos batch file
CD c:\MyApp
Text.Exe
If Errorlevel 2 Goto ExitCodeIsTwo
If Errorlevel 1 Goto ExitCodeIsOne

REM process here a null exit code


REM ...
GOTO EndBatch

:ExitCodeTwo
REM process here exit code equals to two
REM ...
GOTO EndBatch

:ExitCodeOne
REM process here exit code equals to one
REM ...

:EndBatch
ECHO goodbye.

Effettuare una chiamata telefonica utilizzando TAPI


Private Declare Function tapiRequestMakeCall Lib "TAPI32.DLL" (ByVal Dest As _
String, ByVal AppName As String, ByVal CalledParty As String, _
ByVal Comment As String) As Long

' effettua una chiamata telefonica utilizzando TAPI


' restituisce True in caso di successo

Function PhoneCall(ByVal PhoneNumber As String, ByVal DestName As String, _


Optional ByVal Comment As String) As Boolean
If tapiRequestMakeCall(Trim$(PhoneNumber), App.Title, Trim$(DestName), _
Comment) = 0 Then
PhoneCall = True
End If
End Function

Creare un'icona per la System Tray di Windows


Type NOTIFYICONDATA
cbSize As Long ' dimensioni della struttura
hwnd As Long ' handle della finestra padre
uId As Long ' utilizzato solo con più icone associate
' alla medesima applicazione
uFlags As Long ' Flag
uCallBackMessage As Long ' gestore delle notifiche
hIcon As Long ' handle dell'icona
szTip As String * 64 ' testo del tooltip
End Type

Declare Function Shell_NotifyIcon Lib "shell32" Alias "Shell_NotifyIconA" _


(ByVal dwMessage As Long, pnid As NOTIFYICONDATA) As Boolean

Const NIM_ADD = &H0 ' Aggiunge un'icona


Const NIM_MODIFY = &H1 ' Modifica un'icona
Const NIM_DELETE = &H2 ' Elimina un'icona

Const NIF_MESSAGE = &H1 ' Per modificare il membro uCallBackMessage


Const NIF_ICON = &H2 ' Per modificare l'icona
Const NIF_TIP = &H4 ' Per modificare il testo del tooltip

Const WM_MOUSEMOVE = &H200


Const WM_LBUTTONDOWN = &H201 ' Clic con il tasto sinistro
Const WM_LBUTTONDBLCLK = &H203 ' Doppio clic con il tasto sinistro
Const WM_RBUTTONDOWN = &H204 ' Clic con in tasto destro
Const WM_RBUTTONDBLCLK = &H206 ' Doppio clic con il tasto destro

Dim nid As NOTIFYICONDATA

Private Sub Form_Load()


nid.cbSize = Len(nid)
nid.hwnd = Form1.hwnd
nid.uId = 0
nid.uFlags = NIF_ICON Or NIF_TIP Or NIF_MESSAGE
nid.uCallBackMessage = WM_MOUSEMOVE
nid.hIcon = Form1.Icon
' Chr$(0) è necessario alla fine della stringa
nid.szTip = "Hello World" + vbNullChar
Shell_NotifyIcon NIM_ADD, nid
End Sub

Private Sub Form_MouseMove(Button As Integer, Shift As Integer, X As Single, _


Y As Single)
Dim Msg As Long
Msg = ScaleX(X, ScaleMode, vbPixels)
Select Case Msg
Case WM_LBUTTONDOWN
MsgBox "Left click!"
Case WM_LBUTTONDBLCLK
MsgBox "Left double-click!"
Case WM_RBUTTONDOWN
MsgBox "Right click!"
Case WM_RBUTTONDBLCLK
MsgBox "Right double-click!"
End Select
End Sub

Private Sub cmdChangeTooltip()


nid.szTip = "A new tooltip text" & vbNullChar
Shell_NotifyIcon NIM_MODIFY, nid
End Sub

Private Sub Form_Unload(Cancel As Integer)


Shell_NotifyIcon NIM_DELETE, nid
End Sub

Tracciare le modifiche alla data/ora del sistema ed alla


risoluzione dello schermo
' Richiede un riferimento alla libreria MSGHOOK.DLL,che può essere scaricata dal seguente URL:
' http://www.vb2themax.com/Downloads/Files/MsgHook.zip

' è possibile omettere le seguenti definizioni di costanti,


' in quanto risultano definite nella type library MsgHookX
Const WM_TIMECHANGE = &H1E
Const WM_DISPLAYCHANGE = &H7E
Const WM_COMPACTING = &H41

Dim WithEvents FormHook As MsgHook

Private Sub Form_Load()


' inizia il subclassing degli eventi della form
Set FormHook = New MsgHook
FormHook.StartSubclass hWnd
End Sub

Private Sub FormHook_AfterMessage(ByVal uMsg As Long, ByVal wParam As Long, _


ByVal lParam As Long, retValue As Long)
Select Case uMsg
Case WM_DISPLAYCHANGE
' la risoluzione dello schermo è stato modificata.
' ...
' ... (aggiungere in questo punto la procedura per ridimensionare le form) ...
' ...
Case WM_TIMECHANGE
' la data e l'ora del sistema sono state modificate.
' ...
Case WM_COMPACTING
' Windows è a corto di memoria (rilascia le risorse se possibile).
' ...
' ... (rilascia il maggior numero di risorse possibile) ...
' ...
End Select
End Sub

Utilizzare valori Currency invece di LARGE_INTEGER nelle


chiamate API
Private Declare Function GetDiskFreeSpaceEx2 Lib "kernel32" Alias _
"GetDiskFreeSpaceExA" (ByVal lpDirectoryName As String, _
lpFreeBytesAvailableToCaller As Currency, lpTotalNumberOfBytes As Currency, _
lpTotalNumberOfFreeBytes As Currency) As Long

Dim lpFreeBytesAvailableToCaller As Currency


Dim lpTotalNumberOfBytes As Currency
Dim lpTotalNumberOfFreeBytes As Currency

GetDiskFreeSpaceEx2 "C:\", lpFreeBytesAvailableToCaller, lpTotalNumberOfBytes, _


LpTotalNumberOfFreeBytes
' eliminare le quattro cifre decimali dai risultati
Debug.Print "Free Bytes Available to Caller = " & lpFreeBytesAvailableToCaller * 10000#
Debug.Print "Total Number of Bytes = " & lpTotalNumberOfBytes * 10000#
Debug.Print "Total Number of Free Bytes = " & lpTotalNumberOfFreeBytes * 10000#
Capitolo 16 - Applicazioni Internet
Creare uno shortcut per Internet
' Crea uno shortcut per Internet
'
' quando selezionato con un doppio clic, questo shortcut
' caricherà il browser predefinito e farà in modo che questo punti
' alla pagina relativa all'URL specificato

Sub CreateInternetShortcut(ByVal FileName As String, ByVal URL As String)


Dim fnum As Integer

' uno shortcut per Internet è semplicemente un file di testo di due righe
fnum = FreeFile
Open FileName For Output As #fnum
Print #fnum, "[InternetShortcut]"
Print #fnum, "URL=" & URL
Close #fnum
End Sub

Verificare la validità di un URL


Private Declare Function IsValidURL Lib "urlmon" (ByVal pBC As Long, _
url As Byte, ByVal dwReserved As Long) As Long

Dim url As String


Dim b() As Byte

url = "http://www.vb2themax.com/default.asp"
' Questo è necessario in quanto viene passata una stringa Unicode
b = url & vbNullChar

If IsValidURL(0, b(0), 0) = 0 Then


' URL valido
Else
' URL non valido
End If

Verificare che il RAS sia installato


Private Declare Function InternetGetConnectedState Lib "wininet.dll" (ByRef _
lpdwFalgs As Long, ByVal dwReserved As Long) As Boolean
Const INTERNET_RAS_INSTALLED = 16

' controlla se il RAS è stato installato

Function IsRASInstalled() As Boolean


Dim connStatus As Long
InternetGetConnectedState connStatus, 0&
IsRASInstalled = (connStatus And INTERNET_RAS_INSTALLED)
End Function

Verificare se esiste una connessione ad Internet


Private Declare Function InternetGetConnectedState Lib "wininet.dll" (ByRef _
lpSFlags As Long, ByVal dwReserved As Long) As Long
Const INTERNET_CONNECTION_MODEM = 1
Const INTERNET_CONNECTION_LAN = 2
Const INTERNET_CONNECTION_PROXY = 4
Const INTERNET_CONNECTION_MODEM_BUSY = 8

Dim flags As Long


If InternetGetConnectedState(flags, 0) = 0 Then
' nessuna connessione attiva
ElseIf flags = INTERNET_CONNECTION_MODEM Then
' connessione attiva via modem
ElseIf flags = INTERNET_CONNECTION_LAN Then
' connessione attiva via LAN
ElseIf flags = INTERNET_CONNECTION_PROXY Then
' connessione attiva via proxy
End If

Verificare se l'utente lavora off-line


Const INTERNET_OPTION_CONNECTED_STATE = 50
Const INTERNET_STATE_DISCONNECTED_BY_USER = &H10

Private Type INTERNET_CONNECTED_INFO


dwConnectedState As Long
dwFlags As Long
End Type
Private Declare Function InternetQueryOption Lib "wininet.dll" Alias _
"InternetQueryOptionA" (ByVal hInternet As Long, ByVal dwOption As Long, _
lpBuffer As INTERNET_CONNECTED_INFO, lpdwBufferLength As Long) As Boolean

' Restituisce True se l'utente lavora off-line

Function IsWorkingOffLine() As Boolean


Dim dwState As Long, dwSize As Long
Dim ci As INTERNET_CONNECTED_INFO

dwState = 0
dwSize = LenB(dwState)

If InternetQueryOption(0&, INTERNET_OPTION_CONNECTED_STATE, ci, dwSize) Then


IsWorkingOffLine = (ci.dwConnectedState And _
INTERNET_STATE_DISCONNECTED_BY_USER)
End If
End Function

Eseguire il download di un file


Private Declare Function URLDownloadToFile Lib "urlmon" Alias _
"URLDownloadToFileA" (ByVal pCaller As Long, ByVal szURL As String, _
ByVal szFileName As String, ByVal dwReserved As Long, _
ByVal lpfnCB As Long) As Long

' Questo file è di 2MB, pertanto si potrebbe voler tentare con qualcosa di più semplice
Dim errcode As Long
Dim url As String
Dim localFileName As String

url = "http://www.vb2themax.com/vbmaximizer/files/vbm_demo.zip"
localFileName = "c:\vbm_demo.zip"

errcode = URLDownloadToFile(0, url, localFileName, 0, 0)


If errcode = 0 Then
MsgBox "Download ok"
Else
MsgBox "Error while downloading"
End If

Lanciare il browser predefinito caricando un determinato URL


Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, _
ByVal lpParameters As String, ByVal lpDirectory As String, _
ByVal nShowCmd As Long) As Long

' Apre il browser predefinito caricando un determinato URL


' Restituisce True in caso di successo, altrimenti False

Public Function OpenBrowser(ByVal URL As String) As Boolean


Dim res As Long

' l'URL deve obbligatoriamente contenere il prefisso http:// o https://


If InStr(1, URL, "http", vbTextCompare) <> 1 Then
URL = "http://" & URL
End If

res = ShellExecute(0&, "open", URL, vbNullString, vbNullString, _


vbNormalFocus)
OpenBrowser = (res > 32)
End Function

Aprire il programma predefinito per inviare messaggi di posta


elettronica
Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, _
ByVal lpParameters As String, ByVal lpDirectory As String, _
ByVal nShowCmd As Long) As Long

' Apre il programma predefinito per l'invio dei messaggi di posta elettronica
' Restituisce True in caso di successo, altrimenti False

Public Function OpenEmailProgram(ByVal EmailAddress As String) As Boolean


Dim res As Long
res = ShellExecute(0&, "open", "mailto:" & EmailAddress, vbNullString, _
vbNullString, vbNormalFocus)
OpenEmailProgram = (res > 32)
End Function

Impostare la modalità off-line


Const INTERNET_OPTION_CONNECTED_STATE = 50
Const INTERNET_STATE_CONNECTED = 1
Const INTERNET_STATE_DISCONNECTED = 2
Const INTERNET_STATE_DISCONNECTED_BY_USER = &H10
Const INTERNET_STATE_IDLE = &H100
Const INTERNET_STATE_BUSY = &H200
Const ISO_FORCE_DISCONNECTED = 1

Private Declare Function InternetSetOption Lib "wininet.dll" Alias _


"InternetSetOptionA" (ByVal hInternet As Long, ByVal dwOption As Long, _
lpBuffer As INTERNET_CONNECTED_INFO, ByVal dwBufferLength As Long) As _
Boolean

' Applica le modalità OffLine o OnLine

Sub SetOffLineMode(ByVal offLineMode As Boolean)


Dim ci As INTERNET_CONNECTED_INFO
Dim retValue As Boolean

If offLineMode Then
ci.dwConnectedState = INTERNET_STATE_DISCONNECTED_BY_USER
ci.dwFlags = ISO_FORCE_DISCONNECTED
Else
ci.dwConnectedState = INTERNET_STATE_CONNECTED
End If

retValue = InternetSetOption(0&, INTERNET_OPTION_CONNECTED_STATE, ci, _


LenB(ci))
If retValue = False Then Err.Raise vbObjectError + 1000, , _
"An error occurred calling SetOffLineMode function"
End Sub

Aggiungere un URL ai favoriti di Internet Explorer


Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, _
ByVal lpParameters As String, ByVal lpDirectory As String, _
ByVal nShowCmd As Long) As Long

Sub AddToIEFavorites(ByVal sURL As String, ByVal sDescr As String)


sURL = LCase(sURL)
' aggiungere http:// se mancante
If Left$(sURL, 7) <> "http://" Then sURL = "http://" & sURL
' apri IE ed esegui lo script
ShellExecute 0, "Open", "javascript:window.external.AddFavorite('" & sURL & _
"','" & sDescr & "')", "", "", 0
End Sub

Convertire una stringa in HTML


Function HTMLEncode(ByVal Text As String) As String
Dim i As Integer
Dim acode As Integer
Dim repl As String

HTMLEncode = Text

For i = Len(HTMLEncode) To 1 Step -1


acode = Asc(Mid$(HTMLEncode, i, 1))
Select Case acode
Case 32
repl = "&nbsp;"
Case 34
repl = "&quot;"
Case 38
repl = "&amp;"
Case 60
repl = "&lt;"
Case 62
repl = "&gt;"
Case 32 To 127
' non modificare i caratteri alfanumerici
Case Else
repl = "&#" & CStr(acode) & ";"
End Select
If Len(repl) Then
HTMLEncode = Left$(HTMLEncode, i - 1) & repl & Mid$(HTMLEncode, _
i + 1)
repl = ""
End If
Next
End Function

Function HTMLDecode(ByVal html As String) As String


Dim i As Long
HTMLDecode = html
Do
' ricerca il carattere & successivo, esci se non ce ne sono
i = InStr(i + 1, HTMLDecode, "&")
If i = 0 Then Exit Do
' controlla se si tratta di un carattere speciale
If StrComp(Mid$(HTMLDecode, i, 6), "&nbsp;", vbTextCompare) = 0 Then
HTMLDecode = Left$(HTMLDecode, i - 1) & " " & Mid$(HTMLDecode, _
i + 6)
ElseIf StrComp(Mid$(HTMLDecode, i, 6), "&quot;", vbTextCompare) = 0 Then
HTMLDecode = Left$(HTMLDecode, i - 1) & """" & Mid$(HTMLDecode, _
i + 6)
ElseIf StrComp(Mid$(HTMLDecode, i, 5), "&amp;", vbTextCompare) = 0 Then
HTMLDecode = Left$(HTMLDecode, i - 1) & "&" & Mid$(HTMLDecode, _
i + 5)
ElseIf StrComp(Mid$(HTMLDecode, i, 4), "&lt;", vbTextCompare) = 0 Then
HTMLDecode = Left$(HTMLDecode, i - 1) & "<" & Mid$(HTMLDecode, _
i + 4)
ElseIf StrComp(Mid$(HTMLDecode, i, 4), "&gt;", vbTextCompare) = 0 Then
HTMLDecode = Left$(HTMLDecode, i - 1) & ">" & Mid$(HTMLDecode, _
i + 4)
End If
Loop
End Function
Capitolo 17 - Ambiente di sviluppo e Setup
(nessun esempio di codice)