Come riconoscere in modo univoco una postazione PC?

di il
29 risposte

Come riconoscere in modo univoco una postazione PC?

Un saluto a chi mi legge.

Vorrei provare a programmare un "lanciatore" per ambiente Windows da usare su più PC a partire da drive esterno (stick usb). L'idea è creare un programma che riconosca il PC sul quale è stato montato il drive esterno e lanci una serie di altri programmi, eventualmente aprendo con questi dei documenti selezionati. Tutti i programmi e i documenti da aprire si troverebbero sullo stick usb stesso, che finirebbe per essere relativamente autonomo rispetto al PC "ospitante".

Il lanciatore l'ho già realizzato, e funziona molto bene usando dei file di configurazione preimpostati con gli elenchi dei percorsi relativi dei file da aprire e alcune etichette "strategiche". Il problema non è quello.

Quello che mi piacerebbe provare a ottenere, è la possibilità che il lanciatore "capisca" in automatico su quale PC si trova, per poter "decidere" in autonomia quale set di file lanciare. Più specificamente, l'obiettivo è fornire a un insegnante la possibilità di preimpostare lezioni specificamente per determinate aule dove si trovano determinati PC, in modo univoco, e aprire con un unico comando tutti i programmi e tutti i file necessari per la lezione, senza doverli cercare uno ad uno.

La domanda è quindi: su cosa mi posso basare per riconoscere in modo deterministico un certo PC (e quindi una certa aula scolastica) senza impazzire con meccanismi troppo complicati? Al momento l'idea che m'è venuta è quella di individuare un qualche codice legato all'hardware, e per ora sto provando a usare il numero di serie del volume C: con GetVolumeInformation(), ma non so se il valore ricavato (un comune DWORD) si può considerare unico e ragionevolmente invariabile oppure no (dovrei fare una serie di test in condizioni poco agevoli). Sapreste darmi un suggerimento?

29 Risposte

  • Re: Come riconoscere in modo univoco una postazione PC?

    Il MAC Address della scheda Ethernet potrebbe essere un buon punto di partenza.
  • Re: Come riconoscere in modo univoco una postazione PC?

    Come lo ricavo? E' ragionevolmente univoco?
  • Re: Come riconoscere in modo univoco una postazione PC?

    Il mac è per definizione univoco. Anche se può essere alterato ma non mi sembra il tuo caso.

    Per ottenerlo devi interrogare le proprietà della scheda di rete. Ovviamente si fa in tanti modi.
  • Re: Come riconoscere in modo univoco una postazione PC?

    La "strada" della lettura dell'ID del volume C: del disco fisso non è sufficientemente attendibile, per gli scopi terra-terra che ho cercato di illustrare? Non si tratta di questioni legate a protezione dati o simili, semplicemente si tratta della necessità (desiderio?) di poter identificare automaticamente il PC di una certa aula distinguendolo da quelli delle altre aule*.

    La domanda, sintetizzandola, potrebbe essere riformulata così:
    l'ID di un volume è ragionevolmente sempre lo stesso?

    Se non ho letto male la documentazione, dovrebbe cambiare solo in caso di riformattazione del volume (eventualità molto rara e ininfluente nel caso dell'utilizzo che ho in mente: basterebbe aggiornare l'elenco degli ID delle aule su un file dedicato; trattandosi di una trentina di aule in tutto non sarebbe un gran dramma). In caso fosse così, una semplice chiamata a GetVolumeInformation() di Win32 indicando il volume C: sarebbe tutto quel che mi serve. In caso contrario, dovrei trovare un'altra strada e tornerei a tediarvi per scoprire come.

    *L'uso del programma, nelle intenzioni, sarebbe di questo tipo...
    1) in ambiente tranquillo, l'insegnante predispone sullo stick usb dove c'è il lanciatore dei file con gli elenchi dei collegamenti ai programmi e ai file che intende usare nelle prossime lezioni, un file per classe
    2) successivamente, l'insegnante accede all'aula dove deve tenere la lezione, inserisce lo stick usb nel PC e inizia a interagire con la classe
    3) mentre introduce l'argomento, il lanciatore sullo stick usb si avvia con un semplice file autorun.inf, riconosce l'aula in base all'id del PC (quello che sto cercando di capire come identificare) e inizia in automatico a lanciare i programmi e ad aprire file necessari per la lezione in quella classe, prelevandoli dal file di impostazione predisposto in precedenza dall'insegnante
    4) terminata l'introduzione dell'argomento alla classe, la postazione PC/LIM è immediatamente pronta per presentare tutto quel che occorre per la lezione, che può iniziare subito senza che sia necessario interrompere il flusso di lavoro per cercare manualmente programmi e file vari sullo stick usb (quei pochi minuti sono spesso causa di distrazione e trambusto, con conseguente generazione di "tensioni" per placarli).
  • Re: Come riconoscere in modo univoco una postazione PC?

    Secondo me per l'univocità è più che sufficiente il volume serial number assegnato da windows in fase di formattazione.
    Io, in questi casi, personalmente uso il "manufacturer's serial number", che non cambia in caso di formattazione.
    wmic diskdrive get serialnumber
    P.
  • Re: Come riconoscere in modo univoco una postazione PC?

    AldoBaldo ha scritto:


    l'ID di un volume è ragionevolmente sempre lo stesso?
    Cosa vuol dire "ragionevolmente"?

    Quasi sicuramente non ne troverai due uguali nei computer che stai trattando.

    Ma è facilmente modificabile con un semplice tool anche senza formattazione.

    Ottenere il MAC della scheda non è poi così impossibile e garantisce univocità assoluta (anche se è un dato modificabile anche questo ...).
    Ad esempio, grazie a System.Net.NetworkInformation.NetworkInterface
    
    Imports System.Net.NetworkInformation
    
     Function getMacAddress()
            Dim nics() As NetworkInterface = _
                  NetworkInterface.GetAllNetworkInterfaces
            Return nics(0).GetPhysicalAddress.ToString
    End Function 
    
    Potresti anche utilizzare il "serial number" hardware di un disco o della scheda madre, informazioni ancora più affidabili, ma tieni presente che, in certi (remoti) casi, alcuni device NON restituiscono tale informazione.
  • Re: Come riconoscere in modo univoco una postazione PC?

    "Quasi sicuramente" è perfino oltre il limite del "ragionevolmente" che proponevo, quindi va benissimo! Anche perché non c'è in ballo alcun rischio che vada oltre il vedersi presentare un messaggio di errore del tipo "PC non riconosciuto" da parte del mio programma, con la richiesta di individuare "a mano" il file dei collegamenti.

    Al numero di serie avevo pensato anch'io, e ho fatto un programma di prova con una chiamata a wmic usando system(). Funzionava come si deve, però aveva un difetto "estetico" che non mi andava giù -- ad ogni lancio, fosse anche solo con un rapido "flash", veniva presentata la finestra della console, che invece vorrei evitare (il mio programma, in assenza di errori, farebbe il suo sporco lavoro in modo del tutto invisibile, senza aprire alcuna finestra, quindi si chiuderebbe automaticamente come se non fosse neppure stato lanciato).

    Dunque, completerò il mio progettino usando GetVolumeInformation(), poi passerò a una fase di test (che mi richiederà qualche tempo, stante che non ho in casa una quantità di PC sufficiente per una casistica significativa) per confermarne l'efficienza.

    Ammesso che vi interessi, tornerò per dire come sono andate le cose, anche se probabilmente sarà tra un paio di settimane.

    Grazie per l'attenzione che mi avete dedicato.
  • Re: Come riconoscere in modo univoco una postazione PC?

    Per ora ho messo insieme quel che serve per identificare il PC e rimandare a un file di testo dal quale ricavare i dati per gli ulteriori lanci e aperture:

    file identificazione_pc.h
    #ifndef IDENTIFICAZIONE_PC_H_INCLUDED
    #define IDENTIFICAZIONE_PC_H_INCLUDED
    
    #include <windows.h>
    
    // nome del file dal quale ricavare i dati sull'autoapertura abbinata al PC
    extern const char *kStrFileAbbinamentoDati;
    
    // nome della cartella che contiene i file dei dati per l'autoapertura
    extern const char *kStrCartellaAutoaperturaFile;
    
    // estensione usata per i file
    extern const char *kStrEstFileDati;
    
    /*==============================================================================
    Questa funzione esegue queste operazioni:
    
    1) tenta di rilevare il numero di serie del volume C:\ del PC dal quale viene
       eseguito il programma
    
    2) se il numero di serie e' stato rilevato, cerca nella cartella identificata
       dalla stringa kStrCartellaAutoaperturaFile un file di testo di nome
       kStrFileAbbinamentoDati che si presume contenga una o piu' righe, ciascuna
       delle quali costituita da un codice numerico a otto cifre esadecimali seguito
       da uno spazio e da una stringa che identifichi il nome di un ulteriore file
       con estensione (non espressa nella stringa) kStrEstFileDati, dal quale il
       programma potra' ricavare i riferimenti a ulteriori programmi e documenti da
       aprire in automatico
       un esempio del contenuto del file kStrFileAbbinamentoDati puo' essere:
    
           D230A6CF Lezione classe 2B
           315D51AA Apri questo se sei in 3F
           B55AF624 Lezione classe 1C
    
       se chiamata con kStrCartellaAutoaperturaFile uguale a "Autoapertura (file)"
       da un programma lanciato sul computer con numero di serie del volume C:\
       D230A6CF, la funzione restituisce in nfd questo percorso (relativo) e nome di
       file:
    
           "Autoapertura (file)\\Lezione classe 2B.txt"
    
       se il numero di serie e' 315D51AA, sullo stesso computer restituisce in nfd:
    
           "Autoapertura (file)\\Apri questo se sei in 3F.txt"
    
    3) in caso non sia possibile leggere dal file kStrFileAbbinamentoDati o qualora
       in quel file non sia riconoscibile un id corrispondente a quello del drive C,
       la funzione tenta di usare il nome del file dell'eseguibile dal quale e'
       stato lanciato il programma
    
    4) se riesce a portare a termine uno di questi compiti, la funzione restituisce
       TRUE e in nfd si trova il percorso relativo del file dei dati dal quale
       rilevare i percorsi dei programmi da lanciare e dei file da aprire in
       automatico
       se nessuno dei compiti puo' essere portato a termine, la funzione restituisce
       FALSE e la memoria puntata da nfd non viene modificata
    
    N.B. l'irreperibilita' del file kStrFileAbbinamentoDati potrebbe essere un modo
       per ottenere INTENZIONALMENTE il caricamento di un file di dati con lo stesso
       nome del file dell'eseguibile .exe, al fine di aggirare il meccanismo che
       vincola un certo file dei dati ad un particolare PC
    
    PARAMETRI
       nfd: nome del file dei dati (out); la funzione fa affidamento sul fatto che
            lo spazio di memoria puntato possa contenere almeno _MAX_PATH char
    ==============================================================================*/
    
    BOOL trova_nome_file_dati( char *nfd );
    
    #endif // IDENTIFICAZIONE_PC_H_INCLUDED
    
    file identificazione_pc.c
    #include "identificazione_pc.h"
    
    #include <stdio.h>
    #include <ctype.h>
    
    enum {
        kCodErrNoErr,
        kCodErrNullPtr,
        kCodErrExtraCar,
        kCodErrNoFileDatiPC,
        kCodErrNoVolInfo,
        kCodErrPCIgnoto,
        kCodErrNoHModule,
        kCodeErrNoModuleName
    };
    
    // nome del file dal quale ricavare il nome del file dei dati abbinato al PC
    const char *kStrFileAbbinamentoDati = "abbin_id";
    
    // nome della cartella che contiene i file dei dati per l'autoapertura
    const char *kStrCartellaAutoaperturaFile = "Autoapertura (file)";
    
    // estensione usata per i file nella cartella kStrFileAbbinamentoDati
    const char *kStrEstFileDati = ".txt";
    
    /*==============================================================================
    // Preimpostato, in previsione di un'eventuale modifica al programma per avere
    // la segnalazione "incorporataPIP_ADAPTER_INFO" dell'errore...
    // Al momento e' inutile e quindi disattivata.
    
    static const char *kStrErr[] = {
        "",
        "Puntatore NULL. ",
        "Troppi caratteri nel percorso del file. ",
        "File di abbinamento dati/PC non aperto. ",
        "ID volume C:\\ non rilevato. ",
        "Non riconosco questo PC. ",
        "Handle del programma non rilevato. ",
        "Nome del file del programma non rilevato. "
    };
    
    static int gCodErr = 0; // kCodErrNoErr
    ==============================================================================*/
    
    // PROTOTIPI
    static int nome_file_dati_da_nome_programma( char *nfd );
    static int identifica_id_volume_c_pc( DWORD *idPc );
    static int identifica_file_dei_dati( char *nfd, DWORD idPc );
    
    /*==============================================================================
    nfd: nome del file dei dati (out) la funzione fa affidamento sul fatto che
         lo spazio di memoria puntato possa contenere almeno _MAX_PATH char
    ==============================================================================*/
    
    BOOL trova_nome_file_dati( char *nfd ) {
       int esito = kCodErrNullPtr;
    
       if( NULL!=nfd ) {
          DWORD idPc;
    
          // per prima cosa, vediamo qual e' l'id del drive C
          esito = identifica_id_volume_c_pc( &idPc );
    
          // se abbiamo l'id, cerchiamo di leggere in kStrFileAbbinamentoDati
          if( kCodErrNoErr==esito ) esito = identifica_file_dei_dati( nfd, idPc );
    
          // se non abbiamo l'id o se non siamo riusciti a leggere i dati nel file
          // kStrFileAbbinamentoDati, basiamoci sul nome del file eseguibile .exe
          if( kCodErrNoErr!=esito ) esito = nome_file_dati_da_nome_programma( nfd );
       }
    
     // hai visto mai che, in futuro, uno voglia che l'errore venga segnalato...
     // if( kCodErrNoErr != esito )
     //     MessageBox( NULL, kStrErr[gCodErr], "Errore", MB_ICONHAND );
    
       return kCodErrNoErr == esito;
    }
    
    /*==============================================================================
    Ricava il nome del file dei dati a partire dal nome del file eseguibile .exe del
    programma
    
    nfd: nome del file dei dati (out) la funzione fa affidamento sul fatto che
         lo spazio di memoria puntato possa contenere almeno _MAX_PATH char
    ==============================================================================*/
    
    static int nome_file_dati_da_nome_programma( char *nfd ) {
        const char *nca = kStrCartellaAutoaperturaFile;
        HMODULE hcm = GetModuleHandle( NULL );
        char mfn[_MAX_PATH], nf[_MAX_FNAME];
        DWORD lMfn, lDir, lNome, lEst;
    
        if( NULL==hcm ) return kCodErrNoHModule;
    
        lMfn = GetModuleFileName( hcm, mfn, _MAX_PATH );
        if( 0==lMfn ) return kCodeErrNoModuleName;
    
        _splitpath( mfn, NULL, NULL, nf, NULL );
    
        lDir  = lstrlen( nca );
        lNome = lstrlen( nf );
        lEst  = lstrlen( kStrEstFileDati );
    
        if( lDir+1+lNome+lEst+1 > _MAX_PATH )
            return kCodErrExtraCar;
    
        _makepath( nfd, NULL, nca, nf, kStrEstFileDati );
    
        return kCodErrNoErr;
    }
    
    /*==============================================================================
    idPc: codice identificativo del volume C nel PC (out)
    ==============================================================================*/
    
    static int identifica_id_volume_c_pc( DWORD *idPc ) {
        int esito = kCodErrNullPtr;
    
        if( NULL != idPc ) {
            BOOL ok = 0 != GetVolumeInformation(
                "C:\\", // address of root directory of the file system
                NULL,   // address of name of the volume
                0,      // length of lpVolumeNameBuffer
                idPc,   // address of volume serial number
                NULL,   // address of system's maximum filename length
                NULL,   // address of file system flags
                NULL,   // address of name of file system
                0       // length of lpFileSystemNameBuffer
            );
    
            esito = ok ? kCodErrNoErr : kCodErrNoVolInfo;
        }
    
        return esito;
    }
    
    /*==============================================================================
    Tenta di leggere il file kStrFileAbbinamentoDati per trovare il nome del file
    dei dati abbinato al PC il cui drive C ha l'id idPc
    
    idPc: codice identificativo del volume C nel PC (in)
    nfd:  nome del file dei dati (out) la funzione fa affidamento sul fatto che
          lo spazio di memoria puntato possa contenere almeno _MAX_PATH char
    ==============================================================================*/
    
    static int identifica_file_dei_dati( char *nfd, DWORD idPc ) {
        const char *nca = kStrCartellaAutoaperturaFile;
        const char *nfa = kStrFileAbbinamentoDati;
        DWORD lNca, lNfa, lEst;
        char nfId[_MAX_PATH]; // nome del file con l'elenco degli identificatori
        FILE *fId = NULL; // lo stream del file con l'elenco degli identificatori
        int esito = kCodErrNoFileDatiPC;
    
        // non serve controllare la validita' dei puntatori passati,
        // e' gia' stato fatto in trova_nome_file_dati_abbinato_a_pc()
    
        lNca = lstrlen( nca );
        lNfa = lstrlen( nfa );
        lEst = lstrlen( kStrEstFileDati );
    
        if( lNca+1+lNfa+lEst+1 > _MAX_PATH )
            return kCodErrExtraCar;
    
        _makepath( nfId, NULL, nca, nfa, kStrEstFileDati );
    
        fId = fopen( nfId, "r" ); // apre il file con l'elenco degli identificatori
    
        if( NULL != fId ) { // file aperto
           while( kCodErrNoErr != esito ) {
              char idStr[16]; // l'identificativo del PC, come letto dal file
              char nfStr[_MAX_PATH]; // la stringa abbinata all'identificativo
              int i, j, c; // contatori e variabili temporanee
    
              while( '\n'==(c=fgetc(fId)) ); // elimina le eventuali righe vuote
    
              for( j=0, i=0; i<8&&EOF!=c; ++i, c=fgetc(fId) ) // carica l'ID
                 idStr[j++] = c;
    
              if( 8==i && (' '==c||'\t'==c) ) { // 8 caratteri letti
                  DWORD idTmp;                  // prima dello spazio
                  char *fine;
    
                  idStr[i] = '\0'; // non e' detta che sia gia' terminata
    
                  idTmp = strtoul( idStr, &fine, 16 ); // da stringa a valore
    
                  if( fine != idStr ) { // strtoul ha trovato un valore
                     if( idTmp == idPc ) { // il valore corrisponde all'id del PC
                        while( isspace(c=fgetc(fId)) ); // salta gli spazi di troppo
                        fseek( fId, -1, SEEK_CUR ); // torna indietro di un caratt.
    
                        // fgets() legge la riga
                        if( NULL!=fgets(nfStr,_MAX_PATH-6-lNca-1,fId) ) {
                            size_t l = lstrlen(nfStr); // la lunghezza della riga
    
                            if( '\n'==nfStr[l-1] || feof(fId) ) {
                               if('\n'==nfStr[l-1] ) nfStr[--l] = '\0';
                               if( 0==lstrlen(nfStr) ) continue; // nessun dato
                               _makepath( nfd, NULL, nca, nfStr, kStrEstFileDati );
                               esito = kCodErrNoErr; // trovato!
                            }
                        }
                     }
                     else {
                        // salta quel che avanza sulla riga
                        for( c=fgetc(fId); '\n'!=c&&EOF!=c; c=fgetc(fId) );
                        if( EOF==c ) break; // siamo in fondo al file?
                     }
                  }
               }
            }
    
            fclose( fId );
    
            if( kCodErrNoErr != esito ) esito = kCodErrPCIgnoto;
        }
    
        return esito;
    }
    
    Sembra funzionare come si deve...
    Quel che più conta, mi sto divertendo un sacco.
  • Re: Come riconoscere in modo univoco una postazione PC?

    Sto verificando "sul campo" il meccanismo del riconoscimento delle postazioni PC/LIM tramite GetVolumeInformation() e... be', non funziona. Meglio, funziona benissimo, ma non fornisce l'univocità richiesta: oggi, tra le tante, ho trovato tre postazioni diverse che condividono lo stesso ID, probabilmente perché sono PC della stessa marca, stesso modello, stessa configurazione, stesso lotto d'acquisto. Ipotizzo che la situazione si verifichi perché quei PC sono stati configurati mediante copia fisica bit-per-bit di uno stesso hard disk preconfigurato, usato come modello, probabilmente in fabbrica. Domani verificherò ulteriormente questa ipotesi, perché avrò accesso ad altre due aule nelle quali son certo che ci siano altri due PC "gemelli" dei tre che hanno evidenziato il fenomeno. Va be', vedrò di trovare altre vie. Un metodo semplice potrebbe essere inserire in ogni postazione un semplice "file civetta" in una collocazione fissa, con un codice univoco. Non so, vedrò che fare.
  • Re: Come riconoscere in modo univoco una postazione PC?

    Come ti e stato già indicato il numero del volume non è caratteristico (otre che facilmente modificabile) quindi è preferibile il MAC della scheda (identificativo univoco) che puoi associare ad ulteriori elementi seriali dell'hardware.
  • Re: Come riconoscere in modo univoco una postazione PC?

    Infatti .. e gli ho pure fornito le poche li linee di codice che servono per il mac.

    Chissà perché si chiede sul forum e poi non si seguono i consigli ma si cercano strade astruse e inconsistenti ... boh.
  • Re: Come riconoscere in modo univoco una postazione PC?

    Non è che non voglio seguire i consigli, è che non si applicano alla situazione specifica nella quale mi trovo a voler cercare le soluzioni. Ad esempio, il codice che mi suggerisci usa un linguaggio col quale non ho alcuna dimestichezza, e fa almeno in apparenza riferimento ad ambienti di sviluppo diversi da quello che uso (Code::Blocks, con MinGw/gcc). Inoltre credo tiri in ballo delle classi, mentre io vorrei rimanere nell'ambito del comunissimo C. Ho inoltre necessità/desiderio di fare un programmino di dimensioni risibili e che possa "girare" su sistemi datati, arretrando almeno fino a XP, usando solo ed esclusivamente le API di base di Win32.

    E' possibile "tradurre" il tuo esempio affinché rientri in questa casistica?

    Spulciando la documentazione a mia disposizione, mi sono imbattuto in una funzione chiamata UUIDCreate(), che crea un fantomatico Unique Universal Identifier... potrebbe essere utile per ottenere gli scopi che ho descritto in precedenza?

    Se mi si indirizza verso una documentazione alla mia portata faccio anche da me. Probabilmente ho fatto le ricerche usando chiavi inadatte, perché non sono venuto a capo di niente.
  • Re: Come riconoscere in modo univoco una postazione PC?

    Willy, se potessi modificare il numero del volume sarei a cavallo: mi basterebbe cambiare quelli delle "postazioni" gemelle per renderle uniche. Tenendo presente che sono "freezate" con un sistema di protezione di quelli che resettano il sistema ad ogni riavvio, come potrei fare?
  • Re: Come riconoscere in modo univoco una postazione PC?

    Prima che qualcuno cominci a pensar male: non c'è nessuna intenzione di sabotaggio in quel che ho in mente. Vorrei semplicemente avere un modo per far capire al mio programma in quale aula si trova al momento del lancio, tramite l'identificazione della postazione PC/LIM.
Devi accedere o registrarti per scrivere nel forum
29 risposte