Scrivere dati nel Registro di Sistema (Regedit) e recuperarli quando servono. - pagina vista: volte

PRIMA PARTE:

Requisiti: buona conoscenza procedure VBA - Conoscenza accesso e struttura del Registro di Sistema (Registro di configurazione).

Prima che qualcuno faccia dei danni al proprio o altrui registro di sistema, è opportuno che chi si accinge a leggere questo articolo, possieda i requisiti richiesti.


Antefatto: esistono delle istruzioni (VB e VBA) che consentono di scrivere, modificare, recuperare o cancellare dati nel registro di sistema, alias

Registro di configurazione - (In Microsoft Windows versione 3.1, i dati di registrazione OLE e le associazioni dei file vengono memorizzati nel database di registrazione e le impostazioni di programma vengono memorizzate nei file di inizializzazione (.ini) di sistema di Windows. In Microsoft Windows 95 (e successivi n.d.w.), il registro Windows funge da database di configurazione centralizzato per informazioni relative all'utente, alle applicazioni e specifiche del computer, incluse le informazioni precedentemente contenute sia nel database di registrazione che nei file ini di Windows 3.1.)
 

La prima istruzione che ci interessa è l'Istruzione SaveSetting che consente di salvare o creare una voce per un'applicazione nel registro di configurazione di Windows; questa istruzione richiede che vengano precisati quattro argomenti tutti obbligatori, e cioè

  • appname - Obbligatoria. Espressione stringa contenente il nome dell'applicazione o del progetto a cui si riferisce l'impostazione.

  • section - Obbligatoria. Espressione stringa contenente il nome della sezione nella quale viene salvata l'impostazione di chiave.

  • key - Obbligatoria. Espressione stringa contenente il nome dell'impostazione di chiave salvata.

  • setting - Obbligatoria. Espressione contenente il valore sul quale viene impostato l'argomento key.

Quindi, tanto per fare un esempio, se con appname  decidiamo di usare la voce "Excelmio", con Section impostiamo la parola "wbookopen", con key impostiamo la parola "passw", e come setting usiamo il valore "elios", i quattro argomenti creeranno queste 4 voci/valori nel registro:

e l'istruzione vba che ha prodotto il risultato visto sopra, è questa:

  • Sub Registra()
    SaveSetting "Excelmio", "wbookopen", "passw", "elios"
    End Sub

come si nota è una istruzione semplice, veloce ed efficace, ed essendo tutti e quattro gli argomenti rappresentati da testo (sono stringhe), devono essere passati tra doppi apici; se al posto di "elios" come "valore" avessimo passato un numero, avremmo usato il numero così come scritto, senza doppi apici. Gli inserimenti registrati tramite il vba di Excel li troveremo nel seguente percorso del Registro di configurazione:

HKEY_CURRENT_USER\Software\VB and VBA Program Settings\appname da voi scelto 

importante: gli argomenti devono necessariamente essere separati da una virgola, e se rilanciamo l'istruzione, le chiavi create non vengono riscritte ma sovrascritte alle stesse esistenti; ciò consente ad esempio di modificare un valore ("setting"), oppure di aggiungere nuove "key" con nuovi "setting", riscrivendoli nella stessa sezione (section) dello stesso nome programma (appname), dove pure sarà possibile aggiungere altre sezioni con relative chiavi e relativi valori.

Bene, abbiamo visto come poter scrivere dei dati nel registro di configurazione, e mi sembrano evidenti le implicazioni di tale attività; la nostra utilità sarà quella di poter memorizzare valori e istruzioni in qualcosa che rimane "acceso" anche dopo che avremo "spento" Excel, cioè dopo che avremo chiuso il nostro programma xls.

Potremo quindi evitare di "memorizzare" dati e valori in celle di nessun foglio, nascondendo meglio ad esempio una password di accesso (eventuali programmi di recupero password infatti le cercano nel file xls protetto), oppure ottenere dati che potranno essere richiamati alla riapertura della cartella con la quale abbiamo generato le nostre chiavi e che potremo "richiamare" quando vorremo. In pratica usiamo il Registro di Configurazione come una banca dati (occhio a non esagerare, rischiando di "intasare" il Registro con migliaia di voci che servono solo al nostro programma ....)

 

La seconda istruzione.

L'istruzione che serve a recuperare i "valori" precedentemente scritti, è la funzione GetSetting , (esiste anche la funzione GetAllSettings di cui non parliamo in questo articolo) che restituisce un valore (il "setting" visto sopra) di impostazione di chiave da una voce del registro di configurazione di Windows. In pratica questa funzione restituirebbe, dall'esempio fatto sopra, solo il valore "elios" e non altro.

La  funzione GetSetting richiede quattro argomenti predefiniti di cui solo i primi tre sono obbligatori:

  • appname - Obbligatoria. Espressione stringa contenente il nome dell'applicazione o del progetto per il quale viene richiesta l'impostazione di chiave.

  • section - Obbligatoria. Espressione stringa contenente il nome della sezione nella quale viene individuata l'impostazione di chiave.

  • key - Obbligatoria. Espressione stringa contenente il nome dell'impostazione di chiave da restituire.

  • default - Facoltativa. Espressione contenente il valore da restituire nel caso in cui nell'impostazione di chiave non sia impostato alcun valore. Se default è stato omesso (nel regedit), viene considerato come una stringa di lunghezza zero ("").

Se uno qualsiasi degli argomenti della funzione GetSetting non esiste, la funzione restituisce il valore di default.

L'istruzione quindi per il recupero (o il confronto) con GetSetting sarebbe, per l'esempio fatto sopra:

  • GetSetting("Excelmio", "wbookopen", "passw", "elios")  -  come si nota gli argomenti sono passati tra doppi apici, separati da virgola e compresi tra parentesi tonde)

e a conferma che se eliminiamo l'argomento default che è facoltativo, anche la sottostante istruzione restituirebbe il valore registrato che è "elios" :

  • GetSetting("Excelmio", "wbookopen", "passw")  - è stato soppresso il quarto argomento

Dal momento che la restituzione tramite GetSetting ci restituisce solo il "valore" assegnato ad una chiave (key), dovremo usare una variabile come contenitore di detto valore, e se detto valore dovrà essere numerico, potremo usare una "funzione di conversione del tipo" (vedi in questa sezione sul sito, "Variabili e Tipi di dati").

Proviamo a fare un esempio con una istruzione che valuti, all'apertura di una cartella, quindi con una routine richiamata nell'evento WorkBook_Open (o direttamente ivi scritta), la password precedentemente inserita nel Registro di configurazione tramite l'istruzione SaveSetting; potremo quindi far chiudere la cartella se la password è errata, oppure dare accesso alla cartella. Nella routine sotto faccio solo apparire due messaggi come esempio del funzionamento. Usiamo una InputBox come richiesta per l'inserimento della password per accedere

  • Sub ApriConPassword()
    Dim Dimmi, Valore
    Dimmi = InputBox("Scrivi la password")
    Valore = GetSetting("Excelmio", "wbookopen", "passw") 
    'con la variabile "Valore" memorizziamo la restituzione della funzione GetSetting che 'nell'esempio fatto fino ad ora corrisponedrà al valore "elios"

    If Dimmi <> Valore Then   
     'quindi confrontiamo ciò che avremo scritto nella inputbox, con la variabile"Valore" ed eseguiremo le condizioni
    MsgBox "Password errata"
    Else
    MsgBox "Password esatta"
    End If
    End Sub

Qualcuno si chiederà: ma come faccio a far trovare le chiavi scritte nel Registro di configurazione per poterle controllare, se all'apertura della cartella pongo subito l'istruzione di controllo? Semplice, usiamo lo stesso evento, quello del WorkBook_Open per lanciare per prima l'istruzione SaveSetting (che se anche ripetuta sovrascrive i dati senza far danni), e subito dopo la routine per il controllo, esempio con il richiamo delle due macro, poste nel WorkBook_Open:

  • Private Sub Workbook_Open()
    Registra
    ApriConPassword
    End Sub

Come si nota il confronto avviene tra ciò che scriviamo in una inputbox ed un valore NON contenuto nella cartella, quindi dovremmo essere tranquilli che sarà difficile scoprire che la password è "elios"; invece è tutta un'illusione: infatti chiunque, disattivando l'esecuzione delle macro che Excel chiede (si/no) può andare a leggersi le istruzioni contenute nelle macro della cartella( compreso la macro Registra()), e neppure se avremo protetto con altra password l'accesso al VBAProject saremo tranquilli; anche quest'ultima password è facilmente valicabile. L'esempio con il controllo password è fatto SOLAMENTE come esempio per spiegare il funzionamento delle istruzioni. Oppure di aprire il Registro al percorso indicato (HTKEY_ ecc) e cercare qualcosa che assomigli ad un salvataggio "casereccio".

Una parziale soluzione di mascheramento potrebbe essere quella di usare un appname tipo "PresenterSoftPowerVideoMaccher" con una section tipo "VisData69" una Key "NotAcceleration" ed un setting tipo "Euroview" (la vera password), insomma, tutti nomi difficilmente riconducibili ad una cartella xls o ad una password.

 

Registrare (e ricaricare) dati da componenti ActiveX di UserForm (TextBox, ListBox, etc) in due parti.

Prima Parte.

Ora facciamo un esercizio più consono alle possibilità che queste istruzioni possono offrire: salvare dei dati contenuti in una ListBox posta su una userform, in modo che alla riapertura della cartella, la stessa ListBox "ricarichi" la stessa lista dati. Condizione necessaria è che in una fase iniziale, prima del primo salvataggio, si sia caricata la ListBox con dati sfruttando uno dei metodi tradizionali (*Additem oppure assegnando un'intervallo dati al RowSource della ListBox).

*Ricordo che col metodo AddItem è possibile inserire dati in una ListBox in fase di esecuzione, sfruttando ad esempio una InputBox.
Scrivi = InputBox("AGGIUNGI DATI")
ListBox1.AddItem Scrivi

Si potrà obiettare: "ma non facciamo prima a salvare detta lista in un foglio di lavoro?", certo, ma supponendo che NON si voglia lasciare una lista dei dati salvata su fogli che altri utenti vedono, e però averla a disposizione in quanto "salvata" in una posizione esterna al file? Qualunque sia il motivo, esamineremo un esempio in tal senso.

La prima considerazione da fare è che dovremo reperire tante chiavi (Key) quanti saranno i valori nella lista della ListBox; potremmo creare una matrice contenenti le chiavi, ma possiamo seguire una strada che ci farà risparmiare un bel pò di istruzioni: i dati in una ListBox sono riconoscibili dal numero di riga o di lista (List) oltre che dal valore (sia esso un nome, un numero o altro) stesso (e dal numero di colonna, che con una sola colonna sarà la colonna numero 0); dovremo quindi pensare ad associare tali valori al loro numero di lista, in modo da adoperare il numero di lista come chiave (Key)  ed il valore come settaggio (setting) di quella chiave. Chiaro??

Per eseguire questa associazione useremo un ciclo For ..Next che "girerà" tante volte quanti sono i valori (e quindi le righe) della lista, e per ogni ciclo scriverà nel Registro di configurazione (d'ora in poi chiamato Registro) usando l'istruzione SaveSetting.

La seconda considerazione da fare e che, per "recuperare",  non è possibile "scorrere" oppure "contare" esattamente quanti sono i valori registrati nel Registro come chiavi del nostro programma, per poter determinare il ciclo di "ritorno dati", cioè il "recupero" dei valori per ricaricare la ListBox: per cui, nel momento in cui usiamo il SaveSetting, inseriremo una chiave "quanti" che porterà come valore di setting il numero dei valori salvati.

Con GetSetting leggeremo quindi per primo il valore numerico della chiave "quanti" e lo useremo come limite superiore del ciclo di "recupero",  meno 1; si, meno 1 perchè nel salvataggio noi siamo partiti a salvare dalla chiave 0 (zero) che è il numero della prima riga o lista di una ListBox.

Vediamo due immagini che ci mostrano una ListBox con 6 nomi, e il Registro con le sei chiavi (key) salvate (i numeri di riga) e relativi valori (i nomi):

Per automatizzare il richiamo dei dati nella ListBox, potremmo lanciare le due istruzioni (quella per salvare: SaveSetting e quella per ricaricare la listBox : GetSetting) sfruttando due eventi dell'UserForm su cui risiede la Listbox: l'evento Activate per caricare la ListBox e l'evento Terminate per salvare i dati presenti nella ListBox, ma si possono verificare le seguenti situazioni non gestibili:

  • apertura della Userform (quindi evento Activate con richiamo dei dati dal Registro) SENZA aver eseguito preventivamente il salvataggio; in questo caso si genera un errore in quanto non vengono trovati nè l' appname nè la sezione nè tanto meno chiavi e valori , e anche gestendo l'errore, non risolviamo il fatto che la ListBox in origine è vuota e non contiene dati da poter salvare.

  • avremo bisogno di caricare una prima volta la ListBox con i dati provenienti dal foglio (RowSource) oppure inserendoli con AddItem, e che soltanto dopo il "carico" potranno essere salvati nel Registro, e per far ciò  useremo un CommandButton.

Suggerisco quindi di creare due routine distinte Sub SalvaLista() e Sub RiCaricaLista() da lanciare con due CommandButton preposti appunto al salvataggio e al richiamo dei dati nel e dal Registro.

Se poi il lettore è smaliziato troverà le giuste azioni da compiere per salvare e caricare, sfruttando eventi dell'userform  e/o CommandButton, tenendo altresì presente che se l'applicativo gira sul proprio computer sarà possibile sfruttare un commandbutton una sola prima volta, per registrare dati della listbox, e modificare poi le istruzioni automatizzando con gli eventi Activate e Terminate il lancio delle istruzioni (infatti avremo già i dati memorizzati nel Registro)

Queste le istruzioni:

  • Sub SalvaLista()
    Dim N

    N = ListBox1.ListCount 
      'con la variabile N contiamo quante sono gli elementi (quindi quante righe) presenti nella ListBox
    If N > 0 Then               
    ' se N sarà maggiore di zero, salviamo (riga sotto) la chiave "quanti" col valore "N" nel programma "Prova Salvataggio" (appname) 'nella sezione "Lista"

    SaveSetting "Prova Salvataggio", "Lista", "quanti", N

    For Q = 0 To N - 1
     ' quindi iniziamo un ciclo che registrerà, stesso programma, stessa sezione, la chiave (key) rappresentata dal numero del contatore "Q" '(che rappresenta il numero di riga), con il valore (setting) reperito tramite la proprietà List(numeroriga, numerocolonna) della listbox (N meno 1 perchè il 'numero di riga inizia da zero)

    SaveSetting "Prova Salvataggio", "Lista", Q, ListBox1.List(Q, 0)

    Next
    Else    '
    se invece la variabile N indica che la Lista è vuota, avvisiamo col il seguente messaggio
    MsgBox "CARICARE LA LISTBOX CON NUOVI DATI"
    End If
    End Sub

E' nelle istruzioni di "recupero" che è più necessario istruire una gestione degli errori, in quanto se nel registro non vengono trovati i dati cercati si genera un errore; in questo esempio, se si genera un errore,  io rimando alla routine SalvaLista, che provvede ad avvisare con un messaggio:

  • Sub RiCaricaLista()
    On Error GoTo E       
     'impostiamo la gestione degli errori: se l'istruzione (sotto) GetSetting troverà il Registro vuoto dei dati da recuperare, si genera un 'errore che tramite il richiamo della macro SalvaLista() ci avviserà con un messaggio, altrimenti, se i dati sono presenti, con la variabile "W" otteniamo in 'restituzione il valore rappresentato dalla chiave "quanti", cioè il numero di quante erano in origine le righe occupate.
    W = GetSetting("Prova Salvataggio", "Lista", "quanti", N)
    'ora che sappiamo il valore di "W" possiamo iniziare un ciclo che sfrutterà la variabile "U" come contatore che restituirà, tramite Additem, il valore "default" alla 'riga "U" della ListBox
    For U = 0 To W - 1
    ListBox1.AddItem GetSetting("Prova Salvataggio", "Lista", U, Default)
    Next

    Exit Sub  
     'terminato il ciclo, la ListBox risulta "caricata", ed usciamo dalla sub.
    E:
    SalvaLista

    End Sub

Sarà quindi ora possibile sfruttare il richiamo alle due macro negli eventi Activate (Sub RicaricaLista) e Terminate (Sub SalvaLista).

 

La terza istruzione.

Se desideriamo eliminare dal Registro le voci che abbiamo registrato possiamo utilizzare l' Istruzione DeleteSetting che consente appunto di eliminare un'impostazione di sezione (setting) o di chiave (key) dalla voce del registro di configurazione di Windows (quindi NON è consentita l'eliminazione della voce corrispondente all' appname).

La sintassi dell'istruzione DeleteSetting è composta dai seguenti tre argomenti predefiniti, di cui solo i primi due obbligatori:

  • appname - Obbligatoria. Espressione stringa contenente il nome dell'applicazione o del progetto a cui si riferisce l'impostazione di sezione o di chiave.

  • section - Obbligatoria. Espressione stringa contenente il nome della sezione dalla quale viene eliminata l'impostazione di chiave. Se vengono specificati solo gli argomenti appname e section, la sezione specificata viene eliminata insieme a tutte le impostazioni di chiave correlate.

  • key - Facoltativa. Espressione stringa contenente il nome dell'impostazione di chiave da eliminare.

Quindi, proseguendo con il primo esempio dell'articolo, se volessimo cancellare il settaggio  (o valore) "elios" dalla chiave (key) "passw" dovremmo scrivere:

  • DeleteSetting "Excelmio", "wbookopen", "passw"

Se invece usassimo la seguente istruzione elimineremo anche la sezione specificata oltre che chiavi e relativi settaggi:

  • DeleteSetting "Excelmio", "wbookopen"

Attenzione : Se si utilizza l'istruzione DeleteSetting in una impostazione di sezione o di chiave non esistente, verrà generato un errore di run-time, sarà quindi opportuno inserire anche una gestione degli errori.

 


Esercizio: salvare un elenco (lista) da una ListBox multicolonna.

Sempre per restare in tema di ListBox vediamo come cambiano le istruzioni se vogliamo salvare e poi recuperare i dati di una ListBox multicolonna; intanto sappiamo che per avere più di una sola colonna (di default, con indice 0) è necessario impostare la proprietà ColumnCount della ListBox al valore di colonne desiderato; farò un esempio con 4 colonne.

Ci affideremo a due routine separate che potremo, in seguito al primo salvataggio, automatizzare con eventi della UserForm. Farei comunque una precisazione per aiutare a capire il "meccanismo" impiegato: ci troviamo, con una ListBox multicolonna, a dover gestire una situazione a due dimensioni , righe e colonne, quindi due serie di indici (indice riga e indice colonna) mentre nel Registro, questi indici che saranno le "chiavi", sono obbligatoriamente monodimensionali: solo una unica colonna di dati.

Dovendo utilizzare sia per salvare, sia per ricaricare, doppi cicli For Next che scorrano le righe e le colonne, con relativi contatori di ciclo, diventa impossibile gestire il salvataggio ed il recupero che richiedono un solo contatore, altro problema è rappresentato dalla proprietà List(numeroriga, numerocolonna) della ListBox che appunto richiede due valori.

Si risolve (senza matrici) con un terzo contatore incrementale, che ad ogni ciclo letto (riga e colonna), si incrementi di 1 unità, e adoperiamo quindi questo contatore come chiave (key) di registrazione e/o di lettura.

Le istruzioni che leggeremo sotto, si riferiscono a questa situazione: una ListBox con 5 righe e 4 colonne, quindi 20 chiavi (key) da memorizzare, contando anche lo zero si memorizza fino al 19:


Come appname ho scelto la stringa "Prova LBoxMulti", come section la parola "Lista", le chiavi (key) che vediamo partono da 0 e finiscono al valore 19 (ricordo che le chiavi sono sempre espressioni stringa, quindi testo, e come tali se numeri, incolonnate), e sono state inserite le chiavi "colonne" e "righe" che portano come valori (setting) il totale delle righe e delle colonne lette nella listbox.

  • Sub SalvaMultiLista()
    Dim N, C, Cont
    Cont = 0                   
     'usiamo la variabile "Cont" inizializzata a zero, variabile che usiamo per definire le "chiavi" (le key) nell'istruzione SaveSetting dei cicli
    N = ListBox1.ListCount            
      'con la variabile N otteniamo il numero di elementi (righe) presenti nella ListBox
    C = ListBox1.ColumnCount        
    'con la variabile C otteniamo il numero di colonne presenti nella ListBox
    If N > 0 Then                           
    'controlliamo che esista almeno una riga
    SaveSetting "Prova LBoxMulti", "Lista", "righe", N                     
    'e quindi salviamo la chiave "righe" col suo valore N
    SaveSetting "Prova LBoxMulti", "Lista", "colonne", C               
     'e salviamo pure la chiave "colonne" col suo valore C

    For Z = 0 To C - 1    
    'poi iniziamo due cicli: il primo esterno che serve a scorrere le colonne, e per ogni colonna scorsa, lanciamo un secondo ciclo
    For Q = 0 To N - 1   
    'interno che scorre le righe: i limiti sup. di entrambi i cicli sono rappresentati dalle due variabili C e N (meno 1) . Si imposta quindi, sotto, 'l'istruzione di salvataggio che registra l'appname, la section, la chiave Cont che ora è uguale a zero, e come valore il valore letto dall'istruzione List (numeroriga 'Q, numerocolonna Z)
    SaveSetting "Prova LBoxMulti", "Lista", Cont, ListBox1.List(Q, Z)
    Cont = Cont + 1     
    ' e quindi si incrementa di 1 il valore Cont ; è questo valore che formerà in progressione la "key" di registrazione

    Next
    Next

    Else  
    'se invece la variabile N non è maggiore di zero, quindi la lista non contiene elementi nella prima colonna, si avvisa con il messaggio

    MsgBox "CARICARE LA LISTBOX CON NUOVI DATI"

    End If
    End Sub

Per ricaricare la ListBox, useremo anche in questo caso un "contatore" che ci consentirà di "leggere" la giusta chiave e reperirne il settaggio (o valore di chiave che dir si voglia). Poichè dovremo anche in questo caso sfruttare la proprietà List per aggiungere i dati letti, e detta proprietà non può lavorare se non si aprono prima le righe necessarie, sfruttiamo un ciclo che usando Additem a vuoto, aprirà tante righe quante lette nella chiave "righe" del Registro.

  • Sub CaricaMultiLista()
    Dim W, T, Cont, U, L, Riga
    On Error GoTo E            
    'impostiamo la gestione degli errori
    W = GetSetting("Prova LBoxMulti", "Lista", "righe", N)
     'con "W" otteniamo in valore di N dal Registro, e ciòè il numero di righe memorizzate
    For Riga = 0 To W              
    'quindi si inizia un ciclo che aggiungerà W righe vuote nella ListBox
    ListBox1.AddItem             
     'tramite l'istruzione AddItem
    Next
    T = GetSetting("Prova LBoxMulti", "Lista", "colonne", C)  
    'indi con "T" otteniamo il numero dal Registro di quante colonne sono memorizzate
    Cont = 0                                           
    'quindi impostiamo a zero la variabile Cont che sarà il vettore per leggere le key dal Registro
    For L = 0 To T - 1
    'ciclo per scorrere le colonne della listbox    
    For U = 0 To W - 1
    'ciclo per scorrere le righe della Listbox - Le due variabili U e L saranno l'indice riga e l'indice colonna nell'istruzione List

    ListBox1.List(U, L) = GetSetting("Prova LBoxMulti", "Lista", Cont, Default)   
    'quindi si "carica" la ListBox tramite l'istruzione GetSetting

    Cont = Cont + 1        
    'e  si incrementa di 1 il valore Cont ; è questo valore che formerà in progressione la "key" di lettura
    Next
    Next
    Exit Sub
    E:                     
       'se si genera un errore per la mancata registrazione nel Registro, si chiama l'altra sub
    SalvaMultiLista
    End Sub

E' ovvio che le istruzioni suggerite per le ListBox poste su UserForm sono valide anche per le ComboBox.

Per ora è tutto, a breve presenterò un successivo articolo per spiegare come salvare nel registro dati e/o impostazioni contenute in TextBox, OptionButton, CommandButton, etc.

 

prelevato sul sito www.ennius.altervista.org