Visual Basic Simple
Utilizzare il registro di Windows
(seconda parte)
Sincronizza Indice
Sincronizza Indice
Scarica il progetto
Scarica il progetto
Scarica il testo dell'articolo
Testo dell'articolo
Stampa l'articolo
Stampa l'articolo
Ricerca personalizzata

Difficoltà: 5 / 5

<< Continua dalla parte 1

Dopo l'introduttiva presentazione delle dichiarazioni della classe passiamo direttamente alla fase implementativa; vedremo in ordine le proprietà pubbliche della classe, i suoi metodi pubblici ed infine quelli privati. L'ordine seguito è quello esclusivamente alfabetico per i tre gruppi; pertanto a volte saranno utilizzate funzioni descritte soltanto in seguito.

  1. Public Property Get Chiave() As Long
  2.   Chiave = lngKeyValue
  3. End Property
  4. Public Property Let Chiave(ByVal newChiave As Long)
  5.   Call RegOpenKeyEx(newChiave, vbNullString, ByVal 0&, lngKeySecurity, newChiave)
  6.   Call RegCloseKey(lngKeyValue)
  7.   lngKeyValue = newChiave
  8. End Property

La proprietà Chiave di lettura e scrittura restituisce ed imposta l'handle della chiave utilizzata dalla classe assegnando il contenuto del membro interno. L'operazione di scrittura della proprietà è però leggermente più complessa: prima di assegnare il nuovo valore alla variabile lngKeyValue è necessario riaprire la nuova chiave (riga 77) e chiudere quella precedente (riga 78).

Questo perché Windows tiene conto del numero di volte che la chiave è stata aperta. Se due istanze aprono la stessa chiave, ed una delle due istanze ne richiede la chiusura, l'handle dell'altra istanza non verrà invalidato ed entrambe le chiavi chiuse.
È pertanto fondamentale chiudere soltanto la chiave che l'istanza ha aperto e riaprire la nuova chiave ogni volta che un'istanza ne usufruisce. Questo garantisce che la chiave non venga chiusa da un'altra istanza o da un altro processo.

  1. Public Property Get NumeroSottoChiavi() As Long
  2.   Dim strClass As String
  3.   strClass = Space$(1024)
  4.   Call RegQueryInfoKey(lngKeyValue, strClass, Len(strClass), ByVal 0&, NumeroSottoChiavi, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&)
  5. End Property
  6. Public Property Get NumeroValori() As Long
  7.   Dim strClass As String
  8.   strClass = Space$(1024)
  9.   Call RegQueryInfoKey(lngKeyValue, strClass, Len(strClass), ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&, NumeroValori, ByVal 0&, ByVal 0&, ByVal 0&, ByVal 0&)
  10. End Property

Seguono due proprietà in sola lettura che restituiscono rispettivamente il numero di sottochiavi ed il numero di valori della chiave aperta. Entrambe le proprietà utilizzano la funzione API RegQueryInfoKey. In ambienti Windows NT/2000/XP è necessario che venga fornito un buffer per la classe, indicato da strClass.

  1. Public Property Get Security() As ChiaviSecurity
  2.   Security = lngKeySecurity
  3. End Property
  4. Public Property Let Security(ByVal newSecurity As ChiaviSecurity)
  5.   lngKeySecurity = newSecurity
  6. End Property

La proprietà Security consente di recuperare ed impostare un valore relativo ai permessi concessi. Trascurabile su Windows 95/98/ME, necessaria invece su Windows NT/2000/XP.

  1. Public Property Get Valore(Optional ByVal NomeValore As String, Optional ByRef TipoDati As TipoValoriRegistro) As Variant
  2.   Dim AmpiezzaBuffer As Long
  3.   Dim Buffer() As Byte
  4.   TipoDati = REG_NONE
  5.   Call RegQueryValueEx(lngKeyValue, NomeValore, ByVal 0&, TipoDati, ByVal 0&, AmpiezzaBuffer)
  6.   If AmpiezzaBuffer > 0 Then
  7.     ReDim Buffer(AmpiezzaBuffer - 1)
  8.     Call RegQueryValueEx(lngKeyValue, NomeValore, ByVal 0&, ByVal 0&, Buffer(0), AmpiezzaBuffer)

La proprietà Valore si presenta un pochino più complessa delle precedenti. L'operazione di lettura recupera il contenuto dei valori: quello predefinito della chiave se non viene specificato il nome del valore, o quello indicato se ne viene specificato il nome.

Sarà anche possibile recuperare con la stessa proprietà il tipo di dato passando una variabile come secondo argomento. Al ritorno della funzione tale variabile conterrà il tipo di dati del valore richiesto.

Alla riga 106 sono richiesti il tipo di dato del valore e l'ampiezza del contenuto del valore mediante la funzione RegQueryValueEx. Se il dato può essere recuperato, la variabile AmpiezzaBuffer conterrà la dimensione in bytes del buffer da allocare.

Tale buffer sarà effettivamente allocato alla riga 108 ed i dati potranno quindi essere recuperati richiamando nuovamente la funzione RegQueryValueEx e fornendo il puntatore al buffer precedentemente allocato.

  1.     Select Case TipoDati
  2.       Case REG_SZ
  3.         Valore = Left$(StrConv(Buffer, vbUnicode), AmpiezzaBuffer - 1)
  4.       Case REG_NONE, REG_BINARY, REG_EXPAND_SZ, REG_MULTI_SZ
  5.         Valore = Buffer
  6.       Case REG_DWORD, REG_DWORD_LITTLE_ENDIAN
  7.         If UBound(Buffer) = 3 Then
  8.           Valore = CDbl(Buffer(0) + Buffer(1) * 256& + Buffer(2) * 65536) + Buffer(3) * 16777216#
  9.         Else
  10.           Valore = Buffer
  11.         End If
  12.       Case REG_DWORD_BIG_ENDIAN, REG_LINK, REG_RESOURCE_LIST
  13.         MsgBox "Tipo di dati non implementato [Valore Get]!", vbCritical + vbOKOnly, "FBIRegistry"
  14.     End Select
  15.   End If
  16.   If AmpiezzaBuffer = 0 Then Valore = Null
  17.   Erase Buffer
  18. End Property

Avendo recuperato il contenuto del valore ed il suo tipo di dati, sarà quindi possibile effettuare la conversione nel formato più convenzionale: stringa per il tipo REG_SZ, numero intero double per il tipo REG_DWORD o matrice di byte per gli altri tipi di dato. Per quanto riguarda la conversione in stringa verrà effettuata la conversione da formato ANSI ad Unicode (riga 112). La conversione in numero double effettuerà un controllo in più alla riga 116: sebbene il processo di conversione è semplice (riga 117), esistono all'interno del registro alcuni valore memorizzati in maniera errata, ovvero con un numero di bytes minore o maggiore dei normali quattro che compongono un valore DWORD. Per tali casi sarà semplicemente restituita la matrice di dati senza effettuare la conversione (riga 119).

Se il valore richiesto non esiste all'interno del registro oppure non è impostato alcun valore per esso, la dimensione del buffer sarà uguale a 0 e sarà pertanto restituito un valore Null (riga 125).

  1. Public Property Let Valore(ByVal NomeValore As String, Optional ByRef TipoDati As TipoValoriRegistro = REG_SZ, ByVal newValore As Variant)
  2.   Dim AmpiezzaBuffer As Long
  3.   Dim Buffer() As Byte
  4.   If (TipoDati < REG_NONE) Or (TipoDati > REG_RESOURCE_LIST) Then TipoDati = REG_SZ
  5.   Select Case TipoDati
  6.     Case REG_SZ
  7.       Buffer = StrConv(newValore, vbFromUnicode) & vbNullChar

L'ultima proprietà è l'operazione di scrittura sul valore del registro. Come la precedente consente opzionalmente la specifica di un nome di valore e di un tipo di dati che se non specificato assume il significato di stringa (REG_SZ). Alla riga 132 viene effettuato un controllo d'obbligo sul tipo di dato specificato.

Se il dato da scrivere è di tipo stringa esso sarà semplicemente convertito da Unicode ad ANSI e trasferito nella variabile binaria Buffer.

  1.     Case REG_BINARY, REG_NONE, REG_EXPAND_SZ, REG_MULTI_SZ
  2.       If (VarType(newValore) And vbArray) = vbArray Then
  3.         ReDim Buffer(0) As Byte
  4.         If Not IsNull(newValore) Then ReDim Buffer(UBound(newValore) + 1) As Byte
  5.         Buffer(UBound(Buffer)) = 0
  6.         Select Case (VarType(newValore) Xor vbArray)
  7.           Case vbByte
  8.             Buffer = newValore
  9.             If Not IsNull(newValore) Then ReDim Preserve Buffer(UBound(newValore) + 1) As Byte
  10.             Buffer(UBound(Buffer)) = 0
  11.           Case vbInteger, vbLong, vbDouble, vbVariant
  12.             For AmpiezzaBuffer = LBound(newValore) To UBound(newValore)
  13.               Buffer(AmpiezzaBuffer) = (newValore(AmpiezzaBuffer) And 255)
  14.             Next AmpiezzaBuffer
  15.           Case Else
  16.             MsgBox "Tipo di dati non supportato [Valore Let]!", vbCritical + vbOKOnly, "FBIRegistry"
  17.             Exit Property
  18.         End Select

La situazione si complica nel caso dei tipi di dati binari specificato alla riga 136. Essi dovranno essere convertiti in maniera idonea in una matrice. L'operazione sarebbe uno scherzo se il tipo di dati non fosse Variant e come tale Visual Basic si rifiuterà di trattarlo o trasformarlo direttamente in matrice di byte, a meno che il suo sottotipo non sia Byte.

Sarà quindi verificato alla riga 137 che il nuovo valore passato sia una matrice: in tal caso verrà allocata una nuova matrice di bytes di nome Buffer della dimensione dell'altra matrice ed il contenuto verrà quindi ricopiato nella matrice Buffer. Sarà generato un errore se il sottotipo dei dati non è numerico (riga 151).

  1.       ElseIf Not IsNull(newValore) Then
  2.         MsgBox "I dati binari devono essere in una matrice!", vbCritical + vbOKOnly, "FBIRegistry"
  3.         Exit Property
  4.       Else
  5.         ReDim Buffer(0) As Byte
  6.       End If
  7.     Case REG_DWORD, REG_DWORD_LITTLE_ENDIAN
  8.       If IsNull(newValore) = False Then
  9.         If IsArray(newValore) = False Then
  10.           ReDim Buffer(4) As Byte
  11.           Buffer(0) = (newValore And 255)
  12.           Buffer(1) = (newValore \ 256) And 255
  13.           Buffer(2) = (newValore And &H7FFFFF00) \ 65536 And 255
  14.           Buffer(3) = ((newValore And &HFF000000) \ 16777216) And 255
  15.         Else
  16.           ReDim Buffer(UBound(newValore) + 1) As Byte
  17.           For AmpiezzaBuffer = LBound(newValore) To UBound(newValore)
  18.             Buffer(AmpiezzaBuffer) = (newValore(AmpiezzaBuffer) And 255)
  19.           Next AmpiezzaBuffer
  20.         End If
  21.       Else
  22.         ReDim Buffer(0) As Byte
  23.       End If

Sarà generato un avviso anche nel caso in cui i dati passati non siano in una matrice ed il tipo richiesto sia comunque di dati binari (riga 155). Nell'ultimo caso che il valore passato sia un valore Nullo sarà generata una matrice vuota (riga 158).

Se invece il tipo di dati richiesto è il REG_DWORD sarà verificato che i dati non siano una matrice. Nel caso che non lo fossero dovrà quindi essere calcolato il nuovo valore ed assegnarlo ad una matrice di 4 bytes (righe 163-167). Se i dati sono invece in una matrice tali dati saranno ricopiati nel nuovo buffer (righe 169-172).

  1.     Case REG_DWORD_BIG_ENDIAN, REG_LINK, REG_RESOURCE_LIST
  2.       MsgBox "Tipo di dati non implementato [Valore Let]!", vbCritical + vbOKOnly, "FBIRegistry"
  3.       Exit Property
  4.   End Select
  5.   If IsNull(newValore) And (TipoDati = REG_SZ) Then
  6.     Call RegDeleteValue(lngKeyValue, NomeValore)
  7.   Else
  8.     Call RegSetValueEx(lngKeyValue, NomeValore, ByVal 0&, TipoDati, Buffer(0), UBound(Buffer))
  9.   End If
  10.   Erase Buffer
  11. End Property

Tutti gli altri tipi di dati non sono supportati (righe 177-179).
Prima di procedere con la scrittura dei dati richiesti sarà effettuato un ultimo controllo: se il dato da scrivere è un valore Null ed il tipo richiesto è la stringa REG_SZ il valore non sarà scritto affatto, anzi sarà cancellato del tutto il valore (riga 182) tramite funzione API RegDeleteValue. Nel caso contrario sarà effettuata la normale scrittura del buffer tramite la funzione RegSetValueEx (riga 184).

Segue parte 3 >>

Fibia FBI
1 Aprile 2002
Corretto il 20 Settembre 2002
Rivisitato il 24 Maggio 2003

Scarica il progetto
Scarica il progetto
Scarica il testo dell'articolo
Scarica il testo dell'articolo
Stampa l'articolo
Stampa l'articolo
Torna all'indice degli HowTo